Showing preview only (588K chars total). Download the full file or copy to clipboard to get everything.
Repository: juju/utils
Branch: master
Commit: b92083fa0866
Files: 204
Total size: 543.2 KB
Directory structure:
gitextract_0jyt02x8/
├── .gitignore
├── ISSUE_TEMPLATE.md
├── LICENSE
├── LICENSE.golang
├── Makefile
├── README.md
├── SECURITY.md
├── arch/
│ ├── arch.go
│ ├── arch_test.go
│ └── package_test.go
├── attempt.go
├── attempt_test.go
├── bzr/
│ ├── bzr.go
│ ├── bzr_test.go
│ ├── bzr_unix_test.go
│ └── bzr_windows_test.go
├── cache/
│ ├── cache.go
│ ├── cache_test.go
│ ├── export_test.go
│ └── package_test.go
├── cert/
│ ├── cert.go
│ ├── cert_test.go
│ └── exports_test.go
├── command.go
├── command_test.go
├── context.go
├── context_test.go
├── du/
│ ├── LICENSE.ricochet2200
│ ├── diskusage.go
│ └── diskusage_windows.go
├── errors.go
├── exec/
│ ├── exec.go
│ ├── exec_internal_test.go
│ ├── exec_linux_test.go
│ ├── exec_test.go
│ ├── exec_unix.go
│ ├── exec_windows.go
│ ├── exec_windows_test.go
│ └── package_test.go
├── export_test.go
├── file.go
├── file_test.go
├── file_unix.go
├── file_unix_test.go
├── file_windows.go
├── file_windows_test.go
├── filepath/
│ ├── common.go
│ ├── common_test.go
│ ├── export_test.go
│ ├── filepath.go
│ ├── filepath_test.go
│ ├── interface_test.go
│ ├── package_test.go
│ ├── stdlib.go
│ ├── stdlib_test.go
│ ├── stdlibmatch.go
│ ├── unix.go
│ ├── unix_test.go
│ ├── win.go
│ └── win_test.go
├── filestorage/
│ ├── doc.go
│ ├── export_test.go
│ ├── fakes_test.go
│ ├── interfaces.go
│ ├── metadata.go
│ ├── metadata_store.go
│ ├── metadata_test.go
│ ├── package_test.go
│ ├── wrapper.go
│ └── wrapper_test.go
├── fs/
│ ├── copy.go
│ └── copy_test.go
├── go.mod
├── go.sum
├── gomaxprocs.go
├── gomaxprocs_test.go
├── hash/
│ ├── fingerprint.go
│ ├── fingerprint_test.go
│ ├── hash.go
│ ├── hash_test.go
│ ├── package_test.go
│ ├── writer.go
│ └── writer_test.go
├── home_unix.go
├── home_unix_test.go
├── home_windows.go
├── home_windows_test.go
├── isubuntu.go
├── isubuntu_test.go
├── jsonhttp/
│ ├── jsonhttp.go
│ ├── jsonhttp_test.go
│ └── package_test.go
├── keyvalues/
│ ├── keyvalues.go
│ ├── keyvalues_test.go
│ └── package_test.go
├── limiter.go
├── limiter_test.go
├── multireader.go
├── multireader_test.go
├── naturalsort.go
├── naturalsort_test.go
├── network.go
├── network_test.go
├── os.go
├── os_test.go
├── package_test.go
├── parallel/
│ ├── package_test.go
│ ├── parallel.go
│ ├── parallel_test.go
│ ├── try.go
│ └── try_test.go
├── password.go
├── password_test.go
├── proxy/
│ ├── package_test.go
│ ├── proxy.go
│ └── proxy_test.go
├── randomstring.go
├── randomstring_test.go
├── registry/
│ ├── export_test.go
│ ├── package_test.go
│ ├── registry.go
│ └── registry_test.go
├── relativeurl.go
├── relativeurl_test.go
├── setenv.go
├── setenv_test.go
├── shell/
│ ├── bash.go
│ ├── bash_test.go
│ ├── command.go
│ ├── interface_test.go
│ ├── output.go
│ ├── package_test.go
│ ├── powershell.go
│ ├── powershell_test.go
│ ├── renderer.go
│ ├── renderer_test.go
│ ├── script.go
│ ├── script_test.go
│ ├── unix.go
│ ├── win.go
│ ├── wincmd.go
│ └── wincmd_test.go
├── size.go
├── size_test.go
├── ssh/
│ ├── authorisedkeys.go
│ ├── authorisedkeys_test.go
│ ├── clientkeys.go
│ ├── clientkeys_test.go
│ ├── export_test.go
│ ├── fakes_test.go
│ ├── fingerprint.go
│ ├── fingerprint_test.go
│ ├── generate.go
│ ├── generate_test.go
│ ├── package_test.go
│ ├── run.go
│ ├── run_test.go
│ ├── ssh.go
│ ├── ssh_gocrypto.go
│ ├── ssh_gocrypto_test.go
│ ├── ssh_openssh.go
│ ├── ssh_test.go
│ ├── stream.go
│ ├── stream_test.go
│ ├── stream_wrapper_unix.go
│ ├── stream_wrapper_windows.go
│ └── testing/
│ └── keys.go
├── symlink/
│ ├── export_test.go
│ ├── symlink.go
│ ├── symlink_posix.go
│ ├── symlink_test.go
│ ├── symlink_windows.go
│ ├── symlink_windows_test.go
│ ├── zsymlink_windows_386.go
│ └── zsymlink_windows_amd64.go
├── systemerrmessages_unix.go
├── systemerrmessages_windows.go
├── tailer/
│ ├── export_test.go
│ ├── package_test.go
│ ├── tailer.go
│ └── tailer_test.go
├── tar/
│ ├── tar.go
│ └── tar_test.go
├── timer.go
├── timer_test.go
├── trivial.go
├── trivial_test.go
├── uptime/
│ ├── uptime_nix.go
│ ├── uptime_windows.go
│ ├── zuptime_windows_386.go
│ └── zuptime_windows_amd64.go
├── username.go
├── username_test.go
├── uuid.go
├── uuid_test.go
├── voyeur/
│ ├── package_test.go
│ ├── value.go
│ └── value_test.go
├── yaml.go
├── yaml_test.go
├── zfile_windows.go
└── zip/
├── package_test.go
├── zip.go
└── zip_test.go
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# GoLand
.idea/
# Dependency directories (remove the comment below to include it)
# vendor/
================================================
FILE: ISSUE_TEMPLATE.md
================================================
## Issues tracked in Launchpad
Please file an issue against https://bugs.launchpad.net/juju/+filebug
================================================
FILE: LICENSE
================================================
All files in this repository are licensed as follows. If you contribute
to this repository, it is assumed that you license your contribution
under the same license unless you state otherwise.
All files Copyright (C) 2015 Canonical Ltd. unless otherwise specified in the file.
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.
================================================
FILE: LICENSE.golang
================================================
This licence applies to the following files:
* filepath/stdlib.go
* filepath/stdlibmatch.go
Copyright (c) 2010 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.
================================================
FILE: Makefile
================================================
PROJECT := github.com/juju/utils/v4
.PHONY: check-licence check-go check
check: check-licence check-go
go test -v $(PROJECT)/...
check-licence:
@(grep -rFl "Licensed under the LGPLv3" .;\
grep -rFl "MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT" .;\
grep -rFl "license that can be found in the LICENSE.ricochet2200 file" .; \
find . -name "*.go") | sed -e 's,\./,,' | sort | uniq -u | \
xargs -I {} echo FAIL: licence missed: {}
check-go:
$(eval GOFMT := $(strip $(shell gofmt -l .| sed -e "s/^/ /g")))
@(if [ x$(GOFMT) != x"" ]; then \
echo go fmt is sad: $(GOFMT); \
exit 1; \
fi )
@(go vet -all -composites=false -copylocks=false .)
# Install packages required to develop in utils and run tests.
install-dependencies: install-snap-dependencies install-mongo-dependencies
@echo Installing dependencies
@echo Installing bzr
@sudo apt install bzr --yes
@echo Installing zip
@sudo apt install zip --yes
install-snap-dependencies:
## install-snap-dependencies: Install the supported snap dependencies
@echo Installing go-1.17 snap
@sudo snap install go --channel=1.17/stable --classic
install-mongo-dependencies:
## install-mongo-dependencies: Install Mongo and its dependencies
@echo Adding juju PPA for mongodb
@sudo apt-add-repository --yes ppa:juju/stable
@sudo apt-get update
@echo Installing mongodb
@sudo apt-get --yes install \
$(strip $(DEPENDENCIES)) \
$(shell apt-cache madison mongodb-server-core juju-mongodb3.2 juju-mongodb mongodb-server | head -1 | cut -d '|' -f1)
================================================
FILE: README.md
================================================
juju/utils
============
This package provides general utility packages and functions.
================================================
FILE: SECURITY.md
================================================
# Security policy
## Reporting a vulnerability
Please provide a description of the issue, the steps you took to
create the issue, affected versions, and, if known, mitigations for
the issue.
The preferred way to report a security issue is through
[GitHub's security advisory for this project](https://github.com/juju/utils/security/advisories/new). See
[Privately reporting a security
vulnerability](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability)
for instructions on reporting using GitHub's security advisory feature.
The [Ubuntu Security disclosure and embargo
policy](https://ubuntu.com/security/disclosure-policy) contains more
information about how can contact us, what you can expect when you contact us,
and what we expect from you.
================================================
FILE: arch/arch.go
================================================
// Copyright 2014-2016 Canonical Ltd.
// Licensed under the LGPLv3, 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"
PPC64EL = "ppc64el"
S390X = "s390x"
RISCV64 = "riscv64"
// Older versions of Juju used "ppc64" instead of ppc64el
LEGACY_PPC64 = "ppc64"
)
// AllSupportedArches records the machine architectures recognised by Juju.
var AllSupportedArches = []string{
AMD64,
I386,
ARM,
ARM64,
PPC64EL,
S390X,
RISCV64,
}
// Info records the information regarding each architecture recognised by Juju.
var Info = map[string]ArchInfo{
AMD64: {64},
I386: {32},
ARM: {32},
ARM64: {64},
PPC64EL: {64},
S390X: {64},
RISCV64: {64},
}
// ArchInfo is a struct containing information about a supported architecture.
type ArchInfo struct {
// WordSize is the architecture's word size, in bits.
WordSize int
}
// 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("ppc64|ppc64el|ppc64le"), PPC64EL},
{regexp.MustCompile("s390x"), S390X},
{regexp.MustCompile("riscv64|risc$|risc-[vV]64"), RISCV64},
}
// 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
}
}
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
}
================================================
FILE: arch/arch_test.go
================================================
// Copyright 2014 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package arch_test
import (
jc "github.com/juju/testing/checkers"
gc "gopkg.in/check.v1"
"github.com/juju/utils/v4/arch"
)
type archSuite struct {
}
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"},
{"arm64", "arm64"},
{"ppc64el", "ppc64el"},
{"ppc64le", "ppc64el"},
{"ppc64", "ppc64el"},
{"s390x", "s390x"},
{"riscv64", "riscv64"},
{"risc", "riscv64"},
{"risc-v64", "riscv64"},
{"risc-V64", "riscv64"},
} {
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)
}
func (s *archSuite) TestArchInfo(c *gc.C) {
for _, a := range arch.AllSupportedArches {
_, ok := arch.Info[a]
c.Assert(ok, jc.IsTrue)
}
}
================================================
FILE: arch/package_test.go
================================================
// Copyright 2013 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package arch_test
import (
"testing"
gc "gopkg.in/check.v1"
)
func Test(t *testing.T) {
gc.TestingT(t)
}
================================================
FILE: attempt.go
================================================
// Copyright 2011, 2012, 2013 Canonical Ltd.
// Licensed under the LGPLv3, 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
}
================================================
FILE: attempt_test.go
================================================
// Copyright 2011, 2012, 2013 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package utils_test
import (
"time"
gc "gopkg.in/check.v1"
"github.com/juju/utils/v4"
)
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)
}
================================================
FILE: bzr/bzr.go
================================================
// Copyright 2013 Canonical Ltd.
// Licensed under the LGPLv3, 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 {
// Need to trim \r as well as \n for Windows compatibility
b.location = strings.TrimRight(string(stdout), "\r\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
}
================================================
FILE: bzr/bzr_test.go
================================================
// Copyright 2014 Canonical Ltd.
// Copyright 2014 Cloudbase Solutions SRL
// Licensed under the LGPLv3, see LICENCE file for details.
package bzr_test
import (
"io/ioutil"
"os"
"os/exec"
"path/filepath"
stdtesting "testing"
"github.com/juju/testing"
jc "github.com/juju/testing/checkers"
gc "gopkg.in/check.v1"
"github.com/juju/utils/v4/bzr"
)
func Test(t *stdtesting.T) {
gc.TestingT(t)
}
var _ = gc.Suite(&BzrSuite{})
type BzrSuite struct {
testing.CleanupSuite
b *bzr.Branch
}
const bzr_config = `[DEFAULT]
email = testing <test@example.com>
`
func (s *BzrSuite) SetUpTest(c *gc.C) {
s.CleanupSuite.SetUpTest(c)
bzrdir := c.MkDir()
s.PatchEnvironment("BZR_HOME", bzrdir)
err := os.MkdirAll(filepath.Join(bzrdir, bzrHome), 0755)
c.Assert(err, jc.ErrorIsNil)
err = ioutil.WriteFile(
filepath.Join(bzrdir, bzrHome, "bazaar.conf"),
[]byte(bzr_config), 0644)
c.Assert(err, jc.ErrorIsNil)
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, jc.ErrorIsNil)
b := bzr.New(s.b.Join("dir"))
// When bzr has to search for the root, it will expand any symlinks it
// found along the way.
path, err := filepath.EvalSymlinks(s.b.Location())
c.Assert(err, jc.ErrorIsNil)
c.Assert(b.Location(), jc.SamePath, path)
}
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, jc.ErrorIsNil)
}
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, jc.ErrorIsNil)
f.Close()
err = s.b.Add("myfile")
c.Assert(err, jc.ErrorIsNil)
err = s.b.Commit("my log message")
c.Assert(err, jc.ErrorIsNil)
revid, err := s.b.RevisionId()
c.Assert(err, jc.ErrorIsNil)
cmd := exec.Command("bzr", "log", "--long", "--show-ids", "-v", s.b.Location())
output, err := cmd.CombinedOutput()
c.Assert(err, jc.ErrorIsNil)
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, jc.ErrorIsNil)
f.Close()
err = b1.Add("file")
c.Assert(err, jc.ErrorIsNil)
err = b1.Commit("added file")
c.Assert(err, jc.ErrorIsNil)
// Push file to b2.
err = b1.Push(&bzr.PushAttr{Location: b2.Location()})
c.Assert(err, jc.ErrorIsNil)
// Push location should be set to b2.
location, err := b1.PushLocation()
c.Assert(err, jc.ErrorIsNil)
c.Assert(location, jc.SamePath, b2.Location())
// Now push it to b3.
err = b1.Push(&bzr.PushAttr{Location: b3.Location()})
c.Assert(err, jc.ErrorIsNil)
// Push location is still set to b2.
location, err = b1.PushLocation()
c.Assert(err, jc.ErrorIsNil)
c.Assert(location, jc.SamePath, 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, jc.ErrorIsNil)
// Now the push location has shifted to b3.
location, err = b1.PushLocation()
c.Assert(err, jc.ErrorIsNil)
c.Assert(location, jc.SamePath, b3.Location())
// Both b2 and b3 should have the file.
_, err = os.Stat(b2.Join("file"))
c.Assert(err, jc.ErrorIsNil)
_, err = os.Stat(b3.Join("file"))
c.Assert(err, jc.ErrorIsNil)
}
func (s *BzrSuite) TestCheckClean(c *gc.C) {
err := s.b.CheckClean()
c.Assert(err, jc.ErrorIsNil)
// Create and add b1/file to the branch.
f, err := os.Create(s.b.Join("file"))
c.Assert(err, jc.ErrorIsNil)
f.Close()
err = s.b.CheckClean()
c.Assert(err, gc.ErrorMatches, `branch is not clean \(bzr status\)`)
}
================================================
FILE: bzr/bzr_unix_test.go
================================================
// Copyright 2014 Canonical Ltd.
// Copyright 2014 Cloudbase Solutions SRL
// Licensed under the LGPLv3, see LICENCE file for details.
//go:build !windows
// +build !windows
package bzr_test
const bzrHome = ".bazaar"
================================================
FILE: bzr/bzr_windows_test.go
================================================
// Copyright 2014 Canonical Ltd.
// Copyright 2014 Cloudbase Solutions SRL
// Licensed under the LGPLv3, see LICENCE file for details.
//go:build windows
// +build windows
package bzr_test
const bzrHome = "Bazaar/2.0"
================================================
FILE: cache/cache.go
================================================
// Copyright 2015 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
// Package cache provides a simple caching mechanism
// that limits the age of cache entries and tries to avoid large
// repopulation events by staggering refresh times.
package cache
import (
"math/rand"
"sync"
"time"
"github.com/juju/errors"
)
// entry holds a cache entry. The expire field
// holds the time after which the entry will be
// considered invalid.
type entry struct {
value any
expire time.Time
}
// Key represents a cache key. It must be a comparable type.
type Key any
// Cache holds a time-limited set of values for arbitrary keys.
type Cache struct {
maxAge time.Duration
// mu guards the fields below it.
mu sync.Mutex
// expire holds when the cache is due to expire.
expire time.Time
// We hold two maps so that can avoid scanning through all the
// items in the cache when the cache needs to be refreshed.
// Instead, we move items from old to new when they're accessed
// and throw away the old map at refresh time.
old, new map[Key]entry
inFlight map[Key]*fetchCall
}
// fetch represents an in-progress fetch call. If a cache Get request
// is made for an item that is currently being fetched, this will
// be used to avoid an extra call to the fetch function.
type fetchCall struct {
wg sync.WaitGroup
val any
err error
}
// New returns a new Cache that will cache items for
// at most maxAge. If maxAge is zero, items will
// never be cached.
func New(maxAge time.Duration) *Cache {
// The returned cache will have a zero-valued expire
// time, so will expire immediately, causing the new
// map to be created.
return &Cache{
maxAge: maxAge,
inFlight: make(map[Key]*fetchCall),
}
}
// Len returns the total number of cached entries.
func (c *Cache) Len() int {
c.mu.Lock()
defer c.mu.Unlock()
return len(c.old) + len(c.new)
}
// Evict removes the entry with the given key from the cache if present.
func (c *Cache) Evict(key Key) {
c.mu.Lock()
defer c.mu.Unlock()
delete(c.new, key)
delete(c.old, key)
}
// EvictAll removes all entries from the cache.
func (c *Cache) EvictAll() {
c.mu.Lock()
defer c.mu.Unlock()
c.new = make(map[Key]entry)
c.old = nil
}
// Get returns the value for the given key, using fetch to fetch
// the value if it is not found in the cache.
// If fetch returns an error, the returned error from Get will have
// the same cause.
func (c *Cache) Get(key Key, fetch func() (any, error)) (any, error) {
return c.getAtTime(key, fetch, time.Now())
}
// getAtTime is the internal version of Get, useful for testing; now represents the current
// time.
func (c *Cache) getAtTime(key Key, fetch func() (any, error), now time.Time) (any, error) {
if val, ok := c.cachedValue(key, now); ok {
return val, nil
}
c.mu.Lock()
if f, ok := c.inFlight[key]; ok {
// There's already an in-flight request for the key, so wait
// for that to complete and use its results.
c.mu.Unlock()
f.wg.Wait()
// The value will have been added to the cache by the first fetch,
// so no need to add it here.
if f.err == nil {
return f.val, nil
}
return nil, errors.Trace(f.err)
}
var f fetchCall
f.wg.Add(1)
c.inFlight[key] = &f
// Mark the request as done when we return, and after
// the value has been added to the cache.
defer f.wg.Done()
// Fetch the data without the mutex held
// so that one slow fetch doesn't hold up
// all the other cache accesses.
c.mu.Unlock()
val, err := fetch()
c.mu.Lock()
defer c.mu.Unlock()
// Set the result in the fetchCall so that other calls can see it.
f.val, f.err = val, err
if err == nil && c.maxAge >= 2*time.Nanosecond {
// If maxAge is < 2ns then the expiry code will panic because the
// actual expiry time will be maxAge - a random value in the
// interval [0, maxAge/2). If maxAge is < 2ns then this requires
// a random interval in [0, 0) which causes a panic.
//
// This value is so small that there's no need to cache anyway,
// which makes tests more obviously deterministic when using
// a zero expiry time.
c.new[key] = entry{
value: val,
expire: now.Add(c.maxAge - time.Duration(rand.Int63n(int64(c.maxAge/2)))),
}
}
delete(c.inFlight, key)
if err == nil {
return f.val, nil
}
return nil, errors.Trace(err)
}
// cachedValue returns any cached value for the given key
// and whether it was found.
func (c *Cache) cachedValue(key Key, now time.Time) (any, bool) {
c.mu.Lock()
defer c.mu.Unlock()
if now.After(c.expire) {
c.old = c.new
c.new = make(map[Key]entry)
c.expire = now.Add(c.maxAge)
}
if e, ok := c.entry(c.new, key, now); ok {
return e.value, true
}
if e, ok := c.entry(c.old, key, now); ok {
// An old entry has been accessed; move it to the new
// map so that we only use a single map access for
// subsequent lookups. Note that because we use the same
// duration for cache refresh (c.expire) as for max
// entry age, this is strictly speaking unnecessary
// because any entries in old will have expired by the
// time it is dropped.
c.new[key] = e
delete(c.old, key)
return e.value, true
}
return nil, false
}
// entry returns an entry from the map and whether it
// was found. If the entry has expired, it is deleted from the map.
func (c *Cache) entry(m map[Key]entry, key Key, now time.Time) (entry, bool) {
e, ok := m[key]
if !ok {
return entry{}, false
}
if now.After(e.expire) {
// Delete expired entries.
delete(m, key)
return entry{}, false
}
return e, true
}
================================================
FILE: cache/cache_test.go
================================================
// Copyright 2015 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package cache_test
import (
"fmt"
"sync"
"time"
"github.com/juju/errors"
"github.com/juju/utils/v4/cache"
gc "gopkg.in/check.v1"
)
type suite struct{}
var _ = gc.Suite(&suite{})
func (*suite) TestSimpleGet(c *gc.C) {
p := cache.New(time.Hour)
v, err := p.Get("a", fetchValue(2))
c.Assert(err, gc.IsNil)
c.Assert(v, gc.Equals, 2)
}
func (*suite) TestEvict(c *gc.C) {
p := cache.New(time.Hour)
v, err := p.Get("a", fetchValue(2))
c.Assert(err, gc.IsNil)
c.Assert(v, gc.Equals, 2)
v, err = p.Get("a", fetchValue(4))
c.Assert(err, gc.IsNil)
c.Assert(v, gc.Equals, 2)
p.Evict("a")
v, err = p.Get("a", fetchValue(3))
c.Assert(err, gc.IsNil)
c.Assert(v, gc.Equals, 3)
v, err = p.Get("a", fetchValue(4))
c.Assert(err, gc.IsNil)
c.Assert(v, gc.Equals, 3)
}
func (*suite) TestEvictOld(c *gc.C) {
// Test that evict removes entries even when they're
// in the old map.
now := time.Now()
p := cache.New(time.Minute)
// Populate the cache with an initial entry.
v, err := cache.GetAtTime(p, "a", fetchValue("a"), now)
c.Assert(err, gc.IsNil)
c.Assert(v, gc.Equals, "a")
c.Assert(p.Len(), gc.Equals, 1)
v, err = cache.GetAtTime(p, "b", fetchValue("b"), now.Add(time.Minute/2))
c.Assert(err, gc.IsNil)
c.Assert(v, gc.Equals, "b")
c.Assert(p.Len(), gc.Equals, 2)
// Fetch an item after the expiry time,
// causing current entries to be moved to old.
v, err = cache.GetAtTime(p, "a", fetchValue("a1"), now.Add(time.Minute+1))
c.Assert(err, gc.IsNil)
c.Assert(v, gc.Equals, "a1")
c.Assert(p.Len(), gc.Equals, 2)
c.Assert(cache.OldLen(p), gc.Equals, 1)
p.Evict("b")
v, err = cache.GetAtTime(p, "b", fetchValue("b1"), now.Add(time.Minute+2))
c.Assert(err, gc.IsNil)
c.Assert(v, gc.Equals, "b1")
}
func (*suite) TestFetchError(c *gc.C) {
p := cache.New(time.Hour)
expectErr := errors.New("hello")
v, err := p.Get("a", fetchError(expectErr))
c.Assert(err, gc.ErrorMatches, "hello")
c.Assert(errors.Cause(err), gc.Equals, expectErr)
c.Assert(v, gc.Equals, nil)
}
func (*suite) TestFetchOnlyOnce(c *gc.C) {
p := cache.New(time.Hour)
v, err := p.Get("a", fetchValue(2))
c.Assert(err, gc.IsNil)
c.Assert(v, gc.Equals, 2)
v, err = p.Get("a", fetchError(errUnexpectedFetch))
c.Assert(err, gc.IsNil)
c.Assert(v, gc.Equals, 2)
}
func (*suite) TestEntryExpiresAfterMaxEntryAge(c *gc.C) {
now := time.Now()
p := cache.New(time.Minute)
v, err := cache.GetAtTime(p, "a", fetchValue(2), now)
c.Assert(err, gc.IsNil)
c.Assert(v, gc.Equals, 2)
// Entry is definitely not expired before half the entry expiry time.
v, err = cache.GetAtTime(p, "a", fetchError(errUnexpectedFetch), now.Add(time.Minute/2-1))
c.Assert(err, gc.IsNil)
c.Assert(v, gc.Equals, 2)
// Entry is definitely expired after the entry expiry time
v, err = cache.GetAtTime(p, "a", fetchValue(3), now.Add(time.Minute+1))
c.Assert(v, gc.Equals, 3)
}
func (*suite) TestEntriesRemovedWhenNotRetrieved(c *gc.C) {
now := time.Now()
p := cache.New(time.Minute)
// Populate the cache with an initial entry.
v, err := cache.GetAtTime(p, "a", fetchValue("a"), now)
c.Assert(err, gc.IsNil)
c.Assert(v, gc.Equals, "a")
c.Assert(p.Len(), gc.Equals, 1)
// Fetch another item after the expiry time,
// causing current entries to be moved to old.
v, err = cache.GetAtTime(p, "b", fetchValue("b"), now.Add(time.Minute+1))
c.Assert(err, gc.IsNil)
c.Assert(v, gc.Equals, "b")
c.Assert(p.Len(), gc.Equals, 2)
c.Assert(cache.OldLen(p), gc.Equals, 1)
// Fetch the other item after another expiry time
// causing the old entries to be discarded because
// nothing has fetched them.
v, err = cache.GetAtTime(p, "b", fetchValue("b"), now.Add(time.Minute*2+2))
c.Assert(err, gc.IsNil)
c.Assert(v, gc.Equals, "b")
c.Assert(p.Len(), gc.Equals, 1)
}
// TestRefreshedEntry tests the code path where a value is moved
// from the old map to new.
func (*suite) TestRefreshedEntry(c *gc.C) {
now := time.Now()
p := cache.New(time.Minute)
// Populate the cache with an initial entry.
v, err := cache.GetAtTime(p, "a", fetchValue("a"), now)
c.Assert(err, gc.IsNil)
c.Assert(v, gc.Equals, "a")
c.Assert(p.Len(), gc.Equals, 1)
// Fetch another item very close to the expiry time.
v, err = cache.GetAtTime(p, "b", fetchValue("b"), now.Add(time.Minute-1))
c.Assert(err, gc.IsNil)
c.Assert(v, gc.Equals, "b")
c.Assert(p.Len(), gc.Equals, 2)
// Fetch it again just after the expiry time,
// which should move it into the new map.
v, err = cache.GetAtTime(p, "b", fetchError(errUnexpectedFetch), now.Add(time.Minute+1))
c.Assert(err, gc.IsNil)
c.Assert(v, gc.Equals, "b")
c.Assert(p.Len(), gc.Equals, 2)
// Fetch another item, causing "a" to be removed from the cache
// and keeping "b" in there.
v, err = cache.GetAtTime(p, "c", fetchValue("c"), now.Add(time.Minute*2+2))
c.Assert(err, gc.IsNil)
c.Assert(v, gc.Equals, "c")
c.Assert(p.Len(), gc.Equals, 2)
}
// TestConcurrentFetch checks that the cache is safe
// to use concurrently. It is designed to fail when
// tested with the race detector enabled.
func (*suite) TestConcurrentFetch(c *gc.C) {
p := cache.New(time.Minute)
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
v, err := p.Get("a", fetchValue("a"))
c.Check(err, gc.IsNil)
c.Check(v, gc.Equals, "a")
}()
wg.Add(1)
go func() {
defer wg.Done()
v, err := p.Get("b", fetchValue("b"))
c.Check(err, gc.IsNil)
c.Check(v, gc.Equals, "b")
}()
wg.Wait()
}
func (*suite) TestRefreshSpread(c *gc.C) {
now := time.Now()
p := cache.New(time.Minute)
// Get all values to start with.
const N = 100
for i := 0; i < N; i++ {
v, err := cache.GetAtTime(p, fmt.Sprint(i), fetchValue(i), now)
c.Assert(err, gc.IsNil)
c.Assert(v, gc.Equals, i)
}
counts := make([]int, time.Minute/time.Millisecond/10+1)
// Continually get values over the course of the
// expiry time; the fetches should be spread out.
slot := 0
for t := now.Add(0); t.Before(now.Add(time.Minute + 1)); t = t.Add(time.Millisecond * 10) {
for i := 0; i < N; i++ {
cache.GetAtTime(p, fmt.Sprint(i), func() (any, error) {
counts[slot]++
return i, nil
}, t)
}
slot++
}
// There should be no fetches in the first half of the cycle.
for i := 0; i < len(counts)/2; i++ {
c.Assert(counts[i], gc.Equals, 0, gc.Commentf("slot %d", i))
}
max := 0
total := 0
for _, count := range counts {
if count > max {
max = count
}
total += count
}
if max > 10 {
c.Errorf("requests grouped too closely (max %d)", max)
}
c.Assert(total, gc.Equals, N)
}
func (*suite) TestSingleFlight(c *gc.C) {
p := cache.New(time.Minute)
start := make(chan struct{})
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
x, err := p.Get("x", func() (any, error) {
start <- struct{}{}
<-start
return 99, nil
})
c.Check(x, gc.Equals, 99)
c.Check(err, gc.Equals, nil)
}()
// Wait for the fetch to start.
<-start
wg.Add(1)
go func() {
defer wg.Done()
x, err := p.Get("x", func() (any, error) {
c.Errorf("fetch function unexpectedly called with inflight request")
return 55, nil
})
c.Check(x, gc.Equals, 99)
c.Check(err, gc.Equals, nil)
}()
// Check that we can still get other values while the
// other fetches are in progress.
y, err := p.Get("y", func() (any, error) {
return 88, nil
})
c.Check(y, gc.Equals, 88)
c.Check(err, gc.Equals, nil)
// Let the original fetch proceed, which should let the other one
// succeed too, but sleep for a little bit to let the second goroutine
// actually initiate its request.
time.Sleep(time.Millisecond)
start <- struct{}{}
wg.Wait()
}
var errUnexpectedFetch = errors.New("fetch called unexpectedly")
func fetchError(err error) func() (any, error) {
return func() (any, error) {
return nil, err
}
}
func fetchValue(val any) func() (any, error) {
return func() (any, error) {
return val, nil
}
}
================================================
FILE: cache/export_test.go
================================================
// Copyright 2015 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package cache
var GetAtTime = (*Cache).getAtTime
func OldLen(c *Cache) int {
return len(c.old)
}
================================================
FILE: cache/package_test.go
================================================
// Copyright 2015 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package cache_test
import (
"testing"
gc "gopkg.in/check.v1"
)
func TestPackage(t *testing.T) {
gc.TestingT(t)
}
================================================
FILE: cert/cert.go
================================================
// Copyright 2012, 2013 Canonical Ltd.
// Copyright 2016 Cloudbase solutions
// Licensed under the LGPLv3, see LICENCE file for details.
package cert
import (
"crypto"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"fmt"
"github.com/juju/errors"
)
// OtherName type for asn1 encoding
type OtherName struct {
A string `asn1:"utf8"`
}
// GeneralName type for asn1 encoding
type GeneralName struct {
OID asn1.ObjectIdentifier
OtherName `asn1:"tag:0"`
}
// GeneralNames type for asn1 encoding
type GeneralNames struct {
GeneralName `asn1:"tag:0"`
}
var (
// https://support.microsoft.com/en-us/kb/287547
// szOID_NT_PRINCIPAL_NAME 1.3.6.1.4.1.311.20.2.3
szOID = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 20, 2, 3}
// http://www.umich.edu/~x509/ssleay/asn1-oids.html
// 2 5 29 17 subjectAltName
subjAltName = asn1.ObjectIdentifier{2, 5, 29, 17}
)
// getUPNExtensionValue returns marsheled asn1 encoded info
func getUPNExtensionValue(subject pkix.Name) ([]byte, error) {
// returns the ASN.1 encoding of val
// in addition to the struct tags recognized
// we used:
// utf8 => causes string to be marsheled as ASN.1, UTF8 strings
// tag:x => specifies the ASN.1 tag number; imples ASN.1 CONTEXT SPECIFIC
return asn1.Marshal(GeneralNames{
GeneralName: GeneralName{
// init our ASN.1 object identifier
OID: szOID,
OtherName: OtherName{
A: subject.CommonName,
},
},
})
}
// ParseCert parses the given PEM-formatted X509 certificate.
func ParseCert(certPEM string) (*x509.Certificate, error) {
certPEMData := []byte(certPEM)
for len(certPEMData) > 0 {
var certBlock *pem.Block
certBlock, certPEMData = pem.Decode(certPEMData)
if certBlock == nil {
break
}
if certBlock.Type == "CERTIFICATE" {
cert, err := x509.ParseCertificate(certBlock.Bytes)
return cert, err
}
}
return nil, errors.New("no certificates found")
}
// ParseCertAndKey parses the given PEM-formatted X509 certificate
// and RSA private key.
func ParseCertAndKey(certPEM, keyPEM string) (*x509.Certificate, crypto.Signer, error) {
tlsCert, err := tls.X509KeyPair([]byte(certPEM), []byte(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.(crypto.Signer)
if !ok {
return nil, nil, fmt.Errorf("private key with unexpected type %T", tlsCert.PrivateKey)
}
return cert, key, nil
}
================================================
FILE: cert/cert_test.go
================================================
// Copyright 2012, 2013 Canonical Ltd.
// Copyright 2016 Cloudbase solutions
// Licensed under the LGPLv3, see LICENCE file for details.
package cert_test
import (
"testing"
jc "github.com/juju/testing/checkers"
gc "gopkg.in/check.v1"
"github.com/juju/utils/v4/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, jc.ErrorIsNil)
c.Assert(xcert.Subject.CommonName, gc.Equals, `juju-generated CA for model "juju testing"`)
xcert, err = cert.ParseCert(caKeyPEM)
c.Check(xcert, gc.IsNil)
c.Assert(err, gc.ErrorMatches, "no certificates found")
xcert, err = cert.ParseCert("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, jc.ErrorIsNil)
c.Assert(xcert.Subject.CommonName, gc.Equals, `juju-generated CA for model "juju testing"`)
c.Assert(key, gc.NotNil)
c.Assert(xcert.PublicKey, gc.DeepEquals, key.Public())
}
var (
caCertPEM = `
-----BEGIN CERTIFICATE-----
MIICHDCCAcagAwIBAgIUfzWn5ktGMxD6OiTgfiZyvKdM+ZYwDQYJKoZIhvcNAQEL
BQAwazENMAsGA1UEChMEanVqdTEzMDEGA1UEAwwqanVqdS1nZW5lcmF0ZWQgQ0Eg
Zm9yIG1vZGVsICJqdWp1IHRlc3RpbmciMSUwIwYDVQQFExwxMjM0LUFCQ0QtSVMt
Tk9ULUEtUkVBTC1VVUlEMB4XDTE2MDkyMTEwNDgyN1oXDTI2MDkyODEwNDgyN1ow
azENMAsGA1UEChMEanVqdTEzMDEGA1UEAwwqanVqdS1nZW5lcmF0ZWQgQ0EgZm9y
IG1vZGVsICJqdWp1IHRlc3RpbmciMSUwIwYDVQQFExwxMjM0LUFCQ0QtSVMtTk9U
LUEtUkVBTC1VVUlEMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAL+0X+1zl2vt1wI4
1Q+RnlltJyaJmtwCbHRhREXVGU7t0kTMMNERxqLnuNUyWRz90Rg8s9XvOtCqNYW7
mypGrFECAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgKkMA8GA1UdEwEB/wQFMAMBAf8w
HQYDVR0OBBYEFHueMLZ1QJ/2sKiPIJ28TzjIMRENMA0GCSqGSIb3DQEBCwUAA0EA
ovZN0RbUHrO8q9Eazh0qPO4mwW9jbGTDz126uNrLoz1g3TyWxIas1wRJ8IbCgxLy
XUrBZO5UPZab66lJWXyseA==
-----END CERTIFICATE-----
`
caKeyPEM = `
-----BEGIN RSA PRIVATE KEY-----
MIIBOgIBAAJBAL+0X+1zl2vt1wI41Q+RnlltJyaJmtwCbHRhREXVGU7t0kTMMNER
xqLnuNUyWRz90Rg8s9XvOtCqNYW7mypGrFECAwEAAQJAMPa+JaUHgO6foxam/LIB
0u95N3OgFR+dWeBaEsgKDclpREdJ0rXNI+3C3kwqeEZR4omoPlBeSEewSkwHxpmI
0QIhAOjKiHZ5v6R8haleipbDzkGUnZW07hEwL5Ld4MNx/QQ1AiEA0tEzSSNAdM0C
M/vY0x5mekIYai8/tFSEG9PJ3ZkpEy0CIQCo9B3YxwI1Un777vbs903iQQeiWP+U
EAHnOQvhLgDxpQIgGkpml+9igW5zoOH+h02aQBLwEoXz7tw/YW0HFrCcE70CIGkS
ve4WjiEqnQaHNAPy0hY/1DfIgBOSpOfnkFHOk9vX
-----END RSA PRIVATE KEY-----
`
)
================================================
FILE: cert/exports_test.go
================================================
// Copyright 2016 Canonical ltd.
// Copyright 2016 Cloudbase solutions
// Licensed under the LGPLv3, see LICENCE file for details.
package cert
================================================
FILE: command.go
================================================
// Copyright 2013 Canonical Ltd.
// Licensed under the LGPLv3, 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
}
================================================
FILE: command_test.go
================================================
// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package utils_test
import (
"io/ioutil"
"path/filepath"
"runtime"
"github.com/juju/testing"
gc "gopkg.in/check.v1"
"github.com/juju/utils/v4"
)
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 {
testing.IsolationSuite
}
var _ = gc.Suite(&commandSuite{})
func (s *commandSuite) TestRunCommandCombinesOutput(c *gc.C) {
var content string
var cmdName string
var expect string
if runtime.GOOS != "windows" {
content = `#!/bin/bash --norc
echo stdout
echo stderr 1>&2
`
cmdName = "test-output"
expect = "stdout\nstderr\n"
} else {
content = `@echo off
echo stdout
echo stderr 1>&2
`
cmdName = "test-output.bat"
expect = "stdout\r\nstderr \r\n"
}
patchExecutable(s, c.MkDir(), cmdName, content)
output, err := utils.RunCommand("test-output")
c.Assert(err, gc.IsNil)
c.Assert(output, gc.Equals, expect)
}
func (s *commandSuite) TestRunCommandNonZeroExit(c *gc.C) {
var content string
var cmdName string
var expect string
if runtime.GOOS != "windows" {
content = `#!/bin/bash --norc
echo stdout
exit 42
`
cmdName = "test-output"
expect = "stdout\n"
} else {
content = `@echo off
echo stdout
exit 42
`
cmdName = "test-output.bat"
expect = "stdout\r\n"
}
patchExecutable(s, c.MkDir(), cmdName, content)
output, err := utils.RunCommand("test-output")
c.Assert(err, gc.ErrorMatches, `exit status 42`)
c.Assert(output, gc.Equals, expect)
}
================================================
FILE: context.go
================================================
// Copyright 2018 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package utils
import (
"fmt"
"sync"
"time"
"golang.org/x/net/context"
"github.com/juju/clock"
)
// timerCtx is an implementation of context.Context that
// is done when a given deadline has passed
// (as measured by the Clock in the clock field)
type timerCtx struct {
clock clock.Clock
timer clock.Timer
deadline time.Time
parent context.Context
done chan struct{}
// mu guards err.
mu sync.Mutex
// err holds context.Canceled or context.DeadlineExceeded
// after the context has been canceled.
// If this is non-nil, then done will have been closed.
err error
}
func (ctx *timerCtx) Deadline() (time.Time, bool) {
return ctx.deadline, true
}
func (ctx *timerCtx) Err() error {
ctx.mu.Lock()
defer ctx.mu.Unlock()
return ctx.err
}
func (ctx *timerCtx) Value(key any) any {
return ctx.parent.Value(key)
}
func (ctx *timerCtx) Done() <-chan struct{} {
return ctx.done
}
func (ctx *timerCtx) cancel(err error) {
ctx.mu.Lock()
defer ctx.mu.Unlock()
if err == nil {
panic("cancel with nil error!")
}
if ctx.err != nil {
// Already canceled - no need to do anything.
return
}
ctx.err = err
if ctx.timer != nil {
ctx.timer.Stop()
}
close(ctx.done)
}
func (ctx *timerCtx) String() string {
return fmt.Sprintf("%v.WithDeadline(%s [%s])", ctx.parent, ctx.deadline, ctx.deadline.Sub(ctx.clock.Now()))
}
// ContextWithTimeout is like context.WithTimeout
// except that it works with a clock.Clock rather than
// wall-clock time.
func ContextWithTimeout(parent context.Context, clk clock.Clock, timeout time.Duration) (context.Context, context.CancelFunc) {
return ContextWithDeadline(parent, clk, clk.Now().Add(timeout))
}
// ContextWithDeadline is like context.WithDeadline
// except that it works with a clock.Clock rather than
// wall-clock time.
func ContextWithDeadline(parent context.Context, clk clock.Clock, deadline time.Time) (context.Context, context.CancelFunc) {
d := deadline.Sub(clk.Now())
ctx := &timerCtx{
clock: clk,
parent: parent,
deadline: deadline,
done: make(chan struct{}),
}
if d <= 0 {
// deadline has already passed
ctx.cancel(context.DeadlineExceeded)
return ctx, func() {}
}
ctx.timer = clk.NewTimer(d)
go func() {
select {
case <-ctx.timer.Chan():
ctx.cancel(context.DeadlineExceeded)
case <-parent.Done():
ctx.cancel(parent.Err())
case <-ctx.done:
}
}()
return ctx, func() {
ctx.cancel(context.Canceled)
}
}
================================================
FILE: context_test.go
================================================
// Copyright 2018 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package utils_test
import (
"fmt"
"time"
"golang.org/x/net/context"
"github.com/juju/clock/testclock"
jc "github.com/juju/testing/checkers"
gc "gopkg.in/check.v1"
"github.com/juju/utils/v4"
)
type contextSuite struct{}
var _ = gc.Suite(&contextSuite{})
// Note: the logic in these tests was copied from the tests
// in the Go standard library.
func (*contextSuite) TestDeadline(c *gc.C) {
clk := testclock.NewClock(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC))
ctx, cancel := utils.ContextWithDeadline(context.Background(), clk, clk.Now().Add(50*time.Millisecond))
defer cancel()
c.Assert(fmt.Sprint(ctx), gc.Equals, `context.Background.WithDeadline(2000-01-01 00:00:00.05 +0000 UTC [50ms])`)
testContextDeadline(c, ctx, "WithDeadline", clk, 1, 50*time.Millisecond)
ctx, cancel = utils.ContextWithDeadline(context.Background(), clk, clk.Now().Add(50*time.Millisecond))
defer cancel()
o := otherContext{ctx}
testContextDeadline(c, o, "WithDeadline+otherContext", clk, 1, 50*time.Millisecond)
ctx, cancel = utils.ContextWithDeadline(context.Background(), clk, clk.Now().Add(50*time.Millisecond))
defer cancel()
o = otherContext{ctx}
ctx, _ = utils.ContextWithDeadline(o, clk, clk.Now().Add(4*time.Second))
testContextDeadline(c, ctx, "WithDeadline+otherContext+WithDeadline", clk, 2, 50*time.Millisecond)
ctx, cancel = utils.ContextWithDeadline(context.Background(), clk, clk.Now().Add(-time.Millisecond))
defer cancel()
testContextDeadline(c, ctx, "WithDeadline+inthepast", clk, 0, 0)
ctx, cancel = utils.ContextWithDeadline(context.Background(), clk, clk.Now())
testContextDeadline(c, ctx, "WithDeadline+now", clk, 0, 0)
}
func (*contextSuite) TestTimeout(c *gc.C) {
clk := testclock.NewClock(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC))
ctx, _ := utils.ContextWithTimeout(context.Background(), clk, 50*time.Millisecond)
c.Assert(fmt.Sprint(ctx), gc.Equals, `context.Background.WithDeadline(2000-01-01 00:00:00.05 +0000 UTC [50ms])`)
testContextDeadline(c, ctx, "WithTimeout", clk, 1, 50*time.Millisecond)
ctx, _ = utils.ContextWithTimeout(context.Background(), clk, 50*time.Millisecond)
o := otherContext{ctx}
testContextDeadline(c, o, "WithTimeout+otherContext", clk, 1, 50*time.Millisecond)
ctx, _ = utils.ContextWithTimeout(context.Background(), clk, 50*time.Millisecond)
o = otherContext{ctx}
ctx, _ = utils.ContextWithTimeout(o, clk, 3*time.Second)
testContextDeadline(c, ctx, "WithTimeout+otherContext+WithTimeout", clk, 2, 50*time.Millisecond)
}
func (*contextSuite) TestCanceledTimeout(c *gc.C) {
clk := testclock.NewClock(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC))
ctx, _ := utils.ContextWithTimeout(context.Background(), clk, time.Second)
o := otherContext{ctx}
ctx, cancel := utils.ContextWithTimeout(o, clk, 2*time.Second)
cancel()
time.Sleep(100 * time.Millisecond) // let cancelation propagate
select {
case <-ctx.Done():
default:
c.Errorf("<-ctx.Done() blocked, but shouldn't have")
}
c.Assert(ctx.Err(), gc.Equals, context.Canceled)
}
func testContextDeadline(c *gc.C, ctx context.Context, name string, clk *testclock.Clock, waiters int, failAfter time.Duration) {
err := clk.WaitAdvance(failAfter, 0, waiters)
c.Assert(err, jc.ErrorIsNil)
select {
case <-time.After(time.Second):
c.Fatalf("%s: context should have timed out", name)
case <-ctx.Done():
}
c.Assert(ctx.Err(), gc.Equals, context.DeadlineExceeded)
}
// otherContext is a Context that's not one of the types defined in context.go.
// This lets us test code paths that differ based on the underlying type of the
// Context.
type otherContext struct {
context.Context
}
================================================
FILE: du/LICENSE.ricochet2200
================================================
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
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 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.
For more information, please refer to <http://unlicense.org>
================================================
FILE: du/diskusage.go
================================================
// Copied from https://github.com/ricochet2200/go-disk-usage
// Copyright 2011 Rick Smith.
// Use of this source code is governed by a public domain
// license that can be found in the LICENSE.ricochet2200 file.
//
//go:build !windows
// +build !windows
package du
import "syscall"
type DiskUsage struct {
stat *syscall.Statfs_t
}
// Returns an object holding the disk usage of volumePath
// This function assumes volumePath is a valid path
func NewDiskUsage(volumePath string) *DiskUsage {
var stat syscall.Statfs_t
syscall.Statfs(volumePath, &stat)
return &DiskUsage{&stat}
}
// Total free bytes on file system
func (this *DiskUsage) Free() uint64 {
return this.stat.Bfree * uint64(this.stat.Bsize)
}
// Total available bytes on file system to an unpriveleged user
func (this *DiskUsage) Available() uint64 {
return this.stat.Bavail * uint64(this.stat.Bsize)
}
// Total size of the file system
func (this *DiskUsage) Size() uint64 {
return this.stat.Blocks * uint64(this.stat.Bsize)
}
// Total bytes used in file system
func (this *DiskUsage) Used() uint64 {
return this.Size() - this.Free()
}
// Percentage of use on the file system
func (this *DiskUsage) Usage() float32 {
return float32(this.Used()) / float32(this.Size())
}
================================================
FILE: du/diskusage_windows.go
================================================
// Copied from https://github.com/ricochet2200/go-disk-usage
// Copyright 2011 Rick Smith.
// Use of this source code is governed by a public domain
// license that can be found in the LICENSE.ricochet2200 file.
//
package du
import (
"syscall"
"unsafe"
)
type DiskUsage struct {
freeBytes int64
totalBytes int64
availBytes int64
}
// Returns an object holding the disk usage of volumePath
// This function assumes volumePath is a valid path
func NewDiskUsage(volumePath string) *DiskUsage {
h := syscall.MustLoadDLL("kernel32.dll")
c := h.MustFindProc("GetDiskFreeSpaceExW")
du := &DiskUsage{}
c.Call(
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(volumePath))),
uintptr(unsafe.Pointer(&du.freeBytes)),
uintptr(unsafe.Pointer(&du.totalBytes)),
uintptr(unsafe.Pointer(&du.availBytes)))
return du
}
// Total free bytes on file system
func (this *DiskUsage) Free() uint64 {
return uint64(this.freeBytes)
}
// Total available bytes on file system to an unpriveleged user
func (this *DiskUsage) Available() uint64 {
return uint64(this.availBytes)
}
// Total size of the file system
func (this *DiskUsage) Size() uint64 {
return uint64(this.totalBytes)
}
// Total bytes used in file system
func (this *DiskUsage) Used() uint64 {
return this.Size() - this.Free()
}
// Percentage of use on the file system
func (this *DiskUsage) Usage() float32 {
return float32(this.Used()) / float32(this.Size())
}
================================================
FILE: errors.go
================================================
// Copyright 2024 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package utils
import (
"fmt"
)
// RcPassthroughError indicates that a Juju plugin command exited with a
// non-zero exit code. This error is used to exit with the return code.
type RcPassthroughError struct {
Code int
}
// Error implements error.
func (e *RcPassthroughError) Error() string {
return fmt.Sprintf("subprocess encountered error code %v", e.Code)
}
// IsRcPassthroughError returns whether the error is an RcPassthroughError.
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}
}
================================================
FILE: exec/exec.go
================================================
// Copyright 2016 Canonical Ltd.
// Copyright 2016 Cloudbase Solutions
// Licensed under the LGPLv3, see LICENCE file for details.
package exec
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"syscall"
"time"
"github.com/juju/clock"
"github.com/juju/errors"
"github.com/juju/loggo/v2"
)
var logger = loggo.GetLogger("juju.util.exec")
// Parameters for RunCommands. Commands contains one or more commands to be
// executed using bash or PowerShell. If WorkingDir is set, this is passed
// through. Similarly if the Environment is specified, this is used
// for executing the command.
// TODO: refactor this to use a config struct and a constructor. Remove todo
// and extra code from WaitWithCancel once this is done.
type RunParams struct {
Commands string
WorkingDir string
Environment []string
Clock clock.Clock
KillProcess func(*os.Process) error
User string
tempDir string
stdout *bytes.Buffer
stderr *bytes.Buffer
ps *exec.Cmd
}
// ExecResponse contains the return code and output generated by executing a
// command.
type ExecResponse struct {
Code int
Stdout []byte
Stderr []byte
}
// mergeEnvironment takes in a string array representing the desired environment
// and merges it with the current environment. On Windows, clearing the environment,
// or having missing environment variables, may lead to standard go packages not working
// (os.TempDir relies on $env:TEMP), and powershell erroring out
// Currently this function is only used for windows
func mergeEnvironment(env []string) []string {
if env == nil {
return nil
}
m := make(map[string]string)
var tmpEnv []string
for _, val := range os.Environ() {
varSplit := strings.SplitN(val, "=", 2)
m[varSplit[0]] = varSplit[1]
}
for _, val := range env {
varSplit := strings.SplitN(val, "=", 2)
m[varSplit[0]] = varSplit[1]
}
for key, val := range m {
tmpEnv = append(tmpEnv, key+"="+val)
}
return tmpEnv
}
// shellAndArgs returns the name of the shell command and arguments to run the
// specified script. shellAndArgs may write into the provided temporary
// directory, which will be maintained until the process exits.
func shellAndArgs(tempDir, script, user string) (string, []string, error) {
var scriptFile string
var cmd string
var args []string
switch runtime.GOOS {
case "windows":
scriptFile = filepath.Join(tempDir, "script.ps1")
cmd = "powershell.exe"
args = []string{
"-NoProfile",
"-NonInteractive",
"-ExecutionPolicy", "RemoteSigned",
"-File", scriptFile,
}
// Exceptions don't result in a non-zero exit code by default
// when using -File. The exit code of an explicit "exit" when
// using -Command is ignored and results in an exit code of 1.
// We use -File and trap exceptions to cover both.
script = "trap {Write-Error $_; exit 1}\n" + script
default:
scriptFile = filepath.Join(tempDir, "script.sh")
if user == "" {
cmd = "/bin/bash"
args = []string{scriptFile}
} else {
// Need to make the tempDir readable by all so the user can see it.
err := os.Chmod(tempDir, 0755)
if err != nil {
return "", nil, errors.Annotatef(err, "making tempdir readable by %q", user)
}
cmd = "/bin/su"
args = []string{user, "--login", "--command", fmt.Sprintf("/bin/bash %s", scriptFile)}
}
}
err := ioutil.WriteFile(scriptFile, []byte(script), 0644)
if err != nil {
return "", nil, err
}
return cmd, args, nil
}
// Run sets up the command environment (environment variables, working dir)
// and starts the process. The commands are passed into bash on Linux machines
// and to powershell on Windows machines.
func (r *RunParams) Run() error {
if runtime.GOOS == "windows" {
r.Environment = mergeEnvironment(r.Environment)
}
tempDir, err := ioutil.TempDir("", "juju-exec")
if err != nil {
return err
}
shell, args, err := shellAndArgs(tempDir, r.Commands, r.User)
if err != nil {
if err := os.RemoveAll(tempDir); err != nil {
logger.Warningf("failed to remove temporary directory: %v", err)
}
return err
}
r.ps = exec.Command(shell, args...)
if r.Environment != nil {
r.ps.Env = r.Environment
}
if r.WorkingDir != "" {
r.ps.Dir = r.WorkingDir
}
r.populateSysProcAttr()
// If there is no user provided KillProcess function we
// use the default one.
if r.KillProcess == nil {
r.KillProcess = KillProcess
}
r.tempDir = tempDir
r.stdout = &bytes.Buffer{}
r.stderr = &bytes.Buffer{}
r.ps.Stdout = r.stdout
r.ps.Stderr = r.stderr
return r.ps.Start()
}
// Process returns the *os.Process instance of the current running process
// This will allow us to kill the process if needed, or get more information
// on the process
func (r *RunParams) Process() *os.Process {
if r.ps != nil && r.ps.Process != nil {
return r.ps.Process
}
return nil
}
// Wait blocks until the process exits, and returns an ExecResponse type
// containing stdout, stderr and the return code of the process. 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 (r *RunParams) Wait() (*ExecResponse, error) {
var err error
if r.ps == nil {
return nil, errors.New("No process has been started yet")
}
err = r.ps.Wait()
if err := os.RemoveAll(r.tempDir); err != nil {
logger.Warningf("failed to remove temporary directory: %v", err)
}
result := &ExecResponse{
Stdout: r.stdout.Bytes(),
Stderr: r.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
}
// ErrCancelled is returned by WaitWithCancel in case it successfully manages to kill
// the running process.
var ErrCancelled = errors.New("command cancelled")
// timeWaitForKill reperesent the time we wait after attempting to kill a
// process before bailing out and returning.
const timeWaitForKill = 30 * time.Second
type resultWithError struct {
execResult *ExecResponse
err error
}
// WaitWithCancel waits until the process exits or until a signal is sent on the
// cancel channel. In case a signal is sent it first tries to kill the process and
// return ErrCancelled. If it fails at killing the process it will return anyway
// and report the problematic PID.
func (r *RunParams) WaitWithCancel(cancel <-chan struct{}) (*ExecResponse, error) {
// TODO: Remove this once we make Clock a required field
_clock := r.Clock
if _clock == nil {
_clock = clock.WallClock
}
done := make(chan resultWithError, 1)
go func() {
defer close(done)
waitResult, err := r.Wait()
done <- resultWithError{waitResult, err}
}()
select {
case resWithError := <-done:
return resWithError.execResult, errors.Trace(resWithError.err)
case <-cancel:
logger.Debugf("attempting to kill process")
err := r.KillProcess(r.ps.Process)
if err != nil {
logger.Debugf("kill returned: %s", err)
}
// After we issue a kill we expect the wait above to return within timeWaitForKill.
// In case it doesn't we just go on and assume the process is stuck, but we don't block
select {
case resWithError := <-done:
return resWithError.execResult, ErrCancelled
case <-_clock.After(timeWaitForKill):
return nil, errors.Errorf("tried to kill process %v, but timed out", r.ps.Process.Pid)
}
}
}
// RunCommands executes the Commands specified in the RunParams using
// powershell on windows, and '/bin/bash -s' on everything else,
// 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) {
err := run.Run()
if err != nil {
return nil, err
}
return run.Wait()
}
================================================
FILE: exec/exec_internal_test.go
================================================
// Copyright 2017 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package exec
import (
"os"
"path/filepath"
"runtime"
"github.com/juju/testing"
jc "github.com/juju/testing/checkers"
gc "gopkg.in/check.v1"
)
type execSuite struct {
testing.IsolationSuite
}
var _ = gc.Suite(&execSuite{})
func (*execSuite) TestShellAndArgsNoUserSpecified(c *gc.C) {
if runtime.GOOS == "windows" {
c.Skip("non-windows only test")
}
dir := c.MkDir()
stat, err := os.Stat(dir)
c.Assert(err, jc.ErrorIsNil)
c.Assert(stat.Mode().Perm(), gc.Equals, os.FileMode(0700))
cmd, args, err := shellAndArgs(dir, "env", "")
c.Assert(err, jc.ErrorIsNil)
scriptFile := filepath.Join(dir, "script.sh")
c.Assert(cmd, gc.Equals, "/bin/bash")
c.Assert(args, jc.DeepEquals, []string{scriptFile})
}
func (*execSuite) TestShellAndArgsAsUser(c *gc.C) {
if runtime.GOOS == "windows" {
c.Skip("non-windows only test")
}
dir := c.MkDir()
stat, err := os.Stat(dir)
c.Assert(err, jc.ErrorIsNil)
c.Assert(stat.Mode().Perm(), gc.Equals, os.FileMode(0700))
cmd, args, err := shellAndArgs(dir, "env", "ubuntu")
c.Assert(err, jc.ErrorIsNil)
scriptFile := filepath.Join(dir, "script.sh")
c.Assert(cmd, gc.Equals, "/bin/su")
command := "/bin/bash " + scriptFile
c.Assert(args, jc.DeepEquals, []string{"ubuntu", "--login", "--command", command})
// The directory is now readable by everyone.
stat, err = os.Stat(dir)
c.Assert(err, jc.ErrorIsNil)
c.Assert(stat.Mode().Perm(), gc.Equals, os.FileMode(0755))
// And the file is world readable
stat, err = os.Stat(scriptFile)
c.Assert(err, jc.ErrorIsNil)
c.Assert(stat.Mode().Perm(), gc.Equals, os.FileMode(0644))
}
================================================
FILE: exec/exec_linux_test.go
================================================
// Copyright 2014 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package exec_test
import (
jc "github.com/juju/testing/checkers"
gc "gopkg.in/check.v1"
"github.com/juju/utils/v4/exec"
)
// 0 is thrown by linux because RunParams.Wait
// only sets the code if the process exits cleanly
const cancelErrCode = 0
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",
}, {
message: "multiple commands",
commands: "cat\necho 123",
stdout: "123\n",
},
} {
c.Logf("%v: %s", i, test.message)
params := exec.RunParams{
Commands: test.commands,
WorkingDir: test.workingDir,
Environment: test.environment,
}
result, err := exec.RunCommands(params)
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)
err = params.Run()
c.Assert(err, gc.IsNil)
c.Assert(params.Process(), gc.Not(gc.IsNil))
result, err = params.Wait()
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)
err = params.Run()
c.Assert(err, gc.IsNil)
c.Assert(params.Process(), gc.Not(gc.IsNil))
result, err = params.WaitWithCancel(nil)
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)
}
================================================
FILE: exec/exec_test.go
================================================
// Copyright 2016 Canonical Ltd.
// Copyright 2016 Cloudbase Solutions
// Licensed under the LGPLv3, see LICENCE file for details.
package exec_test
import (
"fmt"
"os"
"time"
"github.com/juju/testing"
jc "github.com/juju/testing/checkers"
gc "gopkg.in/check.v1"
"github.com/juju/clock"
"github.com/juju/utils/v4/exec"
)
type execSuite struct {
testing.IsolationSuite
}
var _ = gc.Suite(&execSuite{})
func (*execSuite) TestWaitWithCancel(c *gc.C) {
params := exec.RunParams{
Commands: "sleep 100",
Clock: &mockClock{C: make(chan time.Time)},
}
err := params.Run()
c.Assert(err, gc.IsNil)
c.Assert(params.Process(), gc.Not(gc.IsNil))
cancelChan := make(chan struct{}, 1)
defer close(cancelChan)
cancelChan <- struct{}{}
result, err := params.WaitWithCancel(cancelChan)
c.Assert(err, gc.Equals, exec.ErrCancelled)
c.Assert(string(result.Stdout), gc.Equals, "")
c.Assert(string(result.Stderr), gc.Equals, "")
c.Assert(result.Code, gc.Equals, cancelErrCode)
}
func (s *execSuite) TestKillAbortedIfUnsuccessfull(c *gc.C) {
killCalled := false
mockChan := make(chan time.Time, 1)
defer close(mockChan)
params := exec.RunParams{
Commands: "sleep 100",
WorkingDir: "",
Environment: []string{},
Clock: &mockClock{C: mockChan},
KillProcess: func(*os.Process) error {
killCalled = true
return nil
},
}
err := params.Run()
c.Assert(err, gc.IsNil)
c.Assert(params.Process(), gc.Not(gc.IsNil))
cancelChan := make(chan struct{}, 1)
defer close(cancelChan)
cancelChan <- struct{}{}
mockChan <- time.Now()
res, err := params.WaitWithCancel(cancelChan)
c.Assert(err, gc.ErrorMatches, fmt.Sprintf("tried to kill process %d, but timed out", params.Process().Pid))
c.Assert(res, gc.IsNil)
c.Assert(killCalled, jc.IsTrue)
}
type mockClock struct {
clock.Clock
C <-chan time.Time
}
func (m *mockClock) After(t time.Duration) <-chan time.Time {
return m.C
}
================================================
FILE: exec/exec_unix.go
================================================
// Copyright 2016 Canonical Ltd.
// Copyright 2016 Cloudbase Solutions SRL
// Licensed under the LGPLv3, see LICENCE file for details.
//go:build !windows
// +build !windows
package exec
import (
"os"
"syscall"
)
// KillProcess tries to kill the process being ran by RunParams
// We need this convoluted implementation because everything
// ran under the bash script is spawned as a different process
// and doesn't get killed by a regular process.Kill()
// For details see https://groups.google.com/forum/#!topic/golang-nuts/XoQ3RhFBJl8
func KillProcess(proc *os.Process) error {
pgid, err := syscall.Getpgid(proc.Pid)
if err == nil {
return syscall.Kill(-pgid, 15) // note the minus sign
}
return nil
}
// populateSysProcAttr exists so that the method Kill on the same struct
// can work correctly. For more information see Kill's comment.
func (r *RunParams) populateSysProcAttr() {
r.ps.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
}
================================================
FILE: exec/exec_windows.go
================================================
// Copyright 2016 Canonical Ltd.
// Copyright 2016 Cloudbase Solutions SRL
// Licensed under the LGPLv3, see LICENCE file for details.
//go:build windows
// +build windows
package exec
import (
"os"
)
// KillProcess tries to kill the process passed in.
func KillProcess(proc *os.Process) error {
return proc.Kill()
}
// populateSysProcAttr is a noop on windows
func (r *RunParams) populateSysProcAttr() {}
================================================
FILE: exec/exec_windows_test.go
================================================
// Copyright 2014 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package exec_test
import (
"path/filepath"
"strings"
"syscall"
jc "github.com/juju/testing/checkers"
gc "gopkg.in/check.v1"
"github.com/juju/utils/v4/exec"
)
// 1 is thrown by powershell after the a command is cancelled
const cancelErrCode = 1
// longPath is copied over from the symlink package. This should be removed
// if we add it to gc or in some other convenience package
func longPath(path string) ([]uint16, error) {
pathp, err := syscall.UTF16FromString(path)
if err != nil {
return nil, err
}
longp := pathp
n, err := syscall.GetLongPathName(&pathp[0], &longp[0], uint32(len(longp)))
if err != nil {
return nil, err
}
if n > uint32(len(longp)) {
longp = make([]uint16, n)
n, err = syscall.GetLongPathName(&pathp[0], &longp[0], uint32(len(longp)))
if err != nil {
return nil, err
}
}
longp = longp[:n]
return longp, nil
}
func longPathAsString(path string) (string, error) {
longp, err := longPath(path)
if err != nil {
return "", err
}
return syscall.UTF16ToString(longp), nil
}
func (*execSuite) TestRunCommands(c *gc.C) {
newDir, err := longPathAsString(c.MkDir())
c.Assert(err, gc.IsNil)
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\r\n",
}, {
message: "test stderr capture",
commands: "Write-Error 'testing stderr'",
stderr: "testing stderr\r\n",
}, {
message: "test return code",
commands: "exit 42",
code: 42,
}, {
message: "test working dir",
commands: "(pwd).Path",
workingDir: newDir,
stdout: filepath.FromSlash(newDir) + "\r\n",
}, {
message: "test environment",
commands: "echo $env:OMG_IT_WORKS",
environment: []string{"OMG_IT_WORKS=like magic"},
stdout: "like magic\r\n",
},
} {
c.Logf("%v: %s", i, test.message)
params := exec.RunParams{
Commands: test.commands,
WorkingDir: test.workingDir,
Environment: test.environment,
}
result, err := exec.RunCommands(params)
c.Assert(err, gc.IsNil)
c.Assert(string(result.Stdout), gc.Equals, test.stdout)
c.Assert(string(result.Stderr), jc.Contains, test.stderr)
c.Assert(result.Code, gc.Equals, test.code)
err = params.Run()
c.Assert(err, gc.IsNil)
c.Assert(params.Process(), gc.Not(gc.IsNil))
result, err = params.Wait()
c.Assert(err, gc.IsNil)
c.Assert(string(result.Stdout), gc.Equals, test.stdout)
c.Assert(string(result.Stderr), jc.Contains, test.stderr)
c.Assert(result.Code, gc.Equals, test.code)
err = params.Run()
c.Assert(err, gc.IsNil)
c.Assert(params.Process(), gc.Not(gc.IsNil))
result, err = params.WaitWithCancel(nil)
c.Assert(err, gc.IsNil)
c.Assert(string(result.Stdout), gc.Equals, test.stdout)
c.Assert(string(result.Stderr), jc.Contains, 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)
stderr := strings.Replace(string(result.Stderr), "\r\n", "", -1)
c.Assert(stderr, jc.Contains, "is not recognized as the name of a cmdlet")
// 1 is returned by RunCommands when powershell commands throw exceptions
c.Assert(result.Code, gc.Equals, 1)
}
================================================
FILE: exec/package_test.go
================================================
// Copyright 2014 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package exec_test
import (
"testing"
gc "gopkg.in/check.v1"
)
func Test(t *testing.T) {
gc.TestingT(t)
}
================================================
FILE: export_test.go
================================================
// Copyright 2013 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package utils
import (
"time"
)
var (
GOMAXPROCS = &gomaxprocs
NumCPU = &numCPU
ResolveSudoByFunc = resolveSudo
)
func ExposeBackoffTimerDuration(bot *BackoffTimer) time.Duration {
return bot.currentDuration
}
================================================
FILE: file.go
================================================
// Copyright 2013 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package utils
import (
"fmt"
"io"
"io/ioutil"
"os"
"path"
"path/filepath"
"regexp"
"github.com/juju/errors"
)
// 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) (hDir string, err error) {
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 we're forced to use Home() to make the tests pass.
// All of our tests currently construct paths with the default user in
// mind eg "~/foo".
return Home(), nil
}
hDir, err = homeDir(userName)
if err != nil {
return "", err
}
return hDir, nil
}
// Only match paths starting with ~ (~user/test, ~/test). This will prevent
// accidental expansion on Windows when short form paths are present (C:\users\ADMINI~1\test)
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
}
// ExpandPath normalises (via Normalize) a path returning an absolute path.
func ExpandPath(path string) (string, error) {
normPath, err := NormalizePath(path)
if err != nil {
return "", errors.Annotate(err, "unable to normalise file path")
}
return filepath.Abs(normPath)
}
// EnsureBaseDir ensures that path is always prefixed by baseDir,
// allowing for the fact that path might have a Window drive letter in
// it.
func EnsureBaseDir(baseDir, path string) string {
if baseDir == "" {
return path
}
volume := filepath.VolumeName(path)
return filepath.Join(baseDir, path[len(volume):])
}
// 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(string) 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 func() { _ = f.Close() }()
defer func() {
if err != nil {
// Don't leave the temp file lying around on error.
// Close the file before removing. Trying to remove an open file on
// Windows will fail.
_ = f.Close()
_ = os.Remove(f.Name())
}
}()
if _, err := f.Write(contents); err != nil {
return fmt.Errorf("cannot write %q contents: %v", filename, err)
}
if err := f.Sync(); err != nil {
return err
}
if err := f.Close(); err != nil {
return err
}
if err := change(f.Name()); 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 string) error {
// FileMod.Chmod() is not implemented on Windows, however, os.Chmod() is
if err := os.Chmod(f, perms); err != nil {
return fmt.Errorf("cannot set permissions: %v", err)
}
return nil
})
}
================================================
FILE: file_test.go
================================================
// Copyright 2013 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package utils_test
import (
"fmt"
"io/ioutil"
"os"
"os/user"
"path/filepath"
"github.com/juju/testing"
jc "github.com/juju/testing/checkers"
gc "gopkg.in/check.v1"
"github.com/juju/utils/v4"
)
type fileSuite struct {
testing.IsolationSuite
}
var _ = gc.Suite(&fileSuite{})
func (*fileSuite) TestNormalizePath(c *gc.C) {
home := filepath.FromSlash(c.MkDir())
err := utils.SetHome(home)
c.Assert(err, gc.IsNil)
// TODO (frankban) bug 1324841: improve the isolation of this suite.
currentUser, err := user.Current()
c.Assert(err, gc.IsNil)
for i, test := range []struct {
path string
expected string
err string
}{{
path: filepath.FromSlash("/var/lib/juju"),
expected: filepath.FromSlash("/var/lib/juju"),
}, {
path: "~/foo",
expected: filepath.Join(home, "foo"),
}, {
path: "~/foo//../bar",
expected: filepath.Join(home, "bar"),
}, {
path: "~",
expected: home,
}, {
path: "~" + currentUser.Username,
expected: currentUser.HomeDir,
}, {
path: "~" + currentUser.Username + "/foo",
expected: filepath.Join(currentUser.HomeDir, "foo"),
}, {
path: "~" + currentUser.Username + "/foo//../bar",
expected: filepath.Join(currentUser.HomeDir, "bar"),
}, {
path: filepath.FromSlash("foo~bar/baz"),
expected: filepath.FromSlash("foo~bar/baz"),
}, {
path: "~foobar/path",
err: ".*" + utils.NoSuchUserErrRegexp,
}} {
c.Logf("test %d: %s", i, test.path)
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) TestExpandPath(c *gc.C) {
home := filepath.FromSlash(c.MkDir())
err := utils.SetHome(home)
c.Assert(err, gc.IsNil)
currentUser, err := user.Current()
c.Assert(err, gc.IsNil)
cwd, err := os.Getwd()
c.Assert(err, gc.IsNil)
for i, test := range []struct {
path string
expected string
err string
}{{
path: filepath.FromSlash("/var/lib/juju"),
expected: filepath.FromSlash("/var/lib/juju"),
}, {
path: "~/foo",
expected: filepath.Join(home, "foo"),
}, {
path: "~/foo//../bar",
expected: filepath.Join(home, "bar"),
}, {
path: "~",
expected: home,
}, {
path: "~" + currentUser.Username,
expected: currentUser.HomeDir,
}, {
path: "~" + currentUser.Username + "/foo",
expected: filepath.Join(currentUser.HomeDir, "foo"),
}, {
path: "~" + currentUser.Username + "/foo//../bar",
expected: filepath.Join(currentUser.HomeDir, "bar"),
}, {
path: filepath.FromSlash("foo~bar/baz"),
expected: filepath.Join(cwd, "foo~bar/baz"),
}, {
path: filepath.FromSlash("foo/bar"),
expected: filepath.Join(cwd, "foo", "bar"),
}, {
path: filepath.FromSlash("foo/../bar"),
expected: filepath.Join(cwd, "bar"),
}, {
path: filepath.FromSlash("foo/./bar"),
expected: filepath.Join(cwd, "foo", "bar"),
}, {
path: "~foobar/path",
err: ".*" + utils.NoSuchUserErrRegexp,
}} {
c.Logf("test %d: %s", i, test.path)
actual, err := utils.ExpandPath(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)
c.Check(filepath.IsAbs(actual), jc.IsTrue)
}
}
}
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 string) error {
// FileMod.Chmod() is not implemented on Windows, however, os.Chmod() is
return os.Chmod(f, 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(string) 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(string) 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) TestMoveFile(c *gc.C) {
d := c.MkDir()
dest := filepath.Join(d, "foo")
f1Name := filepath.Join(d, ".foo1")
f2Name := filepath.Join(d, ".foo2")
err := ioutil.WriteFile(f1Name, []byte("macaroni"), 0644)
c.Assert(err, gc.IsNil)
err = ioutil.WriteFile(f2Name, []byte("cheese"), 0644)
c.Assert(err, gc.IsNil)
ok, err := utils.MoveFile(f1Name, dest)
c.Assert(ok, gc.Equals, true)
c.Assert(err, gc.IsNil)
ok, err = utils.MoveFile(f2Name, dest)
c.Assert(ok, gc.Equals, false)
c.Assert(err, gc.NotNil)
contents, err := ioutil.ReadFile(dest)
c.Assert(err, gc.IsNil)
c.Assert(contents, gc.DeepEquals, []byte("macaroni"))
}
================================================
FILE: file_unix.go
================================================
// Copyright 2013 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
//go:build !windows
// +build !windows
package utils
import (
"fmt"
"os"
"os/user"
"strconv"
"strings"
"syscall"
"github.com/juju/errors"
)
func homeDir(userName string) (string, error) {
u, err := user.Lookup(userName)
if err != nil {
return "", errors.NewUserNotFound(err, "no such user")
}
return u.HomeDir, nil
}
// MoveFile atomically moves the source file to the destination, returning
// whether the file was moved successfully. If the destination already exists,
// it returns an error rather than overwrite it.
//
// On unix systems, an error may occur with a successful move, if the source
// file location cannot be unlinked.
func MoveFile(source, destination string) (bool, error) {
err := os.Link(source, destination)
if err != nil {
return false, err
}
err = os.Remove(source)
if err != nil {
return true, err
}
return true, nil
}
// 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)
}
// MakeFileURL returns a file URL if a directory is passed in else it does nothing
func MakeFileURL(in string) string {
if strings.HasPrefix(in, "/") {
return "file://" + in
}
return in
}
// ChownPath sets the uid and gid of path to match that of the user
// specified.
func ChownPath(path, username string) error {
u, err := user.Lookup(username)
if err != nil {
return fmt.Errorf("cannot lookup %q user id: %v", username, err)
}
uid, err := strconv.Atoi(u.Uid)
if err != nil {
return fmt.Errorf("invalid user id %q: %v", u.Uid, err)
}
gid, err := strconv.Atoi(u.Gid)
if err != nil {
return fmt.Errorf("invalid group id %q: %v", u.Gid, err)
}
return os.Chown(path, uid, gid)
}
// IsFileOwner checks to see if the ownership of the file corresponds to
// the same username
func IsFileOwner(path, username string) (bool, error) {
u, err := user.Lookup(username)
if err != nil {
return false, errors.Annotatef(err, "cannot lookup %q user id", username)
}
info, err := os.Stat(path)
if err != nil {
return false, errors.Trace(err)
}
stat, ok := info.Sys().(*syscall.Stat_t)
if !ok {
return false, fmt.Errorf("cannot lookup %q file", path)
}
return (strconv.Itoa(int(stat.Uid)) == u.Uid &&
strconv.Itoa(int(stat.Gid)) == u.Gid), nil
}
================================================
FILE: file_unix_test.go
================================================
// Copyright 2015 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
//go:build !windows
// +build !windows
package utils_test
import (
"fmt"
"os"
"path/filepath"
"time"
gc "gopkg.in/check.v1"
"github.com/juju/errors"
"github.com/juju/utils/v4"
)
type unixFileSuite struct {
}
var _ = gc.Suite(&unixFileSuite{})
func (s *unixFileSuite) TestEnsureBaseDir(c *gc.C) {
c.Assert(utils.EnsureBaseDir(`/a`, `/b/c`), gc.Equals, `/a/b/c`)
c.Assert(utils.EnsureBaseDir(`/`, `/b/c`), gc.Equals, `/b/c`)
c.Assert(utils.EnsureBaseDir(``, `/b/c`), gc.Equals, `/b/c`)
}
func (s *unixFileSuite) TestFileOwner(c *gc.C) {
username, err := utils.LocalUsername()
c.Assert(err, gc.IsNil)
path := filepath.Join(os.TempDir(), fmt.Sprintf("file-%d", time.Now().UnixNano()))
_, err = os.Create(path)
c.Assert(err, gc.IsNil)
ok, err := utils.IsFileOwner(path, username)
c.Assert(err, gc.IsNil)
c.Assert(ok, gc.Equals, true)
}
func (s *unixFileSuite) TestFileOwnerUsingRoot(c *gc.C) {
path := filepath.Join(os.TempDir(), fmt.Sprintf("file-%d", time.Now().UnixNano()))
_, err := os.Create(path)
c.Assert(err, gc.IsNil)
ok, err := utils.IsFileOwner(path, "root")
c.Assert(err, gc.IsNil)
c.Assert(ok, gc.Equals, false)
}
func (s *unixFileSuite) TestFileOwnerWithInvalidPath(c *gc.C) {
username, err := utils.LocalUsername()
c.Assert(err, gc.IsNil)
path := filepath.Join(os.TempDir(), "file-bad")
ok, err := utils.IsFileOwner(path, username)
c.Assert(errors.Cause(err), gc.ErrorMatches, "stat .*: no such file or directory")
c.Assert(ok, gc.Equals, false)
}
func (s *unixFileSuite) TestFileOwnerWithInvalidUsername(c *gc.C) {
path := filepath.Join(os.TempDir(), fmt.Sprintf("file-%d", time.Now().UnixNano()))
_, err := os.Create(path)
c.Assert(err, gc.IsNil)
ok, err := utils.IsFileOwner(path, "invalid")
c.Assert(errors.Cause(err), gc.ErrorMatches, "user: unknown user invalid")
c.Assert(ok, gc.Equals, false)
}
================================================
FILE: file_windows.go
================================================
// Copyright 2013 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
//go:build windows
// +build windows
package utils
import (
"fmt"
"os"
"path/filepath"
"syscall"
"unsafe"
"github.com/juju/errors"
)
const (
movefile_replace_existing = 0x1
movefile_write_through = 0x8
)
//sys moveFileEx(lpExistingFileName *uint16, lpNewFileName *uint16, dwFlags uint32) (err error) = MoveFileExW
// MoveFile atomically moves the source file to the destination, returning
// whether the file was moved successfully. If the destination already exists,
// it returns an error rather than overwrite it.
func MoveFile(source, destination string) (bool, error) {
src, err := syscall.UTF16PtrFromString(source)
if err != nil {
return false, &os.LinkError{"move", source, destination, err}
}
dest, err := syscall.UTF16PtrFromString(destination)
if err != nil {
return false, &os.LinkError{"move", source, destination, err}
}
// see http://msdn.microsoft.com/en-us/library/windows/desktop/aa365240(v=vs.85).aspx
if err := moveFileEx(src, dest, movefile_write_through); err != nil {
return false, &os.LinkError{"move", source, destination, err}
}
return true, nil
}
// 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
}
// MakeFileURL returns a proper file URL for the given path/directory
func MakeFileURL(in string) string {
in = filepath.ToSlash(in)
// for windows at least should be <letter>: to be considered valid
// so we cant do anything with less than that.
if len(in) < 2 {
return in
}
if string(in[1]) != ":" {
return in
}
// since go 1.6 http client will only take this format.
return "file://" + in
}
func getUserSID(username string) (string, error) {
sid, _, _, e := syscall.LookupSID("", username)
if e != nil {
return "", e
}
sidStr, err := sid.String()
return sidStr, err
}
func readRegString(h syscall.Handle, key string) (value string, err error) {
var typ uint32
var buf uint32
// Get size of registry key
err = syscall.RegQueryValueEx(h, syscall.StringToUTF16Ptr(key), nil, &typ, nil, &buf)
if err != nil {
return value, err
}
n := make([]uint16, buf/2+1)
err = syscall.RegQueryValueEx(h, syscall.StringToUTF16Ptr(key), nil, &typ, (*byte)(unsafe.Pointer(&n[0])), &buf)
if err != nil {
return value, err
}
return syscall.UTF16ToString(n[:]), err
}
func homeFromRegistry(sid string) (string, error) {
var h syscall.Handle
// This key will exist on all platforms we support the agent on (windows server 2008 and above)
keyPath := fmt.Sprintf("Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\%s", sid)
err := syscall.RegOpenKeyEx(syscall.HKEY_LOCAL_MACHINE,
syscall.StringToUTF16Ptr(keyPath),
0, syscall.KEY_READ, &h)
if err != nil {
return "", err
}
defer syscall.RegCloseKey(h)
str, err := readRegString(h, "ProfileImagePath")
if err != nil {
return "", err
}
return str, nil
}
// homeDir returns a local user home dir on Windows
// user.Lookup() does not populate Gid and HomeDir on Windows,
// so we get it from the registry
func homeDir(user string) (string, error) {
u, err := getUserSID(user)
if err != nil {
return "", errors.NewUserNotFound(err, "no such user")
}
return homeFromRegistry(u)
}
// ChownPath is not implemented for Windows.
func ChownPath(path, username string) error {
// This only exists to allow building on Windows. User lookup and
// file ownership needs to be handled in a completely different
// way and hasn't yet been implemented.
return nil
}
// IsFileOwner is not implemented for Windows.
func IsFileOwner(path, username string) (bool, error) {
return true, nil
}
================================================
FILE: file_windows_test.go
================================================
// Copyright 2013 Canonical Ltd.
// Copyright 2014 Cloudbase Solutions SRL
// Licensed under the LGPLv3, see LICENCE file for details.
//go:build windows
// +build windows
package utils_test
import (
gc "gopkg.in/check.v1"
"github.com/juju/utils/v4"
)
type windowsFileSuite struct {
}
var _ = gc.Suite(&windowsFileSuite{})
func (s *windowsFileSuite) TestMakeFileURL(c *gc.C) {
var makeFileURLTests = []struct {
in string
expected string
}{{
in: "file://C:\\foo\\baz",
expected: "file://C:/foo/baz",
}, {
in: "C:\\foo\\baz",
expected: "file://C:/foo/baz",
}, {
in: "http://foo/baz",
expected: "http://foo/baz",
}, {
in: "file://C:/foo/baz",
expected: "file://C:/foo/baz",
}}
for i, t := range makeFileURLTests {
c.Logf("Test %d", i)
c.Assert(utils.MakeFileURL(t.in), gc.Equals, t.expected)
}
}
func (s *windowsFileSuite) TestEnsureBaseDir(c *gc.C) {
c.Assert(utils.EnsureBaseDir(`C:\r`, `C:\a\b`), gc.Equals, `C:\r\a\b`)
c.Assert(utils.EnsureBaseDir(`C:\r`, `D:\a\b`), gc.Equals, `C:\r\a\b`)
c.Assert(utils.EnsureBaseDir(`C:`, `D:\a\b`), gc.Equals, `C:\a\b`)
c.Assert(utils.EnsureBaseDir(`C:`, `\a\b`), gc.Equals, `C:\a\b`)
c.Assert(utils.EnsureBaseDir(``, `C:\a\b`), gc.Equals, `C:\a\b`)
}
func (s *windowsFileSuite) TestFileOwner(c *gc.C) {
c.Assert(utils.IsFileOwner("file://C:\\foo\\baz", "timmy"), gc.Equals, true)
}
================================================
FILE: filepath/common.go
================================================
// Copyright 2015 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package filepath
func splitSuffix(path string) (string, string) {
for i := len(path) - 1; i >= 0; i-- {
if path[i] == '.' && i > 0 {
return path[:i], path[i:]
}
}
return path, ""
}
================================================
FILE: filepath/common_test.go
================================================
// Copyright 2015 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package filepath_test
import (
"github.com/juju/testing"
gc "gopkg.in/check.v1"
"github.com/juju/utils/v4/filepath"
)
var _ = gc.Suite(&commonSuite{})
type commonSuite struct {
testing.IsolationSuite
}
func (s commonSuite) TestSplitSuffixHasSuffix(c *gc.C) {
path, suffix := filepath.SplitSuffix("spam.ext")
c.Check(path, gc.Equals, "spam")
c.Check(suffix, gc.Equals, ".ext")
}
func (s commonSuite) TestSplitSuffixNoSuffix(c *gc.C) {
path, suffix := filepath.SplitSuffix("spam")
c.Check(path, gc.Equals, "spam")
c.Check(suffix, gc.Equals, "")
}
func (s commonSuite) TestSplitSuffixEmpty(c *gc.C) {
path, suffix := filepath.SplitSuffix("")
c.Check(path, gc.Equals, "")
c.Check(suffix, gc.Equals, "")
}
func (s commonSuite) TestSplitSuffixDotFilePlain(c *gc.C) {
path, suffix := filepath.SplitSuffix(".spam")
c.Check(path, gc.Equals, ".spam")
c.Check(suffix, gc.Equals, "")
}
func (s commonSuite) TestSplitSuffixDofileWithSuffix(c *gc.C) {
path, suffix := filepath.SplitSuffix(".spam.ext")
c.Check(path, gc.Equals, ".spam")
c.Check(suffix, gc.Equals, ".ext")
}
================================================
FILE: filepath/export_test.go
================================================
// Copyright 2015 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package filepath
var (
SplitSuffix = splitSuffix
)
================================================
FILE: filepath/filepath.go
================================================
// Copyright 2015 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package filepath
import (
"runtime"
"strings"
"github.com/juju/errors"
"github.com/juju/utils/v4"
)
// Renderer provides methods for the different functions in
// the stdlib path/filepath package that don't relate to a concrete
// filesystem. So Abs, EvalSymlinks, Glob, Rel, and Walk are not
// included. Also, while the functions in path/filepath relate to the
// current host, the PathRenderer methods relate to the renderer's
// target platform. So for example, a windows-oriented implementation
// will give windows-specific results even when used on linux.
type Renderer interface {
// Base mimics path/filepath.
Base(path string) string
// Clean mimics path/filepath.
Clean(path string) string
// Dir mimics path/filepath.
Dir(path string) string
// Ext mimics path/filepath.
Ext(path string) string
// FromSlash mimics path/filepath.
FromSlash(path string) string
// IsAbs mimics path/filepath.
IsAbs(path string) bool
// Join mimics path/filepath.
Join(path ...string) string
// Match mimics path/filepath.
Match(pattern, name string) (matched bool, err error)
// NormCase normalizes the case of a pathname. On Unix and Mac OS X,
// this returns the path unchanged; on case-insensitive filesystems,
// it converts the path to lowercase.
NormCase(path string) string
// Split mimics path/filepath.
Split(path string) (dir, file string)
// SplitList mimics path/filepath.
SplitList(path string) []string
// SplitSuffix splits the pathname into a pair (root, suffix) such
// that root + suffix == path, and ext is empty or begins with a
// period and contains at most one period. Leading periods on the
// basename are ignored; SplitSuffix('.cshrc') returns ('.cshrc', '').
SplitSuffix(path string) (string, string)
// ToSlash mimics path/filepath.
ToSlash(path string) string
// VolumeName mimics path/filepath.
VolumeName(path string) string
}
// NewRenderer returns a Renderer for the given os.
func NewRenderer(os string) (Renderer, error) {
if os == "" {
os = runtime.GOOS
}
os = strings.ToLower(os)
switch {
case os == utils.OSWindows:
return &WindowsRenderer{}, nil
case utils.OSIsUnix(os):
return &UnixRenderer{}, nil
case os == "ubuntu":
return &UnixRenderer{}, nil
default:
return nil, errors.NotFoundf("renderer for %q", os)
}
}
================================================
FILE: filepath/filepath_test.go
================================================
// Copyright 2015 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package filepath_test
import (
"runtime"
"github.com/juju/errors"
"github.com/juju/testing"
jc "github.com/juju/testing/checkers"
"github.com/juju/utils/v4"
gc "gopkg.in/check.v1"
"github.com/juju/utils/v4/filepath"
)
type filepathSuite struct {
testing.IsolationSuite
unix *filepath.UnixRenderer
windows *filepath.WindowsRenderer
}
var _ = gc.Suite(&filepathSuite{})
func (s *filepathSuite) SetupTest(c *gc.C) {
s.IsolationSuite.SetUpTest(c)
s.unix = &filepath.UnixRenderer{}
s.windows = &filepath.WindowsRenderer{}
}
func (s filepathSuite) checkRenderer(c *gc.C, renderer filepath.Renderer, expected string) {
switch expected {
case "windows":
c.Check(renderer, gc.FitsTypeOf, s.windows)
case "unix":
c.Check(renderer, gc.FitsTypeOf, s.unix)
default:
c.Errorf("unknown kind %q", expected)
}
}
func (s filepathSuite) TestNewRendererDefault(c *gc.C) {
// All possible values of runtime.GOOS should be supported.
renderer, err := filepath.NewRenderer("")
c.Assert(err, jc.ErrorIsNil)
switch runtime.GOOS {
case "windows":
s.checkRenderer(c, renderer, "windows")
default:
s.checkRenderer(c, renderer, "unix")
}
}
func (s filepathSuite) TestNewRendererGOOS(c *gc.C) {
// All possible values of runtime.GOOS should be supported.
renderer, err := filepath.NewRenderer(runtime.GOOS)
c.Assert(err, jc.ErrorIsNil)
switch runtime.GOOS {
case "windows":
s.checkRenderer(c, renderer, "windows")
default:
s.checkRenderer(c, renderer, "unix")
}
}
func (s filepathSuite) TestNewRendererWindows(c *gc.C) {
renderer, err := filepath.NewRenderer("windows")
c.Assert(err, jc.ErrorIsNil)
s.checkRenderer(c, renderer, "windows")
}
func (s filepathSuite) TestNewRendererUnix(c *gc.C) {
for _, os := range utils.OSUnix {
c.Logf("trying %q", os)
renderer, err := filepath.NewRenderer(os)
c.Assert(err, jc.ErrorIsNil)
s.checkRenderer(c, renderer, "unix")
}
}
func (s filepathSuite) TestNewRendererDistros(c *gc.C) {
distros := []string{"ubuntu"}
for _, distro := range distros {
c.Logf("trying %q", distro)
renderer, err := filepath.NewRenderer(distro)
c.Assert(err, jc.ErrorIsNil)
s.checkRenderer(c, renderer, "unix")
}
}
func (s filepathSuite) TestNewRendererUnknown(c *gc.C) {
_, err := filepath.NewRenderer("<unknown OS>")
c.Check(err, jc.Satisfies, errors.IsNotFound)
}
================================================
FILE: filepath/interface_test.go
================================================
// Copyright 2015 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package filepath
var _ Renderer = (*UnixRenderer)(nil)
var _ Renderer = (*WindowsRenderer)(nil)
================================================
FILE: filepath/package_test.go
================================================
// Copyright 2015 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package filepath_test
import (
"testing"
gc "gopkg.in/check.v1"
)
func Test(t *testing.T) {
gc.TestingT(t)
}
================================================
FILE: filepath/stdlib.go
================================================
// Copyright 2015 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
// 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.golang file.
package filepath
import (
"strings"
)
// The following functions are adapted from the GO stdlib source.
// Base mimics path/filepath for the given path separator.
func Base(sep uint8, volumeName func(string) string, path string) string {
if path == "" {
return "."
}
// Strip trailing slashes.
for len(path) > 0 && path[len(path)-1] == sep {
path = path[0 : len(path)-1]
}
// Throw away volume name
path = path[len(volumeName(path)):]
// Find the last element
i := len(path) - 1
for i >= 0 && path[i] != sep {
i--
}
if i >= 0 {
path = path[i+1:]
}
// If empty now, it had only slashes.
if path == "" {
return string(sep)
}
return path
}
// A lazybuf is a lazily constructed path buffer.
// It supports append, reading previously appended bytes,
// and retrieving the final string. It does not allocate a buffer
// to hold the output until that output diverges from s.
type lazybuf struct {
path string
buf []byte
w int
volAndPath string
volLen int
}
func (b *lazybuf) index(i int) byte {
if b.buf != nil {
return b.buf[i]
}
return b.path[i]
}
func (b *lazybuf) append(c byte) {
if b.buf == nil {
if b.w < len(b.path) && b.path[b.w] == c {
b.w++
return
}
b.buf = make([]byte, len(b.path))
copy(b.buf, b.path[:b.w])
}
b.buf[b.w] = c
b.w++
}
func (b *lazybuf) string() string {
if b.buf == nil {
return b.volAndPath[:b.volLen+b.w]
}
return b.volAndPath[:b.volLen] + string(b.buf[:b.w])
}
// Clean mimics path/filepath for the given path separator.
func Clean(sep uint8, volumeName func(string) string, path string) string {
originalPath := path
volLen := len(volumeName(path))
path = path[volLen:]
if path == "" {
if volLen > 1 && originalPath[1] != ':' {
// should be UNC
return FromSlash(sep, originalPath)
}
return originalPath + "."
}
rooted := (path[0] == sep)
// Invariants:
// reading from path; r is index of next byte to process.
// writing to buf; w is index of next byte to write.
// dotdot is index in buf where .. must stop, either because
// it is the leading slash or it is a leading ../../.. prefix.
n := len(path)
out := lazybuf{path: path, volAndPath: originalPath, volLen: volLen}
r, dotdot := 0, 0
if rooted {
out.append(sep)
r, dotdot = 1, 1
}
for r < n {
switch {
case path[r] == sep:
// empty path element
r++
case path[r] == '.' && (r+1 == n || path[r+1] == sep):
// . element
r++
case path[r] == '.' && path[r+1] == '.' && (r+2 == n || path[r+2] == sep):
// .. element: remove to last separator
r += 2
switch {
case out.w > dotdot:
// can backtrack
out.w--
for out.w > dotdot && out.index(out.w) != sep {
out.w--
}
case !rooted:
// cannot backtrack, but not rooted, so append .. element.
if out.w > 0 {
out.append(sep)
}
out.append('.')
out.append('.')
dotdot = out.w
}
default:
// real path element.
// add slash if needed
if rooted && out.w != 1 || !rooted && out.w != 0 {
out.append(sep)
}
// copy element
for ; r < n && path[r] != sep; r++ {
out.append(path[r])
}
}
}
// Turn empty string into "."
if out.w == 0 {
out.append('.')
}
return FromSlash(sep, out.string())
}
// Dir mimics path/filepath for the given path separator.
func Dir(sep uint8, volumeName func(string) string, path string) string {
vol := volumeName(path)
i := len(path) - 1
for i >= len(vol) && path[i] != sep {
i--
}
dir := Clean(sep, volumeName, path[len(vol):i+1])
return vol + dir
}
// Ext mimics path/filepath for the given path separator.
func Ext(sep uint8, path string) string {
for i := len(path) - 1; i >= 0 && path[i] != sep; i-- {
if path[i] == '.' {
return path[i:]
}
}
return ""
}
// FromSlash mimics path/filepath for the given path separator.
func FromSlash(sep uint8, path string) string {
if sep == '/' {
return path
}
return strings.Replace(path, "/", string(sep), -1)
}
// Join mimics path/filepath for the given path separator.
func Join(sep uint8, volumeName func(string) string, elem ...string) string {
for i, e := range elem {
if e != "" {
return Clean(sep, volumeName, strings.Join(elem[i:], string(sep)))
}
}
return ""
}
// Split mimics path/filepath for the given path separator.
func Split(sep uint8, volumeName func(string) string, path string) (dir, file string) {
vol := volumeName(path)
i := len(path) - 1
for i >= len(vol) && path[i] != sep {
i--
}
return path[:i+1], path[i+1:]
}
// ToSlash mimics path/filepath for the given path separator.
func ToSlash(sep uint8, path string) string {
if sep == '/' {
return path
}
return strings.Replace(path, string(sep), "/", -1)
}
================================================
FILE: filepath/stdlib_test.go
================================================
// Copyright 2015 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package filepath_test
import (
gofilepath "path/filepath"
"runtime"
"strings"
"github.com/juju/testing"
jc "github.com/juju/testing/checkers"
gc "gopkg.in/check.v1"
"github.com/juju/utils/v4/filepath"
)
// The tests here are mostly just sanity checks against the behavior
// of the stdlib path/filepath. We are not trying for high coverage levels.
type stdlibSuite struct {
testing.IsolationSuite
path string
volumeName func(string) string
}
var _ = gc.Suite(&stdlibSuite{})
func (s *stdlibSuite) SetUpTest(c *gc.C) {
s.IsolationSuite.SetUpTest(c)
switch runtime.GOOS {
case "windows":
s.path = `C:\a\b\c.xyz`
s.volumeName = func(path string) string {
return "C:"
}
default:
s.path = "/a/b/c.xyz"
s.volumeName = func(string) string { return "" }
}
}
func (s stdlibSuite) TestBase(c *gc.C) {
path := filepath.Base(gofilepath.Separator, s.volumeName, s.path)
gopath := gofilepath.Base(s.path)
c.Check(path, gc.Equals, gopath)
c.Check(path, gc.Equals, "c.xyz")
}
func (s stdlibSuite) TestClean(c *gc.C) {
// TODO(ericsnow) Add more cases.
originals := map[string]string{
s.path: s.path,
}
for original, expected := range originals {
c.Logf("checking %q", original)
path := filepath.Clean(gofilepath.Separator, s.volumeName, original)
gopath := gofilepath.Clean(original)
c.Check(path, gc.Equals, gopath)
c.Check(path, gc.Equals, expected)
}
}
func (s stdlibSuite) TestDir(c *gc.C) {
path := filepath.Dir(gofilepath.Separator, s.volumeName, s.path)
gopath := gofilepath.Dir(s.path)
c.Check(path, gc.Equals, gopath)
switch runtime.GOOS {
case "windows":
c.Check(path, gc.Equals, `\a\b`)
default:
c.Check(path, gc.Equals, "/a/b")
}
}
func (s stdlibSuite) TestExt(c *gc.C) {
ext := filepath.Ext(gofilepath.Separator, s.path)
goext := gofilepath.Ext(s.path)
c.Check(ext, gc.Equals, goext)
c.Check(ext, gc.Equals, ".xyz")
}
func (s stdlibSuite) TestFromSlash(c *gc.C) {
original := "/a/b/c.xyz"
path := filepath.FromSlash(gofilepath.Separator, original)
gopath := gofilepath.FromSlash(original)
c.Check(path, gc.Equals, gopath)
c.Check(path, gc.Equals, s.path)
}
func (s stdlibSuite) TestJoin(c *gc.C) {
path := filepath.Join(gofilepath.Separator, s.volumeName, "a", "b", "c.xyz")
gopath := gofilepath.Join("a", "b", "c.xyz")
c.Check(path, gc.Equals, gopath)
expected := s.path[strings.Index(s.path, string(gofilepath.Separator))+1:]
c.Check(path, gc.Equals, expected)
}
func (s stdlibSuite) TestSplit(c *gc.C) {
dir, base := filepath.Split(gofilepath.Separator, s.volumeName, s.path)
godir, gobase := gofilepath.Split(s.path)
c.Check(dir, gc.Equals, godir)
c.Check(base, gc.Equals, gobase)
switch runtime.GOOS {
case "windows":
c.Check(dir, gc.Equals, `\a\b\`)
default:
c.Check(dir, gc.Equals, "/a/b/")
}
c.Check(base, gc.Equals, "c.xyz")
}
func (s stdlibSuite) TestToSlash(c *gc.C) {
path := filepath.ToSlash(gofilepath.Separator, s.path)
gopath := gofilepath.ToSlash(s.path)
c.Check(path, gc.Equals, gopath)
c.Check(path, gc.Equals, "/a/b/c.xyz")
}
func (s stdlibSuite) TestMatchTrue(c *gc.C) {
tests := map[string]string{
"abc": "abc",
"ab[c]": "abc",
"": "",
"*": "abc",
"a*c": "abc",
"?": "a",
"a?c": "abc",
}
for pattern, name := range tests {
c.Logf("- checking pattern %q against %q -", pattern, name)
matched, err := filepath.Match(gofilepath.Separator, pattern, name)
c.Assert(err, jc.ErrorIsNil)
gomatched, err := gofilepath.Match(pattern, name)
c.Assert(err, jc.ErrorIsNil)
c.Check(matched, gc.Equals, gomatched)
c.Check(matched, jc.IsTrue)
}
}
func (s stdlibSuite) TestMatchFalse(c *gc.C) {
tests := map[string]string{
"abc": "xyz",
"": "abc",
"a*c": "a",
"?": "",
"a?c": "ac",
}
for pattern, name := range tests {
c.Logf("- checking pattern %q against %q -", pattern, name)
matched, err := filepath.Match(gofilepath.Separator, pattern, name)
c.Assert(err, jc.ErrorIsNil)
gomatched, err := gofilepath.Match(pattern, name)
c.Assert(err, jc.ErrorIsNil)
c.Check(matched, gc.Equals, gomatched)
c.Check(matched, jc.IsFalse)
}
}
func (s stdlibSuite) TestMatchBadPattern(c *gc.C) {
tests := map[string]string{
"ab[": "abc",
"ab[-c]": "abc",
"ab[]": "abc",
}
for pattern, name := range tests {
c.Logf("- checking pattern %q against %q -", pattern, name)
_, err := filepath.Match(gofilepath.Separator, pattern, name)
_, goerr := gofilepath.Match(pattern, name)
c.Check(err, gc.Equals, goerr)
c.Check(err, gc.Equals, gofilepath.ErrBadPattern)
}
}
================================================
FILE: filepath/stdlibmatch.go
================================================
// Copyright 2015 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
// 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.golang file.
package filepath
import (
"path/filepath"
"strings"
"unicode/utf8"
)
// The following functions are adapted from the GO stdlib source.
// Match returns true if name matches the shell file name pattern.
// The pattern syntax is:
//
// pattern:
// { term }
// term:
// '*' matches any sequence of non-Separator characters
// '?' matches any single non-Separator character
// '[' [ '^' ] { character-range } ']'
// character class (must be non-empty)
// c matches character c (c != '*', '?', '\\', '[')
// '\\' c matches character c
//
// character-range:
// c matches character c (c != '\\', '-', ']')
// '\\' c matches character c
// lo '-' hi matches character c for lo <= c <= hi
//
// Match requires pattern to match all of name, not just a substring.
// The only possible returned error is ErrBadPattern, when pattern
// is malformed.
//
// On Windows, escaping is disabled. Instead, '\\' is treated as
// path separator.
//
func Match(sep uint8, pattern, name string) (matched bool, err error) {
Pattern:
for len(pattern) > 0 {
var star bool
var chunk string
star, chunk, pattern = scanChunk(sep, pattern)
if star && chunk == "" {
// Trailing * matches rest of string unless it has a /.
return strings.Index(name, string(sep)) < 0, nil
}
// Look for match at current position.
t, ok, err := matchChunk(sep, chunk, name)
// if we're the last chunk, make sure we've exhausted the name
// otherwise we'll give a false result even if we could still match
// using the star
if ok && (len(t) == 0 || len(pattern) > 0) {
name = t
continue
}
if err != nil {
return false, err
}
if star {
// Look for match skipping i+1 bytes.
// Cannot skip /.
for i := 0; i < len(name) && name[i] != sep; i++ {
t, ok, err := matchChunk(sep, chunk, name[i+1:])
if ok {
// if we're the last chunk, make sure we exhausted the name
if len(pattern) == 0 && len(t) > 0 {
continue
}
name = t
continue Pattern
}
if err != nil {
return false, err
}
}
}
return false, nil
}
return len(name) == 0, nil
}
// scanChunk gets the next segment of pattern, which is a non-star string
// possibly preceded by a star.
func scanChunk(sep uint8, pattern string) (star bool, chunk, rest string) {
for len(pattern) > 0 && pattern[0] == '*' {
pattern = pattern[1:]
star = true
}
inrange := false
var i int
Scan:
for i = 0; i < len(pattern); i++ {
switch pattern[i] {
case '\\':
if sep == '\\' {
// error check handled in matchChunk: bad pattern.
if i+1 < len(pattern) {
i++
}
}
case '[':
inrange = true
case ']':
inrange = false
case '*':
if !inrange {
break Scan
}
}
}
return star, pattern[0:i], pattern[i:]
}
// matchChunk checks whether chunk matches the beginning of s.
// If so, it returns the remainder of s (after the match).
// Chunk is all single-character operators: literals, char classes, and ?.
func matchChunk(sep uint8, chunk, s string) (rest string, ok bool, err error) {
for len(chunk) > 0 {
if len(s) == 0 {
return
}
switch chunk[0] {
case '[':
// character class
r, n := utf8.DecodeRuneInString(s)
s = s[n:]
chunk = chunk[1:]
// We can't end right after '[', we're expecting at least
// a closing bracket and possibly a caret.
if len(chunk) == 0 {
err = filepath.ErrBadPattern
return
}
// possibly negated
negated := chunk[0] == '^'
if negated {
chunk = chunk[1:]
}
// parse all ranges
match := false
nrange := 0
for {
if len(chunk) > 0 && chunk[0] == ']' && nrange > 0 {
chunk = chunk[1:]
break
}
var lo, hi rune
if lo, chunk, err = getEsc(sep, chunk); err != nil {
return
}
hi = lo
if chunk[0] == '-' {
if hi, chunk, err = getEsc(sep, chunk[1:]); err != nil {
return
}
}
if lo <= r && r <= hi {
match = true
}
nrange++
}
if match == negated {
return
}
case '?':
if s[0] == sep {
return
}
_, n := utf8.DecodeRuneInString(s)
s = s[n:]
chunk = chunk[1:]
case '\\':
if sep != '\\' {
chunk = chunk[1:]
if len(chunk) == 0 {
err = filepath.ErrBadPattern
return
}
}
fallthrough
default:
if chunk[0] != s[0] {
return
}
s = s[1:]
chunk = chunk[1:]
}
}
return s, true, nil
}
// getEsc gets a possibly-escaped character from chunk, for a character class.
func getEsc(sep uint8, chunk string) (r rune, nchunk string, err error) {
if len(chunk) == 0 || chunk[0] == '-' || chunk[0] == ']' {
err = filepath.ErrBadPattern
return
}
if chunk[0] == '\\' && sep != '\\' {
chunk = chunk[1:]
if len(chunk) == 0 {
err = filepath.ErrBadPattern
return
}
}
r, n := utf8.DecodeRuneInString(chunk)
if r == utf8.RuneError && n == 1 {
err = filepath.ErrBadPattern
}
nchunk = chunk[n:]
if len(nchunk) == 0 {
err = filepath.ErrBadPattern
}
return
}
================================================
FILE: filepath/unix.go
================================================
// Copyright 2015 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package filepath
import (
"strings"
)
// A substantial portion of this code comes from the Go stdlib code.
const (
UnixSeparator = '/' // OS-specific path separator
UnixListSeparator = ':' // OS-specific path list separator
)
// UnixRenderer is a Renderer implementation for most flavors of Unix.
type UnixRenderer struct{}
// Base implements Renderer.
func (ur UnixRenderer) Base(path string) string {
return Base(UnixSeparator, ur.VolumeName, path)
}
// Clean implements Renderer.
func (ur UnixRenderer) Clean(path string) string {
return Clean(UnixSeparator, ur.VolumeName, path)
}
// Dir implements Renderer.
func (ur UnixRenderer) Dir(path string) string {
return Dir(UnixSeparator, ur.VolumeName, path)
}
// Ext implements Renderer.
func (UnixRenderer) Ext(path string) string {
return Ext(UnixSeparator, path)
}
// FromSlash implements Renderer.
func (UnixRenderer) FromSlash(path string) string {
return FromSlash(UnixSeparator, path)
}
// IsAbs implements Renderer.
func (UnixRenderer) IsAbs(path string) bool {
return strings.HasPrefix(path, string(UnixSeparator))
}
// Join implements Renderer.
func (ur UnixRenderer) Join(path ...string) string {
return Join(UnixSeparator, ur.VolumeName, path...)
}
// Match implements Renderer.
func (UnixRenderer) Match(pattern, name string) (matched bool, err error) {
return Match(UnixSeparator, pattern, name)
}
// Split implements Renderer.
func (ur UnixRenderer) Split(path string) (dir, file string) {
return Split(UnixSeparator, ur.VolumeName, path)
}
// SplitList implements Renderer.
func (UnixRenderer) SplitList(path string) []string {
if path == "" {
return []string{}
}
return strings.Split(path, string(UnixListSeparator))
}
// ToSlash implements Renderer.
func (UnixRenderer) ToSlash(path string) string {
return ToSlash(UnixSeparator, path)
}
// VolumeName implements Renderer.
func (UnixRenderer) VolumeName(path string) string {
return ""
}
// NormCase implements Renderer.
func (UnixRenderer) NormCase(path string) string {
return path
}
// SplitSuffix implements Renderer.
func (UnixRenderer) SplitSuffix(path string) (string, string) {
return splitSuffix(path)
}
================================================
FILE: filepath/unix_test.go
================================================
// Copyright 2015 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package filepath_test
import (
gofilepath "path/filepath"
"github.com/juju/testing"
jc "github.com/juju/testing/checkers"
gc "gopkg.in/check.v1"
"github.com/juju/utils/v4/filepath"
)
var _ = gc.Suite(&unixSuite{})
var _ = gc.Suite(&unixThinWrapperSuite{})
type unixBaseSuite struct {
testing.IsolationSuite
path string
renderer *filepath.UnixRenderer
}
func (s *unixBaseSuite) SetUpTest(c *gc.C) {
s.IsolationSuite.SetUpTest(c)
s.path = "/a/b/c.xyz"
s.renderer = &filepath.UnixRenderer{}
}
func (s *unixBaseSuite) matchesRuntime() bool {
return gofilepath.Separator == filepath.UnixSeparator
}
type unixSuite struct {
unixBaseSuite
}
func (s unixSuite) TestIsAbs(c *gc.C) {
isAbs := s.renderer.IsAbs(s.path)
c.Check(isAbs, jc.IsTrue)
if s.matchesRuntime() {
c.Check(isAbs, gc.Equals, gofilepath.IsAbs(s.path))
}
}
func (s unixSuite) TestSplitList(c *gc.C) {
list := s.renderer.SplitList("/a:b:/c/d")
c.Check(list, jc.DeepEquals, []string{"/a", "b", "/c/d"})
if s.matchesRuntime() {
golist := gofilepath.SplitList("/a:b:/c/d")
c.Check(list, jc.DeepEquals, golist)
}
}
func (s unixSuite) TestVolumeName(c *gc.C) {
volumeName := s.renderer.VolumeName(s.path)
c.Check(volumeName, gc.Equals, "")
}
func (s unixSuite) TestNormCaseLower(c *gc.C) {
normalized := s.renderer.NormCase("spam")
c.Check(normalized, gc.Equals, "spam")
}
func (s unixSuite) TestNormCaseUpper(c *gc.C) {
normalized := s.renderer.NormCase("SPAM")
c.Check(normalized, gc.Equals, "SPAM")
}
func (s unixSuite) TestNormCaseMixed(c *gc.C) {
normalized := s.renderer.NormCase("sPaM")
c.Check(normalized, gc.Equals, "sPaM")
}
func (s unixSuite) TestNormCaseCapitalized(c *gc.C) {
normalized := s.renderer.NormCase("Spam")
c.Check(normalized, gc.Equals, "Spam")
}
func (s unixSuite) TestNormCasePunctuation(c *gc.C) {
normalized := s.renderer.NormCase("spam-eggs.ext")
c.Check(normalized, gc.Equals, "spam-eggs.ext")
}
func (s unixSuite) TestSplitSuffix(c *gc.C) {
// This is just a sanity check. The splitSuffix tests are more
// comprehensive.
path, suffix := s.renderer.SplitSuffix("spam.ext")
c.Check(path, gc.Equals, "spam")
c.Check(suffix, gc.Equals, ".ext")
}
// unixThinWrapperSuite contains test methods for UnixRenderer methods
// that are just thin wrappers around the corresponding helpers in the
// filepath package. As such the test coverage is minimal (more of a
// sanity check).
type unixThinWrapperSuite struct {
unixBaseSuite
}
func (s unixThinWrapperSuite) TestBase(c *gc.C) {
path := s.renderer.Base(s.path)
c.Check(path, gc.Equals, "c.xyz")
if s.matchesRuntime() {
gopath := gofilepath.Base(s.path)
c.Check(path, gc.Equals, gopath)
}
}
func (s unixThinWrapperSuite) TestClean(c *gc.C) {
// TODO(ericsnow) Add more cases.
originals := map[string]string{
s.path: s.path,
}
for original, expected := range originals {
c.Logf("checking %q", original)
path := s.renderer.Clean(original)
c.Check(path, gc.Equals, expected)
if s.matchesRuntime() {
gopath := gofilepath.Clean(original)
c.Check(path, gc.Equals, gopath)
}
}
}
func (s unixThinWrapperSuite) TestDir(c *gc.C) {
path := s.renderer.Dir(s.path)
c.Check(path, gc.Equals, "/a/b")
if s.matchesRuntime() {
gopath := gofilepath.Dir(s.path)
c.Check(path, gc.Equals, gopath)
}
}
func (s unixThinWrapperSuite) TestExt(c *gc.C) {
ext := s.renderer.Ext(s.path)
c.Check(ext, gc.Equals, ".xyz")
if s.matchesRuntime() {
goext := gofilepath.Ext(s.path)
c.Check(ext, gc.Equals, goext)
}
}
func (s unixThinWrapperSuite) TestFromSlash(c *gc.C) {
original := "/a/b/c.xyz"
path := s.renderer.FromSlash(original)
c.Check(path, gc.Equals, s.path)
if s.matchesRuntime() {
gopath := gofilepath.FromSlash(original)
c.Check(path, gc.Equals, gopath)
}
}
func (s unixThinWrapperSuite) TestJoin(c *gc.C) {
path := s.renderer.Join("a", "b", "c.xyz")
c.Check(path, gc.Equals, s.path[1:])
if s.matchesRuntime() {
gopath := gofilepath.Join("a", "b", "c.xyz")
c.Check(path, gc.Equals, gopath)
}
}
func (s unixThinWrapperSuite) TestSplit(c *gc.C) {
dir, base := s.renderer.Split(s.path)
c.Check(dir, gc.Equals, "/a/b/")
c.Check(base, gc.Equals, "c.xyz")
if s.matchesRuntime() {
godir, gobase := gofilepath.Split(s.path)
c.Check(dir, gc.Equals, godir)
c.Check(base, gc.Equals, gobase)
}
}
func (s unixThinWrapperSuite) TestToSlash(c *gc.C) {
path := s.renderer.ToSlash(s.path)
c.Check(path, gc.Equals, "/a/b/c.xyz")
if s.matchesRuntime() {
gopath := gofilepath.ToSlash(s.path)
c.Check(path, gc.Equals, gopath)
}
}
func (s unixThinWrapperSuite) TestMatchTrue(c *gc.C) {
tests := map[string]string{
"abc": "abc",
"ab[c]": "abc",
"": "",
"*": "abc",
"a*c": "abc",
"?": "a",
"a?c": "abc",
}
for pattern, name := range tests {
c.Logf("- checking pattern %q against %q -", pattern, name)
matched, err := s.renderer.Match(pattern, name)
c.Assert(err, jc.ErrorIsNil)
c.Check(matched, jc.IsTrue)
if s.matchesRuntime() {
gomatched, err := gofilepath.Match(pattern, name)
c.Assert(err, jc.ErrorIsNil)
c.Check(matched, gc.Equals, gomatched)
}
}
}
func (s unixThinWrapperSuite) TestMatchFalse(c *gc.C) {
tests := map[string]string{
"abc": "xyz",
"": "abc",
"a*c": "a",
"?": "",
"a?c": "ac",
}
for pattern, name := range tests {
c.Logf("- checking pattern %q against %q -", pattern, name)
matched, err := s.renderer.Match(pattern, name)
c.Assert(err, jc.ErrorIsNil)
c.Check(matched, jc.IsFalse)
if s.matchesRuntime() {
gomatched, err := gofilepath.Match(pattern, name)
c.Assert(err, jc.ErrorIsNil)
c.Check(matched, gc.Equals, gomatched)
}
}
}
func (s unixThinWrapperSuite) TestMatchBadPattern(c *gc.C) {
tests := map[string]string{
"ab[": "abc",
"ab[-c]": "abc",
"ab[]": "abc",
}
for pattern, name := range tests {
c.Logf("- checking pattern %q against %q -", pattern, name)
_, err := s.renderer.Match(pattern, name)
c.Check(err, gc.Equals, gofilepath.ErrBadPattern)
if s.matchesRuntime() {
_, goerr := gofilepath.Match(pattern, name)
c.Check(err, gc.Equals, goerr)
}
}
}
================================================
FILE: filepath/win.go
================================================
// Copyright 2015 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package filepath
import (
"strings"
)
// A substantial portion of this code comes from the Go stdlib code.
const (
WindowsSeparator = '\\' // OS-specific path separator
WindowsListSeparator = ';' // OS-specific path list separator
)
// WindowsRenderer is a Renderer implementation for Windows.
type WindowsRenderer struct{}
// Base implements Renderer.
func (ur WindowsRenderer) Base(path string) string {
return Base(WindowsSeparator, ur.VolumeName, path)
}
// Clean implements Renderer.
func (ur WindowsRenderer) Clean(path string) string {
return Clean(WindowsSeparator, ur.VolumeName, path)
}
// Dir implements Renderer.
func (ur WindowsRenderer) Dir(path string) string {
return Dir(WindowsSeparator, ur.VolumeName, path)
}
// Ext implements Renderer.
func (WindowsRenderer) Ext(path string) string {
return Ext(WindowsSeparator, path)
}
// FromSlash implements Renderer.
func (WindowsRenderer) FromSlash(path string) string {
return FromSlash(WindowsSeparator, path)
}
// IsAbs implements Renderer.
func (WindowsRenderer) IsAbs(path string) bool {
l := volumeNameLen(path)
if l == 0 {
return false
}
path = path[l:]
if path == "" {
return false
}
return isSlash(path[0])
}
// Join implements Renderer.
func (ur WindowsRenderer) Join(path ...string) string {
return Join(WindowsSeparator, ur.VolumeName, path...)
}
// Match implements Renderer.
func (WindowsRenderer) Match(pattern, name string) (matched bool, err error) {
return Match(WindowsSeparator, pattern, name)
}
// Split implements Renderer.
func (ur WindowsRenderer) Split(path string) (dir, file string) {
return Split(WindowsSeparator, ur.VolumeName, path)
}
// SplitList implements Renderer.
func (WindowsRenderer) SplitList(path string) []string {
if path == "" {
return []string{}
}
// Split path, respecting but preserving quotes.
list := []string{}
start := 0
quo := false
for i := 0; i < len(path); i++ {
switch c := path[i]; {
case c == '"':
quo = !quo
case c == WindowsListSeparator && !quo:
list = append(list, path[start:i])
start = i + 1
}
}
list = append(list, path[start:])
// Remove quotes.
for i, s := range list {
if strings.Contains(s, `"`) {
list[i] = strings.Replace(s, `"`, ``, -1)
}
}
return list
}
// ToSlash implements Renderer.
func (WindowsRenderer) ToSlash(path string) string {
return ToSlash(WindowsSeparator, path)
}
// VolumeName implements Renderer.
func (WindowsRenderer) VolumeName(path string) string {
return path[:volumeNameLen(path)]
}
// NormCase implements Renderer.
func (WindowsRenderer) NormCase(path string) string {
return strings.ToLower(path)
}
// SplitSuffix implements Renderer.
func (WindowsRenderer) SplitSuffix(path string) (string, string) {
return splitSuffix(path)
}
func isSlash(c uint8) bool {
return c == WindowsSeparator || c == '/'
}
// volumeNameLen returns length of the leading volume name on Windows.
// It returns 0 elsewhere.
func volumeNameLen(path string) int {
if len(path) < 2 {
return 0
}
// with drive letter
c := path[0]
if path[1] == ':' && ('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z') {
return 2
}
// is it UNC
if l := len(path); l >= 5 && isSlash(path[0]) && isSlash(path[1]) &&
!isSlash(path[2]) && path[2] != '.' {
// first, leading `\\` and next shouldn't be `\`. its server name.
for n := 3; n < l-1; n++ {
// second, next '\' shouldn't be repeated.
if isSlash(path[n]) {
n++
// third, following something characters. its share name.
if !isSlash(path[n]) {
if path[n] == '.' {
break
}
for ; n < l; n++ {
if isSlash(path[n]) {
break
}
}
return n
}
break
}
}
}
return 0
}
================================================
FILE: filepath/win_test.go
================================================
// Copyright 2015 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package filepath_test
import (
gofilepath "path/filepath"
"github.com/juju/testing"
jc "github.com/juju/testing/checkers"
gc "gopkg.in/check.v1"
"github.com/juju/utils/v4/filepath"
)
var _ = gc.Suite(&windowsSuite{})
var _ = gc.Suite(&windowsThinWrapperSuite{})
type windowsBaseSuite struct {
testing.IsolationSuite
path string
renderer *filepath.WindowsRenderer
}
func (s *windowsBaseSuite) SetUpTest(c *gc.C) {
s.IsolationSuite.SetUpTest(c)
s.path = `c:\a\b\c.xyz`
s.renderer = &filepath.WindowsRenderer{}
}
func (s *windowsBaseSuite) matchesRuntime() bool {
return gofilepath.Separator == filepath.WindowsSeparator
}
type windowsSuite struct {
windowsBaseSuite
}
func (s windowsSuite) TestIsAbs(c *gc.C) {
isAbs := s.renderer.IsAbs(s.path)
c.Check(isAbs, jc.IsTrue)
if s.matchesRuntime() {
c.Check(isAbs, gc.Equals, gofilepath.IsAbs(s.path))
}
}
func (s windowsSuite) TestSplitList(c *gc.C) {
list := s.renderer.SplitList(`\a;b;\c\d`)
c.Check(list, jc.DeepEquals, []string{`\a`, "b", `\c\d`})
if s.matchesRuntime() {
golist := gofilepath.SplitList(`\a;b;\c\d`)
c.Check(list, jc.DeepEquals, golist)
}
}
func (s windowsSuite) TestVolumeName(c *gc.C) {
volumeName := s.renderer.VolumeName(s.path)
c.Check(volumeName, gc.Equals, "c:")
if s.matchesRuntime() {
goresult := gofilepath.VolumeName(s.path)
c.Check(volumeName, gc.Equals, goresult)
}
}
func (s windowsSuite) TestNormCaseLower(c *gc.C) {
normalized := s.renderer.NormCase("spam")
c.Check(normalized, gc.Equals, "spam")
}
func (s windowsSuite) TestNormCaseUpper(c *gc.C) {
normalized := s.renderer.NormCase("SPAM")
c.Check(normalized, gc.Equals, "spam")
}
func (s windowsSuite) TestNormCaseMixed(c *gc.C) {
normalized := s.renderer.NormCase("sPaM")
c.Check(normalized, gc.Equals, "spam")
}
func (s windowsSuite) TestNormCaseCapitalized(c *gc.C) {
normalized := s.renderer.NormCase("Spam")
c.Check(normalized, gc.Equals, "spam")
}
func (s windowsSuite) TestNormCasePunctuation(c *gc.C) {
normalized := s.renderer.NormCase("spam-eggs.ext")
c.Check(normalized, gc.Equals, "spam-eggs.ext")
}
func (s windowsSuite) TestSplitSuffix(c *gc.C) {
// This is just a sanity check. The splitSuffix tests are more
// comprehensive.
path, suffix := s.renderer.SplitSuffix("spam.ext")
c.Check(path, gc.Equals, "spam")
c.Check(suffix, gc.Equals, ".ext")
}
// windowsThinWrapperSuite contains test methods for WindowsRenderer methods
// that are just thin wrappers around the corresponding helpers in the
// filepath package. As such the test coverage is minimal (more of a
// sanity check).
type windowsThinWrapperSuite struct {
windowsBaseSuite
}
func (s windowsThinWrapperSuite) TestBase(c *gc.C) {
path := s.renderer.Base(s.path)
c.Check(path, gc.Equals, "c.xyz")
if s.matchesRuntime() {
gopath := gofilepath.Base(s.path)
c.Check(path, gc.Equals, gopath)
}
}
func (s windowsThinWrapperSuite) TestClean(c *gc.C) {
// TODO(ericsnow) Add more cases.
originals := map[string]string{
s.path: s.path,
}
for original, expected := range originals {
c.Logf("checking %q", original)
path := s.renderer.Clean(original)
c.Check(path, gc.Equals, expected)
if s.matchesRuntime() {
gopath := gofilepath.Clean(original)
c.Check(path, gc.Equals, gopath)
}
}
}
func (s windowsThinWrapperSuite) TestDir(c *gc.C) {
path := s.renderer.Dir(s.path)
c.Check(path, gc.Equals, `c:\a\b`)
if s.matchesRuntime() {
gopath := gofilepath.Dir(s.path)
c.Check(path, gc.Equals, gopath)
}
}
func (s windowsThinWrapperSuite) TestExt(c *gc.C) {
ext := s.renderer.Ext(s.path)
c.Check(ext, gc.Equals, ".xyz")
if s.matchesRuntime() {
goext := gofilepath.Ext(s.path)
c.Check(ext, gc.Equals, goext)
}
}
func (s windowsThinWrapperSuite) TestFromSlash(c *gc.C) {
original := "/a/b/c.xyz"
path := s.renderer.FromSlash(original)
c.Check(path, gc.Equals, s.path[2:])
if s.matchesRuntime() {
gopath := gofilepath.FromSlash(original)
c.Check(path, gc.Equals, gopath)
}
}
func (s windowsThinWrapperSuite) TestJoin(c *gc.C) {
path := s.renderer.Join("a", "b", "c.xyz")
c.Check(path, gc.Equals, s.path[3:])
if s.matchesRuntime() {
gopath := gofilepath.Join("a", "b", "c.xyz")
c.Check(path, gc.Equals, gopath)
}
}
func (s windowsThinWrapperSuite) TestSplit(c *gc.C) {
dir, base := s.renderer.Split(s.path)
c.Check(dir, gc.Equals, `c:\a\b\`)
c.Check(base, gc.Equals, "c.xyz")
if s.matchesRuntime() {
godir, gobase := gofilepath.Split(s.path)
c.Check(dir, gc.Equals, godir)
c.Check(base, gc.Equals, gobase)
}
}
func (s windowsThinWrapperSuite) TestToSlash(c *gc.C) {
path := s.renderer.ToSlash(s.path)
c.Check(path, gc.Equals, "c:/a/b/c.xyz")
if s.matchesRuntime() {
gopath := gofilepath.ToSlash(s.path)
c.Check(path, gc.Equals, gopath)
}
}
func (s windowsThinWrapperSuite) TestMatchTrue(c *gc.C) {
tests := map[string]string{
"abc": "abc",
"ab[c]": "abc",
"": "",
"*": "abc",
"a*c": "abc",
"?": "a",
"a?c": "abc",
}
for pattern, name := range tests {
c.Logf("- checking pattern %q against %q -", pattern, name)
matched, err := s.renderer.Match(pattern, name)
c.Assert(err, jc.ErrorIsNil)
c.Check(matched, jc.IsTrue)
if s.matchesRuntime() {
gomatched, err := gofilepath.Match(pattern, name)
c.Assert(err, jc.ErrorIsNil)
c.Check(matched, gc.Equals, gomatched)
}
}
}
func (s windowsThinWrapperSuite) TestMatchFalse(c *gc.C) {
tests := map[string]string{
"abc": "xyz",
"": "abc",
"a*c": "a",
"?": "",
"a?c": "ac",
}
for pattern, name := range tests {
c.Logf("- checking pattern %q against %q -", pattern, name)
matched, err := s.renderer.Match(pattern, name)
c.Assert(err, jc.ErrorIsNil)
c.Check(matched, jc.IsFalse)
if s.matchesRuntime() {
gomatched, err := gofilepath.Match(pattern, name)
c.Assert(err, jc.ErrorIsNil)
c.Check(matched, gc.Equals, gomatched)
}
}
}
func (s windowsThinWrapperSuite) TestMatchBadPattern(c *gc.C) {
tests := map[string]string{
"ab[": "abc",
"ab[-c]": "abc",
"ab[]": "abc",
}
for pattern, name := range tests {
c.Logf("- checking pattern %q against %q -", pattern, name)
_, err := s.renderer.Match(pattern, name)
c.Check(err, gc.Equals, gofilepath.ErrBadPattern)
if s.matchesRuntime() {
_, goerr := gofilepath.Match(pattern, name)
c.Check(err, gc.Equals, goerr)
}
}
}
================================================
FILE: filestorage/doc.go
================================================
// Copyright 2014 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
/*
utils/filestorage provides types for abstracting and implementing a
system that stores files, including their metadata.
Each file in the system is identified by a unique ID, determined by the
system at the time the file is stored.
File metadata includes such information as the size of the file, its
checksum, and when it was created. Regardless of how it is stored in
the system, at the abstraction level it is represented as a document.
Metadata can exist in the system without an associated file. However,
every file must have a corresponding metadata doc stored in the system.
A file can be added for a metadata doc that does not have one already.
The main type is the FileStorage interface. It exposes the core
functionality of such a system. This includes adding/removing files,
retrieving them or their metadata, and listing all files in the system.
The package also provides a basic implementation of FileStorage,
available through NewFileStorage(). This implementation simply wraps
two more focused systems: doc storage and raw file storage. The wrapper
uses the doc storage to store the metadata and raw file storage to
store the files.
The two subsystems are exposed via corresponding interfaces: DocStorage
(and its specialization MetadataStorage) and RawFileStorage. While a
single type could implement both, in practice they will be separate.
The doc storage is responsible to generating the unique IDs. The raw
file storage defers to the doc storage for any information about the
file, including the ID.
*/
package filestorage
================================================
FILE: filestorage/export_test.go
================================================
// Copyright 2014 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package filestorage
================================================
FILE: filestorage/fakes_test.go
================================================
// Copyright 2014 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package filestorage_test
import (
"io"
"github.com/juju/errors"
jc "github.com/juju/testing/checkers"
gc "gopkg.in/check.v1"
"github.com/juju/utils/v4/filestorage"
)
// FakeMetadataStorage is used as a DocStorage and MetadataStorage for
// testing purposes.
type FakeMetadataStorage struct {
calls []string
id string
meta filestorage.Metadata
metaList []filestorage.Metadata
err error
idArg string
metaArg filestorage.Metadata
}
// Check verfies the state of the fake.
func (s *FakeMetadataStorage) Check(c *gc.C, id string, meta filestorage.Metadata, calls ...string) {
c.Check(s.calls, jc.DeepEquals, calls)
c.Check(s.idArg, gc.Equals, id)
c.Check(s.metaArg, gc.Equals, meta)
}
func (s *FakeMetadataStorage) Doc(id string) (filestorage.Document, error) {
s.calls = append(s.calls, "Doc")
s.idArg = id
if s.err != nil {
return nil, s.err
}
return s.meta, nil
}
func (s *FakeMetadataStorage) ListDocs() ([]filestorage.Document, error) {
s.calls = append(s.calls, "ListDoc")
if s.err != nil {
return nil, s.err
}
var docs []filestorage.Document
for _, doc := range s.metaList {
docs = append(docs, doc)
}
return docs, nil
}
func (s *FakeMetadataStorage) AddDoc(doc filestorage.Document) (string, error) {
s.calls = append(s.calls, "AddDoc")
meta, err := filestorage.Convert(doc)
if err != nil {
return "", errors.Trace(err)
}
s.metaArg = meta
return s.id, nil
}
func (s *FakeMetadataStorage) RemoveDoc(id string) error {
s.calls = append(s.calls, "RemoveDoc")
s.idArg = id
return s.err
}
func (s *FakeMetadataStorage) Close() error {
s.calls = append(s.calls, "Close")
return s.err
}
func (s *FakeMetadataStorage) Metadata(id string) (filestorage.Metadata, error) {
s.calls = append(s.calls, "Metadata")
s.idArg = id
if s.err != nil {
return nil, s.err
}
return s.meta, nil
}
func (s *FakeMetadataStorage) ListMetadata() ([]filestorage.Metadata, error) {
s.calls = append(s.calls, "ListMetadata")
if s.err != nil {
return nil, s.err
}
return s.metaList, nil
}
func (s *FakeMetadataStorage) AddMetadata(meta filestorage.Metadata) (string, error) {
s.calls = append(s.calls, "AddMetadata")
s.metaArg = meta
if s.err != nil {
return "", s.err
}
return s.id, nil
}
func (s *FakeMetadataStorage) RemoveMetadata(id string) error {
s.calls = append(s.calls, "RemoveMetadata")
s.idArg = id
return s.err
}
func (s *FakeMetadataStorage) SetStored(id string) error {
s.calls = append(s.calls, "SetStored")
s.idArg = id
return s.err
}
// FakeRawFileStorage is used in testing as a RawFileStorage.
type FakeRawFileStorage struct {
calls []string
file io.ReadCloser
err error
idArg string
fileArg io.Reader
sizeArg int64
}
// Check verfies the state of the fake.
func (s *FakeRawFileStorage) Check(c *gc.C, id string, file io.Reader, size int64, calls ...string) {
c.Check(s.calls, jc.DeepEquals, calls)
c.Check(s.idArg, gc.Equals, id)
c.Check(s.fileArg, gc.Equals, file)
c.Check(s.sizeArg, gc.Equals, size)
}
// CheckNotUsed verifies that the fake was not used.
func (s *FakeRawFileStorage) CheckNotUsed(c *gc.C) {
s.Check(c, "", nil, 0)
}
func (s *FakeRawFileStorage) File(id string) (io.ReadCloser, error) {
s.calls = append(s.calls, "File")
s.idArg = id
if s.err != nil {
return nil, s.err
}
return s.file, nil
}
func (s *FakeRawFileStorage) AddFile(id string, file io.Reader, size int64) error {
s.calls = append(s.calls, "AddFile")
s.idArg = id
s.fileArg = file
s.sizeArg = size
return s.err
}
func (s *FakeRawFileStorage) RemoveFile(id string) error {
s.calls = append(s.calls, "RemoveFile")
s.idArg = id
return s.err
}
func (s *FakeRawFileStorage) Close() error {
s.calls = append(s.calls, "Close")
return s.err
}
================================================
FILE: filestorage/interfaces.go
================================================
// Copyright 2014 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package filestorage
import (
"io"
"time"
)
// FileStorage is an abstraction that can be used for the storage of files.
type FileStorage interface {
io.Closer
// Metadata returns a file's metadata.
Metadata(id string) (Metadata, error)
// Get returns a file and its metadata.
Get(id string) (Metadata, io.ReadCloser, error)
// List returns the metadata for each stored file.
List() ([]Metadata, error)
// Add stores a file and its metadata.
Add(meta Metadata, archive io.Reader) (string, error)
// SetFile stores a file for an existing metadata entry.
SetFile(id string, file io.Reader) error
// Remove removes a file from storage.
Remove(id string) error
}
// Document represents a document that can be identified uniquely
// by a string.
type Document interface {
// ID returns the unique ID of the document.
ID() string
// SetID sets the ID of the document. If the ID is already set,
// SetID() should return true (false otherwise).
SetID(id string) (alreadySet bool)
}
// Metadata is the meta information for a stored file.
type Metadata interface {
Document
// Size is the size of the file (in bytes).
Size() int64
// Checksum is the checksum for the file.
Checksum() string
// ChecksumFormat is the kind (and encoding) of checksum.
ChecksumFormat() string
// Stored returns when the file was last stored. If it has not been
// stored yet, nil is returned. If it has been stored but the
// timestamp is not available, a zero value is returned
// (see Time.IsZero).
Stored() *time.Time
// SetFileInfo sets the file info on the metadata.
SetFileInfo(size int64, checksum, checksumFormat string) error
// SetStored records when the file was last stored. If the previous
// value matters, be sure to call Stored() first.
SetStored(timestamp *time.Time)
}
// DocStorage is an abstraction for a system that can store docs (structs).
// The system is expected to generate its own unique ID for each doc.
type DocStorage interface {
io.Closer
// Doc returns the doc that matches the ID. If there is no match,
// an error is returned (see errors.IsNotFound). Any other problem
// also results in an error.
Doc(id string) (Document, error)
// ListDocs returns a list of all the docs in the storage.
ListDocs() ([]Document, error)
// AddDoc adds the doc to the storage. If successful, the storage-
// generated ID for the doc is returned. Otherwise an error is
// returned.
AddDoc(doc Document) (string, error)
// RemoveDoc removes the matching doc from the storage. If there
// is no match an error is returned (see errors.IsNotFound). Any
// other problem also results in an error.
RemoveDoc(id string) error
}
// RawFileStorage is an abstraction around a system that can store files.
// The system is expected to rely on the user for unique IDs.
type RawFileStorage interface {
io.Closer
// File returns the matching file. If there is no match an error is
// returned (see errors.IsNotFound). Any other problem also results
// in an error.
File(id string) (io.ReadCloser, error)
// AddFile adds the file to the storage. If it fails to do so,
// it returns an error. If a file is already stored for the ID,
// AddFile() fails (see errors.IsAlreadyExists).
AddFile(id string, file io.Reader, size int64) error
// RemoveFile removes the matching file from the storage. It fails
// if there is no such file (see errors.IsNotFound). Any other problem
// also results in an error.
RemoveFile(id string) error
}
// MetadataStorage is an extension of DocStorage adapted to file metadata.
type MetadataStorage interface {
io.Closer
// Metadata returns the matching Metadata. It fails if there is no
// match (see errors.IsNotFound). Any other problems likewise
// results in an error.
Metadata(id string) (Metadata, error)
// ListMetadata returns a list of all metadata in the storage.
ListMetadata() ([]Metadata, error)
// AddMetadata adds the metadata to the storage. If successful, the
// storage-generated ID for the metadata is returned. Otherwise an
// error is returned.
AddMetadata(meta Metadata) (string, error)
// RemoveMetadata removes the matching metadata from the storage.
// If there is no match an error is returned (see errors.IsNotFound).
// Any other problem also results in an error.
RemoveMetadata(id string) error
// SetStored updates the stored metadata to indicate that the
// associated file has been successfully stored in a RawFileStorage
// system. If it does not find a stored metadata with the matching
// ID, it will return an error (see errors.IsNotFound). It also
// returns an error if it fails to update the stored metadata.
SetStored(id string) error
}
================================================
FILE: filestorage/metadata.go
================================================
// Copyright 2014 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package filestorage
import (
"time"
"github.com/juju/errors"
)
// RawDoc is a basic, uniquely identifiable document.
type RawDoc struct {
// ID is the unique identifier for the document.
ID string
}
// Doc wraps a document in the Document interface.
type Doc struct {
Raw RawDoc
}
// ID returns the document's unique identifier.
func (d *Doc) ID() string {
return d.Raw.ID
}
// SetID sets the document's unique identifier. If the ID is already
// set, SetID() returns true (false otherwise).
func (d *Doc) SetID(id string) bool {
if d.Raw.ID != "" {
return true
}
d.Raw.ID = id
return false
}
// RawFileMetadata holds info specific to stored files.
type RawFileMetadata struct {
// Size is the size (in bytes) of the stored file.
Size int64
// Checksum is the checksum of the stored file.
Checksum string
// ChecksumFormat describes the kind of the checksum.
ChecksumFormat string
// Stored records the timestamp of when the file was last stored.
Stored *time.Time
}
// FileMetadata contains the metadata for a single stored file.
type FileMetadata struct {
Doc
Raw RawFileMetadata
}
// NewMetadata returns a new Metadata for a stored file.
func NewMetadata() *FileMetadata {
meta := FileMetadata{}
return &meta
}
func (m *FileMetadata) Size() int64 {
return m.Raw.Size
}
func (m *FileMetadata) Checksum() string {
return m.Raw.Checksum
}
func (m *FileMetadata) ChecksumFormat() string {
return m.Raw.ChecksumFormat
}
func (m *FileMetadata) Stored() *time.Time {
return m.Raw.Stored
}
func (m *FileMetadata) SetFileInfo(size int64, checksum, format string) error {
// Fall back to existing values.
if size == 0 {
size = m.Raw.Size
}
if checksum == "" {
checksum = m.Raw.Checksum
}
if format == "" {
format = m.Raw.ChecksumFormat
}
if checksum != "" {
if format == "" {
return errors.Errorf("missing checksum format")
}
} else if format != "" {
return errors.Errorf("missing checksum")
}
// Only allow setting once.
if m.Raw.Size != 0 && size != m.Raw.Size {
return errors.Errorf("file information (size) already set")
}
if m.Raw.Checksum != "" && checksum != m.Raw.Checksum {
return errors.Errorf("file information (checksum) already set")
}
if m.Raw.ChecksumFormat != "" && format != m.Raw.ChecksumFormat {
return errors.Errorf("file information (checksum format) already set")
}
// Set the values.
m.Raw.Size = size
m.Raw.Checksum = checksum
m.Raw.ChecksumFormat = format
return nil
}
func (m *FileMetadata) SetStored(timestamp *time.Time) {
if timestamp == nil {
now := time.Now().UTC()
m.Raw.Stored = &now
} else {
m.Raw.Stored = timestamp
}
}
================================================
FILE: filestorage/metadata_store.go
================================================
// Copyright 2014 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package filestorage
import (
"github.com/juju/errors"
)
// Convert turns a Document into a Metadata if possible.
func Convert(doc Document) (Metadata, error) {
meta, ok := doc.(Metadata)
if !ok {
return nil, errors.Errorf("expected a Metadata doc, got %v", doc)
}
return meta, nil
}
// MetadataDocStorage provides the MetadataStorage methods than can be
// derived from DocStorage methods. To fully implement MetadataStorage,
// this type must be embedded in a type that implements the remaining
// methods.
type MetadataDocStorage struct {
DocStorage
}
// Metadata implements MetadataStorage.Metadata.
func (s *MetadataDocStorage) Metadata(id string) (Metadata, error) {
doc, err := s.Doc(id)
if err != nil {
return nil, errors.Trace(err)
}
meta, err := Convert(doc)
return meta, errors.Trace(err)
}
// ListMetadata implements MetadataStorage.ListMetadata.
func (s *MetadataDocStorage) ListMetadata() ([]Metadata, error) {
docs, err := s.ListDocs()
if err != nil {
return nil, errors.Trace(err)
}
var metaList []Metadata
for _, doc := range docs {
if doc == nil {
continue
}
meta, err := Convert(doc)
if err != nil {
return nil, errors.Trace(err)
}
metaList = append(metaList, meta)
}
return metaList, nil
}
// ListMetadata implements MetadataStorage.ListMetadata.
func (s *MetadataDocStorage) AddMetadata(meta Metadata) (string, error) {
id, err := s.AddDoc(meta)
return id, errors.Trace(err)
}
// ListMetadata implements MetadataStorage.ListMetadata.
func (s *MetadataDocStorage) RemoveMetadata(id string) error {
return errors.Trace(s.RemoveDoc(id))
}
================================================
FILE: filestorage/metadata_test.go
================================================
// Copyright 2014 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package filestorage_test
import (
"time"
"github.com/juju/testing"
gc "gopkg.in/check.v1"
"github.com/juju/utils/v4/filestorage"
)
var (
_ filestorage.Document = (*filestorage.Doc)(nil)
_ filestorage.Metadata = (*filestorage.FileMetadata)(nil)
)
var _ = gc.Suite(&MetadataSuite{})
type MetadataSuite struct {
testing.IsolationSuite
}
func (s *MetadataSuite) TestFileMetadataNewMetadata(c *gc.C) {
meta := filestorage.NewMetadata()
c.Check(meta.ID(), gc.Equals, "")
c.Check(meta.Size(), gc.Equals, int64(0))
c.Check(meta.Checksum(), gc.Equals, "")
c.Check(meta.ChecksumFormat(), gc.Equals, "")
c.Check(meta.Stored(), gc.IsNil)
}
func (s *MetadataSuite) TestFileMetadataSetIDInitial(c *gc.C) {
meta := filestorage.NewMetadata()
meta.SetFileInfo(10, "some sum", "SHA-1")
c.Assert(meta.ID(), gc.Equals, "")
success := meta.SetID("some id")
c.Check(success, gc.Equals, false)
c.Check(meta.ID(), gc.Equals, "some id")
}
func (s *MetadataSuite) TestFileMetadataSetIDAlreadySetSame(c *gc.C) {
meta := filestorage.NewMetadata()
meta.SetFileInfo(10, "some sum", "SHA-1")
success := meta.SetID("some id")
c.Assert(success, gc.Equals, false)
success = meta.SetID("some id")
c.Check(success, gc.Equals, true)
c.Check(meta.ID(), gc.Equals, "some id")
}
func (s *MetadataSuite) TestFileMetadataSetIDAlreadySetDifferent(c *gc.C) {
meta := filestorage.NewMetadata()
meta.SetFileInfo(10, "some sum", "SHA-1")
success := meta.SetID("some id")
c.Assert(success, gc.Equals, false)
success = meta.SetID("another id")
c.Check(success, gc.Equals, true)
c.Check(meta.ID(), gc.Equals, "some id")
}
func (s *MetadataSuite) TestFileMetadataSetFileInfo(c *gc.C) {
meta := filestorage.NewMetadata()
c.Assert(meta.Size(), gc.Equals, int64(0))
c.Assert(meta.Checksum(), gc.Equals, "")
c.Assert(meta.ChecksumFormat(), gc.Equals, "")
c.Assert(meta.Stored(), gc.IsNil)
meta.SetFileInfo(10, "some sum", "SHA-1")
c.Check(meta.Size(), gc.Equals, int64(10))
c.Check(meta.Checksum(), gc.Equals, "some sum")
c.Check(meta.ChecksumFormat(), gc.Equals, "SHA-1")
c.Check(meta.Stored(), gc.IsNil)
}
func (s *MetadataSuite) TestFileMetadataSetStored(c *gc.C) {
meta := filestorage.NewMetadata()
timestamp := time.Now().UTC()
meta.SetStored(×tamp)
c.Check(meta.Stored(), gc.Equals, ×tamp)
}
func (s *MetadataSuite) TestFileMetadataSetStoredDefault(c *gc.C) {
meta := filestorage.NewMetadata()
c.Assert(meta.Stored(), gc.IsNil)
meta.SetStored(nil)
c.Check(meta.Stored(), gc.NotNil)
}
================================================
FILE: filestorage/package_test.go
================================================
// Copyright 2014 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package filestorage_test
import (
"testing"
gc "gopkg.in/check.v1"
)
func TestPackage(t *testing.T) {
gc.TestingT(t)
}
================================================
FILE: filestorage/wrapper.go
================================================
// Copyright 2014 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package filestorage
import (
"io"
"github.com/juju/errors"
)
// Ensure fileStorage implements FileStorage.
var _ = FileStorage((*fileStorage)(nil))
type fileStorage struct {
metaStorage MetadataStorage
rawStorage RawFileStorage
}
// NewFileStorage returns a new FileStorage value that wraps a
// MetadataStorage and a RawFileStorage. It coordinates the two even
// though they may not be designed to be compatible (or the two may be
// the same value).
//
// A stored file will always have a metadata value stored. However, it
// is not required to have a raw file stored.
func NewFileStorage(meta MetadataStorage, files RawFileStorage) FileStorage {
stor := fileStorage{
metaStorage: meta,
rawStorage: files,
}
return &stor
}
// Metadata returns the matching metadata. Failure to find it (see
// errors.IsNotFound) or any other problem results in an error.
func (s *fileStorage) Metadata(id string) (Metadata, error) {
meta, err := s.metaStorage.Metadata(id)
if err != nil {
return nil, errors.Trace(err)
}
return meta, nil
}
// Get returns the matching file and its associated metadata. If there
// is no match (see errors.IsNotFound) or any other problem, it returns
// an error. Both the metadata and file must have been stored for the
// file to be considered found.
func (s *fileStorage) Get(id string) (Metadata, io.ReadCloser, error) {
meta, err := s.Metadata(id)
if err != nil {
return nil, nil, errors.Trace(err)
}
if meta.Stored() == nil {
return nil, nil, errors.NotFoundf("no file stored for %q", id)
}
file, err := s.rawStorage.File(id)
if err != nil {
return nil, nil, errors.Trace(err)
}
return meta, file, nil
}
// List returns a list of the metadata for all files in the storage.
func (s *fileStorage) List() ([]Metadata, error) {
return s.metaStorage.ListMetadata()
}
func (s *fileStorage) addFile(id string, size int64, file io.Reader) error {
err := s.rawStorage.AddFile(id, file, size)
if err != nil {
return errors.Trace(err)
}
err = s.metaStorage.SetStored(id)
if err != nil {
return errors.Trace(err)
}
return nil
}
// Add adds the file to the storage. It returns the unique ID generated
// by the storage for the file. If no file is provided, only the
// metadata is stored. While the passed-in "meta" is not modified, the
// new ID and "stored" flag will be saved in metadata storage. Feel
// free to explicitly call meta.SetID() and meta.SetStored() afterward.
//
// Any problem (including an existing file, see errors.IsAlreadyExists)
// results in an error. If there is an error while storing either the
// file or metadata, neither will be stored.
func (s *fileStorage) Add(meta Metadata, file io.Reader) (string, error) {
id, err := s.metaStorage.AddMetadata(meta)
if err != nil {
return "", errors.Trace(err)
}
if file != nil {
err = s.addFile(id, meta.Size(), file)
if err != nil {
// Remove the metadata we just added.
context := err
err = s.metaStorage.RemoveMetadata(id)
if err != nil {
err = errors.Annotate(err, "while handling another error")
return "", errors.Wrap(context, err)
}
return "", errors.Trace(context)
}
}
return id, nil
}
// SetFile stores the raw file for an existing metadata. If there is no
// matching stored metadata an error is returned (see errors.IsNotFound).
// If a file has already been stored an error is returned (see
// errors.IsAlreadyExists). Any other failure to add the file also
// results in an error.
func (s *fileStorage) SetFile(id string, file io.Reader) error {
meta, err := s.Metadata(id)
if err != nil {
return errors.Trace(err)
}
err = s.addFile(id, meta.Size(), file)
if err != nil {
return errors.Trace(err)
}
return nil
}
// Remove removes both the metadata and raw file from the storage. If
// there is no match an error is returned (see errors.IsNotFound).
//
// The raw file is removed first. Thus if there is any problem after
// removing the raw file, the metadata will still be stored. However,
// in that case the stored metadata is not guaranteed to accurately
// represent that there is no corresponding raw file in storage.
func (s *fileStorage) Remove(id string) error {
err := s.rawStorage.RemoveFile(id)
if err != nil && !errors.IsNotFound(err) {
return errors.Trace(err)
}
err = s.metaStorage.RemoveMetadata(id)
if err != nil {
return errors.Trace(err)
}
return nil
}
// Close implements io.Closer.Close.
func (s *fileStorage) Close() error {
ferr := s.rawStorage.Close()
merr := s.metaStorage.Close()
if ferr == nil {
return errors.Trace(merr)
} else if merr == nil {
return errors.Trace(ferr)
} else {
msg := "closing both failed: metadata (%v) and files (%v)"
return errors.Errorf(msg, merr, ferr)
}
}
================================================
FILE: filestorage/wrapper_test.go
================================================
// Copyright 2014 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package filestorage_test
import (
"bytes"
"io"
"io/ioutil"
"github.com/juju/errors"
"github.com/juju/testing"
jc "github.com/juju/testing/checkers"
gc "gopkg.in/check.v1"
"github.com/juju/utils/v4/filestorage"
)
var _ = gc.Suite(&WrapperSuite{})
type WrapperSuite struct {
testing.IsolationSuite
rawstor *FakeRawFileStorage
metastor *FakeMetadataStorage
stor filestorage.FileStorage
}
func (s *WrapperSuite) SetUpTest(c *gc.C) {
s.IsolationSuite.SetUpTest(c)
s.rawstor = &FakeRawFileStorage{}
s.metastor = &FakeMetadataStorage{}
s.stor = filestorage.NewFileStorage(s.metastor, s.rawstor)
}
func (s *WrapperSuite) metadata() filestorage.Metadata {
meta := filestorage.NewMetadata()
meta.SetFileInfo(10, "", "")
return meta
}
func (s *WrapperSuite) setMeta() (string, filestorage.Metadata) {
id := "<id>"
meta := s.metadata()
meta.SetID(id)
s.metastor.meta = meta
s.metastor.metaList = append(s.metastor.metaList, meta)
return id, meta
}
func (s *WrapperSuite) setFile(data string) (string, filestorage.Metadata, io.ReadCloser) {
id, meta := s.setMeta()
file := ioutil.NopCloser(bytes.NewBufferString(data))
s.rawstor.file = file
meta.SetStored(nil)
return id, meta, file
}
func (s *WrapperSuite) TestFileStorageNewFileStorage(c *gc.C) {
stor := filestorage.NewFileStorage(s.metastor, s.rawstor)
c.Check(stor, gc.NotNil)
}
func (s *WrapperSuite) TestFileStorageMetadata(c *gc.C) {
id, original := s.setMeta()
meta, err := s.stor.Metadata(id)
c.Assert(err, gc.IsNil)
c.Check(meta, jc.DeepEquals, original)
s.metastor.Check(c, id, nil, "Metadata")
s.rawstor.CheckNotUsed(c)
}
func (s *WrapperSuite) TestFileStorageGet(c *gc.C) {
id, origmeta, origfile := s.setFile("spam")
meta, file, err := s.stor.Get(id)
c.Assert(err, gc.IsNil)
c.Check(meta, gc.Equals, origmeta)
c.Check(file, gc.Equals, origfile)
}
func (s *WrapperSuite) TestFileStorageListEmpty(c *gc.C) {
list, err := s.stor.List()
c.Assert(err, gc.IsNil)
c.Check(list, gc.HasLen, 0)
}
func (s *WrapperSuite) TestFileStorageListOne(c *gc.C) {
id, _ := s.setMeta()
list, err := s.stor.List()
c.Assert(err, gc.IsNil)
c.Check(list, gc.HasLen, 1)
c.Assert(list[0], gc.NotNil)
c.Check(list[0].ID(), gc.Equals, id)
}
func (s *WrapperSuite) TestFileStorageListTwo(c *gc.C) {
id1, _ := s.setMeta()
id2, _ := s.setMeta()
list, err := s.stor.List()
c.Assert(err, gc.IsNil)
c.Assert(list, gc.HasLen, 2)
c.Assert(list[0], gc.NotNil)
c.Assert(list[1], gc.NotNil)
if list[0].ID() == id1 {
c.Check(list[1].ID(), gc.Equals, id2)
} else {
c.Check(list[1].ID(), gc.Equals, id1)
}
}
func (s *WrapperSuite) TestFileStorageAddMeta(c *gc.C) {
s.metastor.id = "<spam>"
meta := s.metadata()
c.Assert(meta.ID(), gc.Equals, "")
id, err := s.stor.Add(meta, nil)
c.Assert(err, gc.IsNil)
c.Check(id, gc.Equals, "<spam>")
c.Check(meta.ID(), gc.Equals, "")
s.metastor.Check(c, "", meta, "AddMetadata")
s.rawstor.CheckNotUsed(c)
}
func (s *WrapperSuite) TestFileStorageAddFile(c *gc.C) {
s.metastor.id = "<spam>"
var file *bytes.Buffer
meta := s.metadata()
id, err := s.stor.Add(meta, file)
c.Assert(err, gc.IsNil)
c.Check(meta.ID(), gc.Equals, "")
c.Check(meta.Stored(), gc.IsNil)
c.Check(id, gc.Equals, "<spam>")
c.Check(meta.ID(), gc.Equals, "")
s.metastor.Check(c, id, meta, "AddMetadata", "SetStored")
s.rawstor.Check(c, id, file, 10, "AddFile")
}
func (s *WrapperSuite) TestFileStorageAddIDNotSet(c *gc.C) {
original := s.metadata()
c.Assert(original.ID(), gc.Equals, "")
_, err := s.stor.Add(original, nil)
c.Check(err, gc.IsNil)
c.Check(original.ID(), gc.Equals, "")
}
func (s *WrapperSuite) TestFileStorageAddMetaOnly(c *gc.C) {
id, original := s.setMeta()
meta, err := s.stor.Metadata(id)
c.Assert(err, gc.IsNil)
c.Check(meta, gc.Equals, original)
c.Check(meta.Stored(), gc.IsNil)
}
func (s *WrapperSuite) TestFileStorageAddIDAlreadySet(c *gc.C) {
original := s.metadata()
original.SetID("eggs")
_, err := s.stor.Add(original, nil)
c.Check(err, gc.IsNil) // This should be handled at the lower level.
}
func (s *WrapperSuite) TestFileStorageAddFileFailureDropsMetadata(c *gc.C) {
original := s.metadata()
failure := errors.New("failed!")
raw := &FakeRawFileStorage{err: failure}
stor := filestorage.NewFileStorage(s.metastor, raw)
_, err := stor.Add(original, &bytes.Buffer{})
c.Assert(errors.Cause(err), gc.Equals, failure)
metalist, metaErr := s.metastor.ListMetadata()
c.Assert(metaErr, gc.IsNil)
c.Check(metalist, gc.HasLen, 0)
c.Check(original.ID(), gc.Equals, "")
}
func (s *WrapperSuite) TestFileStorageSetFile(c *gc.C) {
id, _ := s.setMeta()
_, _, err := s.stor.Get(id)
c.Assert(err, gc.NotNil)
file := bytes.NewBufferString("spam")
err = s.stor.SetFile(id, file)
c.Assert(err, gc.IsNil)
s.metastor.Check(c, id, nil, "Metadata", "Metadata", "SetStored")
s.rawstor.Check(c, id, file, 10, "AddFile")
}
func (s *WrapperSuite) TestFileStorageRemove(c *gc.C) {
id := "<spam>"
err := s.stor.Remove(id)
c.Assert(err, gc.IsNil)
s.metastor.Check(c, id, nil, "RemoveMetadata")
s.rawstor.Check(c, id, nil, 0, "RemoveFile")
}
func (s *WrapperSuite) TestClose(c *gc.C) {
metaStor := &FakeMetadataStorage{}
fileStor := &FakeRawFileStorage{}
stor := filestorage.NewFileStorage(metaStor, fileStor)
err := stor.Close()
c.Assert(err, gc.IsNil)
c.Check(metaStor.calls, gc.DeepEquals, []string{"Close"})
c.Check(fileStor.calls, gc.DeepEquals, []string{"Close"})
}
================================================
FILE: fs/copy.go
================================================
// Copyright 2014 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package fs
import (
"fmt"
"io"
"os"
"path/filepath"
)
// Copy recursively copies the file, directory or symbolic link at src
// to dst. The destination must not exist. Symbolic links are not
// followed.
//
// If the copy fails half way through, the destination might be left
// partially written.
func Copy(src, dst string) error {
srcInfo, srcErr := os.Lstat(src)
if srcErr != nil {
return srcErr
}
_, dstErr := os.Lstat(dst)
if dstErr == nil {
// TODO(rog) add a flag to permit overwriting?
return fmt.Errorf("will not overwrite %q", dst)
}
if !os.IsNotExist(dstErr) {
return dstErr
}
switch mode := srcInfo.Mode(); mode & os.ModeType {
case os.ModeSymlink:
return copySymLink(src, dst)
case os.ModeDir:
return copyDir(src, dst, mode)
case 0:
return copyFile(src, dst, mode)
default:
return fmt.Errorf("cannot copy file with mode %v", mode)
}
}
func copySymLink(src, dst string) error {
target, err := os.Readlink(src)
if err != nil {
return err
}
return os.Symlink(target, dst)
}
func copyFile(src, dst string, mode os.FileMode) error {
srcf, err := os.Open(src)
if err != nil {
return err
}
defer srcf.Close()
dstf, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode.Perm())
if err != nil {
return err
}
defer dstf.Close()
// Make the actual permissions match the source permissions
// even in the presence of umask.
if err := os.Chmod(dstf.Name(), mode.Perm()); err != nil {
return err
}
if _, err := io.Copy(dstf, srcf); err != nil {
return fmt.Errorf("cannot copy %q to %q: %v", src, dst, err)
}
return nil
}
func copyDir(src, dst string, mode os.FileMode) error {
srcf, err := os.Open(src)
if err != nil {
return err
}
defer srcf.Close()
if mode&0500 == 0 {
// The source directory doesn't have write permission,
// so give the new directory write permission anyway
// so that we have permission to create its contents.
// We'll make the permissions match at the end.
mode |= 0500
}
if err := os.Mkdir(dst, mode.Perm()); err != nil {
return err
}
for {
names, err := srcf.Readdirnames(100)
for _, name := range names {
if err := Copy(filepath.Join(src, name), filepath.Join(dst, name)); err != nil {
return err
}
}
if err == io.EOF {
break
}
if err != nil {
return fmt.Errorf("error reading directory %q: %v", src, err)
}
}
if err := os.Chmod(dst, mode.Perm()); err != nil {
return err
}
return nil
}
================================================
FILE: fs/copy_test.go
================================================
// Copyright 2014 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package fs_test
import (
"path/filepath"
"testing"
ft "github.com/juju/testing/filetesting"
gc "gopkg.in/check.v1"
"github.com/juju/utils/v4/fs"
)
type copySuite struct{}
var _ = gc.Suite(©Suite{})
func TestPackage(t *testing.T) {
gc.TestingT(t)
}
var copyTests = []struct {
about string
src ft.Entries
dst ft.Entries
err string
}{{
about: "one file",
src: []ft.Entry{
ft.File{"file", "data", 0756},
},
}, {
about: "one directory",
src: []ft.Entry{
ft.Dir{"dir", 0777},
},
}, {
about: "one symlink",
src: []ft.Entry{
ft.Symlink{"link", "/foo"},
},
}, {
about: "several entries",
src: []ft.Entry{
ft.Dir{"top", 0755},
ft.File{"top/foo", "foodata", 0644},
ft.File{"top/bar", "bardata", 0633},
ft.Dir{"top/next", 0721},
ft.Symlink{"top/next/link", "../foo"},
ft.File{"top/next/another", "anotherdata", 0644},
},
}, {
about: "destination already exists",
src: []ft.Entry{
ft.Dir{"dir", 0777},
},
dst: []ft.Entry{
ft.Dir{"dir", 0777},
},
err: `will not overwrite ".+dir"`,
}, {
about: "source with unwritable directory",
src: []ft.Entry{
ft.Dir{"dir", 0555},
},
}}
func (*copySuite) TestCopy(c *gc.C) {
for i, test := range copyTests {
c.Logf("test %d: %v", i, test.about)
src, dst := c.MkDir(), c.MkDir()
test.src.Create(c, src)
test.dst.Create(c, dst)
path := test.src[0].GetPath()
err := fs.Copy(
filepath.Join(src, path),
filepath.Join(dst, path),
)
if test.err != "" {
c.Check(err, gc.ErrorMatches, test.err)
} else {
c.Assert(err, gc.IsNil)
test.src.Check(c, dst)
}
}
}
================================================
FILE: go.mod
================================================
module github.com/juju/utils/v4
go 1.24.4
require (
github.com/juju/clock v1.0.3
github.com/juju/collections v1.0.4
github.com/juju/errors v1.0.0
github.com/juju/loggo/v2 v2.0.0
github.com/juju/mutex/v2 v2.0.0
github.com/juju/testing v1.2.0
golang.org/x/crypto v0.39.0
golang.org/x/net v0.41.0
golang.org/x/text v0.26.0
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7
gopkg.in/yaml.v2 v2.4.0
)
require (
github.com/juju/loggo v1.0.0 // indirect
github.com/juju/utils/v3 v3.1.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/rogpeppe/go-internal v1.9.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/term v0.32.0 // indirect
)
================================================
FILE: go.sum
================================================
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU=
github.com/juju/clock v1.0.3 h1:yJHIsWXeU8j3QcBdiess09SzfiXRRrsjKPn2whnMeds=
github.com/juju/clock v1.0.3/go.mod h1:HIBvJ8kiV/n7UHwKuCkdYL4l/MDECztHR2sAvWDxxf0=
github.com/juju/collections v1.0.4 h1:GjL+aN512m2rVDqhPII7P6qB0e+iYFubz8sqBhZaZtk=
github.com/juju/collections v1.0.4/go.mod h1:hVrdB0Zwq9wIU1Fl6ItD2+UETeNeOEs+nGvJufVe+0c=
github.com/juju/errors v1.0.0 h1:yiq7kjCLll1BiaRuNY53MGI0+EQ3rF6GB+wvboZDefM=
github.com/juju/errors v1.0.0/go.mod h1:B5x9thDqx0wIMH3+aLIMP9HjItInYWObRovoCFM5Qe8=
github.com/juju/loggo v1.0.0 h1:Y6ZMQOGR9Aj3BGkiWx7HBbIx6zNwNkxhVNOHU2i1bl0=
github.com/juju/loggo v1.0.0/go.mod h1:NIXFioti1SmKAlKNuUwbMenNdef59IF52+ZzuOmHYkg=
github.com/juju/loggo/v2 v2.0.0 h1:PzyVIn+NgoZ22QUtPgKF/lh+6SnaCOEXhcP+sE4FhOk=
github.com/juju/loggo/v2 v2.0.0/go.mod h1:647d6WvXBLj5lvka2qBvccr7vMIvF2KFkEH+0ZuFOUM=
github.com/juju/mutex/v2 v2.0.0 h1:rVmJdOaXGWF8rjcFHBNd4x57/1tks5CgXHx55O55SB0=
github.com/juju/mutex/v2 v2.0.0/go.mod h1:jwCfBs/smYDaeZLqeaCi8CB8M+tOes4yf827HoOEoqk=
github.com/juju/testing v1.2.0 h1:Q0wxjaxx4XPVEN+SgzxKr3d82pjmSBcuM3WndAU391c=
github.com/juju/testing v1.2.0/go.mod h1:lqZVzNwBKAbylGZidK77ts6kIdoOkmD52+4m0ysetPo=
github.com/juju/utils/v3 v3.1.0 h1:NrNo73oVtfr7kLP17/BDpubXwa7YEW16+Ult6z9kpHI=
github.com/juju/utils/v3 v3.1.0/go.mod h1:nAj3sHtdYfAkvnkqttTy3Xzm2HzkD9Hfgnc+upOW2Z8=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lunixbochs/vtclean v0.0.0-20160125035106-4fbf7632a2c6/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
github.com/mattn/go-colorable v0.0.6/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.0-20160806122752-66b8e73f3f5c/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20160105164936-4f90aeace3a2/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
================================================
FILE: gomaxprocs.go
================================================
// Copyright 2014 Canonical Ltd.
// Licensed under the LGPLv3, 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)
}
================================================
FILE: gomaxprocs_test.go
================================================
// Copyright 2014 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package utils_test
import (
"os"
"github.com/juju/testing"
jc "github.com/juju/testing/checkers"
gc "gopkg.in/check.v1"
"github.com/juju/utils/v4"
)
type gomaxprocsSuite struct {
testing.IsolationSuite
setmaxprocs chan int
numCPUResponse int
setMaxProcs int
}
var _ = gc.Suite(&gomaxprocsSuite{})
func (s *gomaxprocsSuite) SetUpTest(c *gc.C) {
s.IsolationSuite.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) {
err := os.Setenv("GOMAXPROCS", "1")
c.Assert(err, jc.ErrorIsNil)
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)
}
================================================
FILE: hash/fingerprint.go
================================================
// Copyright 2016 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package hash
import (
"encoding/base64"
"encoding/hex"
"hash"
"io"
"github.com/juju/errors"
)
// Fingerprint represents the checksum for some data.
type Fingerprint struct {
sum []byte
}
// NewFingerprint returns wraps the provided raw hash sum. This function
// roundtrips with Fingerprint.Bytes().
func NewFingerprint(sum []byte, validate func([]byte) error) (Fingerprint, error) {
if validate == nil {
return Fingerprint{}, errors.New("missing validate func")
}
if err := validate(sum); err != nil {
return Fingerprint{}, errors.Trace(err)
}
return newFingerprint(sum), nil
}
// NewValidFingerprint returns a Fingerprint corresponding
// to the current of the provided hash.
func NewValidFingerprint(hash hash.Hash) Fingerprint {
sum := hash.Sum(nil)
return newFingerprint(sum)
}
func newFingerprint(sum []byte) Fingerprint {
return Fingerprint{
sum: append([]byte{}, sum...), // Use an isolated copy.
}
}
// GenerateFingerprint returns the fingerprint for the provided data.
func GenerateFingerprint(reader io.Reader, newHash func() hash.Hash) (Fingerprint, error) {
var fp Fingerprint
if reader == nil {
return fp, errors.New("missing reader")
}
if newHash == nil {
return fp, errors.New("missing new hash func")
}
hash := newHash()
if _, err := io.Copy(hash, reader); err != nil {
return fp, errors.Trace(err)
}
fp.sum = hash.Sum(nil)
return fp, nil
}
// ParseHexFingerprint returns wraps the provided raw fingerprint string.
// This function roundtrips with Fingerprint.Hex().
func ParseHexFingerprint(hexSum string, validate func([]byte) error) (Fingerprint, error) {
if validate == nil {
return Fingerprint{}, errors.New("missing validate func")
}
sum, err := hex.DecodeString(hexSum)
if err != nil {
return Fingerprint{}, errors.Trace(err)
}
fp, err := NewFingerprint(sum, validate)
if err != nil {
return Fingerprint{}, errors.Trace(err)
}
return fp, nil
}
// ParseBase64Fingerprint returns wraps the provided raw fingerprint string.
// This function roundtrips with Fingerprint.Base64().
func ParseBase64Fingerprint(b64Sum string, validate func([]byte) error) (Fingerprint, error) {
if validate == nil {
return Fingerprint{}, errors.New("missing validate func")
}
sum, err := base64.StdEncoding.DecodeString(b64Sum)
if err != nil {
return Fingerprint{}, errors.Trace(err)
}
fp, err := NewFingerprint(sum, validate)
if err != nil {
return Fingerprint{}, errors.Trace(err)
}
return fp, nil
}
// String implements fmt.Stringer.
func (fp Fingerprint) String() string {
return fp.Hex()
}
// Hex returns the hex string representation of the fingerprint.
func (fp Fingerprint) Hex() string {
return hex.EncodeToString(fp.sum)
}
// Base64 returns the base64 encoded fingerprint.
func (fp Fingerprint) Base64() string {
return base64.StdEncoding.EncodeToString(fp.sum)
}
// Bytes returns the raw (sum) bytes of the fingerprint.
func (fp Fingerprint) Bytes() []byte {
return append([]byte{}, fp.sum...)
}
// IsZero returns whether or not the fingerprint is the zero value.
func (fp Fingerprint) IsZero() bool {
return len(fp.sum) == 0
}
// Validate returns an error if the fingerprint is invalid.
func (fp Fingerprint) Validate() error {
if fp.IsZero() {
return errors.NotValidf("zero-value fingerprint")
}
return nil
}
================================================
FILE: hash/fingerprint_test.go
================================================
// Copyright 2016 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package hash_test
import (
"crypto/sha512"
"encoding/hex"
stdhash "hash"
"github.com/juju/errors"
"github.com/juju/testing"
jc "github.com/juju/testing/checkers"
"github.com/juju/testing/filetesting"
gc "gopkg.in/check.v1"
"github.com/juju/utils/v4/hash"
)
var _ = gc.Suite(&FingerprintSuite{})
type FingerprintSuite struct {
stub *testing.Stub
hash *filetesting.StubHash
}
func (s *FingerprintSuite) SetUpTest(c *gc.C) {
s.stub = &testing.Stub{}
s.hash = filetesting.NewStubHash(s.stub, nil)
}
func (s *FingerprintSuite) newHash() stdhash.Hash {
s.stub.AddCall("newHash")
s.stub.NextErr() // Pop one off.
return s.hash
}
func (s *FingerprintSuite) validate(sum []byte) error {
s.stub.AddCall("validate", sum)
if err := s.stub.NextErr(); err != nil {
return errors.Trace(err)
}
return nil
}
func (s *FingerprintSuite) TestNewFingerprintOkay(c *gc.C) {
expected, _ := newFingerprint(c, "spamspamspam")
fp, err := hash.NewFingerprint(expected, s.validate)
c.Assert(err, jc.ErrorIsNil)
sum := fp.Bytes()
s.stub.CheckCallNam
gitextract_0jyt02x8/
├── .gitignore
├── ISSUE_TEMPLATE.md
├── LICENSE
├── LICENSE.golang
├── Makefile
├── README.md
├── SECURITY.md
├── arch/
│ ├── arch.go
│ ├── arch_test.go
│ └── package_test.go
├── attempt.go
├── attempt_test.go
├── bzr/
│ ├── bzr.go
│ ├── bzr_test.go
│ ├── bzr_unix_test.go
│ └── bzr_windows_test.go
├── cache/
│ ├── cache.go
│ ├── cache_test.go
│ ├── export_test.go
│ └── package_test.go
├── cert/
│ ├── cert.go
│ ├── cert_test.go
│ └── exports_test.go
├── command.go
├── command_test.go
├── context.go
├── context_test.go
├── du/
│ ├── LICENSE.ricochet2200
│ ├── diskusage.go
│ └── diskusage_windows.go
├── errors.go
├── exec/
│ ├── exec.go
│ ├── exec_internal_test.go
│ ├── exec_linux_test.go
│ ├── exec_test.go
│ ├── exec_unix.go
│ ├── exec_windows.go
│ ├── exec_windows_test.go
│ └── package_test.go
├── export_test.go
├── file.go
├── file_test.go
├── file_unix.go
├── file_unix_test.go
├── file_windows.go
├── file_windows_test.go
├── filepath/
│ ├── common.go
│ ├── common_test.go
│ ├── export_test.go
│ ├── filepath.go
│ ├── filepath_test.go
│ ├── interface_test.go
│ ├── package_test.go
│ ├── stdlib.go
│ ├── stdlib_test.go
│ ├── stdlibmatch.go
│ ├── unix.go
│ ├── unix_test.go
│ ├── win.go
│ └── win_test.go
├── filestorage/
│ ├── doc.go
│ ├── export_test.go
│ ├── fakes_test.go
│ ├── interfaces.go
│ ├── metadata.go
│ ├── metadata_store.go
│ ├── metadata_test.go
│ ├── package_test.go
│ ├── wrapper.go
│ └── wrapper_test.go
├── fs/
│ ├── copy.go
│ └── copy_test.go
├── go.mod
├── go.sum
├── gomaxprocs.go
├── gomaxprocs_test.go
├── hash/
│ ├── fingerprint.go
│ ├── fingerprint_test.go
│ ├── hash.go
│ ├── hash_test.go
│ ├── package_test.go
│ ├── writer.go
│ └── writer_test.go
├── home_unix.go
├── home_unix_test.go
├── home_windows.go
├── home_windows_test.go
├── isubuntu.go
├── isubuntu_test.go
├── jsonhttp/
│ ├── jsonhttp.go
│ ├── jsonhttp_test.go
│ └── package_test.go
├── keyvalues/
│ ├── keyvalues.go
│ ├── keyvalues_test.go
│ └── package_test.go
├── limiter.go
├── limiter_test.go
├── multireader.go
├── multireader_test.go
├── naturalsort.go
├── naturalsort_test.go
├── network.go
├── network_test.go
├── os.go
├── os_test.go
├── package_test.go
├── parallel/
│ ├── package_test.go
│ ├── parallel.go
│ ├── parallel_test.go
│ ├── try.go
│ └── try_test.go
├── password.go
├── password_test.go
├── proxy/
│ ├── package_test.go
│ ├── proxy.go
│ └── proxy_test.go
├── randomstring.go
├── randomstring_test.go
├── registry/
│ ├── export_test.go
│ ├── package_test.go
│ ├── registry.go
│ └── registry_test.go
├── relativeurl.go
├── relativeurl_test.go
├── setenv.go
├── setenv_test.go
├── shell/
│ ├── bash.go
│ ├── bash_test.go
│ ├── command.go
│ ├── interface_test.go
│ ├── output.go
│ ├── package_test.go
│ ├── powershell.go
│ ├── powershell_test.go
│ ├── renderer.go
│ ├── renderer_test.go
│ ├── script.go
│ ├── script_test.go
│ ├── unix.go
│ ├── win.go
│ ├── wincmd.go
│ └── wincmd_test.go
├── size.go
├── size_test.go
├── ssh/
│ ├── authorisedkeys.go
│ ├── authorisedkeys_test.go
│ ├── clientkeys.go
│ ├── clientkeys_test.go
│ ├── export_test.go
│ ├── fakes_test.go
│ ├── fingerprint.go
│ ├── fingerprint_test.go
│ ├── generate.go
│ ├── generate_test.go
│ ├── package_test.go
│ ├── run.go
│ ├── run_test.go
│ ├── ssh.go
│ ├── ssh_gocrypto.go
│ ├── ssh_gocrypto_test.go
│ ├── ssh_openssh.go
│ ├── ssh_test.go
│ ├── stream.go
│ ├── stream_test.go
│ ├── stream_wrapper_unix.go
│ ├── stream_wrapper_windows.go
│ └── testing/
│ └── keys.go
├── symlink/
│ ├── export_test.go
│ ├── symlink.go
│ ├── symlink_posix.go
│ ├── symlink_test.go
│ ├── symlink_windows.go
│ ├── symlink_windows_test.go
│ ├── zsymlink_windows_386.go
│ └── zsymlink_windows_amd64.go
├── systemerrmessages_unix.go
├── systemerrmessages_windows.go
├── tailer/
│ ├── export_test.go
│ ├── package_test.go
│ ├── tailer.go
│ └── tailer_test.go
├── tar/
│ ├── tar.go
│ └── tar_test.go
├── timer.go
├── timer_test.go
├── trivial.go
├── trivial_test.go
├── uptime/
│ ├── uptime_nix.go
│ ├── uptime_windows.go
│ ├── zuptime_windows_386.go
│ └── zuptime_windows_amd64.go
├── username.go
├── username_test.go
├── uuid.go
├── uuid_test.go
├── voyeur/
│ ├── package_test.go
│ ├── value.go
│ └── value_test.go
├── yaml.go
├── yaml_test.go
├── zfile_windows.go
└── zip/
├── package_test.go
├── zip.go
└── zip_test.go
SYMBOL INDEX (1319 symbols across 185 files)
FILE: arch/arch.go
constant AMD64 (line 14) | AMD64 = "amd64"
constant I386 (line 15) | I386 = "i386"
constant ARM (line 16) | ARM = "armhf"
constant ARM64 (line 17) | ARM64 = "arm64"
constant PPC64EL (line 18) | PPC64EL = "ppc64el"
constant S390X (line 19) | S390X = "s390x"
constant RISCV64 (line 20) | RISCV64 = "riscv64"
constant LEGACY_PPC64 (line 23) | LEGACY_PPC64 = "ppc64"
type ArchInfo (line 49) | type ArchInfo struct
function hostArch (line 73) | func hostArch() string {
function NormaliseArch (line 80) | func NormaliseArch(rawArch string) string {
function IsSupportedArch (line 91) | func IsSupportedArch(arch string) bool {
FILE: arch/arch_test.go
type archSuite (line 13) | type archSuite struct
method TestHostArch (line 18) | func (s *archSuite) TestHostArch(c *gc.C) {
method TestNormaliseArch (line 23) | func (s *archSuite) TestNormaliseArch(c *gc.C) {
method TestIsSupportedArch (line 53) | func (s *archSuite) TestIsSupportedArch(c *gc.C) {
method TestArchInfo (line 60) | func (s *archSuite) TestArchInfo(c *gc.C) {
FILE: arch/package_test.go
function Test (line 12) | func Test(t *testing.T) {
FILE: attempt.go
type AttemptStrategy (line 14) | type AttemptStrategy struct
method Start (line 29) | func (s AttemptStrategy) Start() *Attempt {
type Attempt (line 20) | type Attempt struct
method Next (line 43) | func (a *Attempt) Next() bool {
method nextSleep (line 59) | func (a *Attempt) nextSleep(now time.Time) time.Duration {
method HasNext (line 70) | func (a *Attempt) HasNext() bool {
FILE: attempt_test.go
function doSomething (line 14) | func doSomething() (int, error) { return 0, nil }
function shouldRetry (line 16) | func shouldRetry(error) bool { return false }
function doSomethingWith (line 18) | func doSomethingWith(int) {}
function ExampleAttempt_HasNext (line 20) | func ExampleAttempt_HasNext() {
method TestAttemptTiming (line 44) | func (*utilsSuite) TestAttemptTiming(c *gc.C) {
method TestAttemptNextHasNext (line 67) | func (*utilsSuite) TestAttemptNextHasNext(c *gc.C) {
FILE: bzr/bzr.go
type Branch (line 17) | type Branch struct
method Location (line 48) | func (b *Branch) Location() string {
method Join (line 55) | func (b *Branch) Join(parts ...string) string {
method bzr (line 59) | func (b *Branch) bzr(subcommand string, args ...string) (stdout, stder...
method Init (line 80) | func (b *Branch) Init() error {
method Add (line 86) | func (b *Branch) Add(parts ...string) error {
method Commit (line 92) | func (b *Branch) Commit(message string) error {
method RevisionId (line 98) | func (b *Branch) RevisionId() (string, error) {
method PushLocation (line 115) | func (b *Branch) PushLocation() (string, error) {
method Push (line 135) | func (b *Branch) Push(attr *PushAttr) error {
method CheckClean (line 150) | func (b *Branch) CheckClean() error {
function New (line 23) | func New(location string) *Branch {
function cenv (line 36) | func cenv() []string {
type PushAttr (line 127) | type PushAttr struct
FILE: bzr/bzr_test.go
function Test (line 21) | func Test(t *stdtesting.T) {
type BzrSuite (line 27) | type BzrSuite struct
method SetUpTest (line 36) | func (s *BzrSuite) SetUpTest(c *gc.C) {
method TestNewFindsRoot (line 50) | func (s *BzrSuite) TestNewFindsRoot(c *gc.C) {
method TestJoin (line 61) | func (s *BzrSuite) TestJoin(c *gc.C) {
method TestErrorHandling (line 66) | func (s *BzrSuite) TestErrorHandling(c *gc.C) {
method TestInit (line 71) | func (s *BzrSuite) TestInit(c *gc.C) {
method TestRevisionIdOnEmpty (line 76) | func (s *BzrSuite) TestRevisionIdOnEmpty(c *gc.C) {
method TestCommit (line 82) | func (s *BzrSuite) TestCommit(c *gc.C) {
method TestPush (line 100) | func (s *BzrSuite) TestPush(c *gc.C) {
method TestCheckClean (line 151) | func (s *BzrSuite) TestCheckClean(c *gc.C) {
constant bzr_config (line 32) | bzr_config = `[DEFAULT]
FILE: bzr/bzr_unix_test.go
constant bzrHome (line 10) | bzrHome = ".bazaar"
FILE: bzr/bzr_windows_test.go
constant bzrHome (line 10) | bzrHome = "Bazaar/2.0"
FILE: cache/cache.go
type entry (line 20) | type entry struct
type Key (line 26) | type Key
type Cache (line 29) | type Cache struct
method Len (line 70) | func (c *Cache) Len() int {
method Evict (line 77) | func (c *Cache) Evict(key Key) {
method EvictAll (line 85) | func (c *Cache) EvictAll() {
method Get (line 96) | func (c *Cache) Get(key Key, fetch func() (any, error)) (any, error) {
method getAtTime (line 102) | func (c *Cache) getAtTime(key Key, fetch func() (any, error), now time...
method cachedValue (line 159) | func (c *Cache) cachedValue(key Key, now time.Time) (any, bool) {
method entry (line 187) | func (c *Cache) entry(m map[Key]entry, key Key, now time.Time) (entry,...
type fetchCall (line 50) | type fetchCall struct
function New (line 59) | func New(maxAge time.Duration) *Cache {
FILE: cache/cache_test.go
type suite (line 16) | type suite struct
method TestSimpleGet (line 20) | func (*suite) TestSimpleGet(c *gc.C) {
method TestEvict (line 27) | func (*suite) TestEvict(c *gc.C) {
method TestEvictOld (line 47) | func (*suite) TestEvictOld(c *gc.C) {
method TestFetchError (line 79) | func (*suite) TestFetchError(c *gc.C) {
method TestFetchOnlyOnce (line 88) | func (*suite) TestFetchOnlyOnce(c *gc.C) {
method TestEntryExpiresAfterMaxEntryAge (line 99) | func (*suite) TestEntryExpiresAfterMaxEntryAge(c *gc.C) {
method TestEntriesRemovedWhenNotRetrieved (line 116) | func (*suite) TestEntriesRemovedWhenNotRetrieved(c *gc.C) {
method TestRefreshedEntry (line 145) | func (*suite) TestRefreshedEntry(c *gc.C) {
method TestConcurrentFetch (line 179) | func (*suite) TestConcurrentFetch(c *gc.C) {
method TestRefreshSpread (line 199) | func (*suite) TestRefreshSpread(c *gc.C) {
method TestSingleFlight (line 243) | func (*suite) TestSingleFlight(c *gc.C) {
function fetchError (line 291) | func fetchError(err error) func() (any, error) {
function fetchValue (line 297) | func fetchValue(val any) func() (any, error) {
FILE: cache/export_test.go
function OldLen (line 8) | func OldLen(c *Cache) int {
FILE: cache/package_test.go
function TestPackage (line 12) | func TestPackage(t *testing.T) {
FILE: cert/cert.go
type OtherName (line 20) | type OtherName struct
type GeneralName (line 25) | type GeneralName struct
type GeneralNames (line 31) | type GeneralNames struct
function getUPNExtensionValue (line 45) | func getUPNExtensionValue(subject pkix.Name) ([]byte, error) {
function ParseCert (line 63) | func ParseCert(certPEM string) (*x509.Certificate, error) {
function ParseCertAndKey (line 81) | func ParseCertAndKey(certPEM, keyPEM string) (*x509.Certificate, crypto....
FILE: cert/cert_test.go
function TestAll (line 16) | func TestAll(t *testing.T) {
type certSuite (line 20) | type certSuite struct
method TestParseCertificate (line 24) | func (certSuite) TestParseCertificate(c *gc.C) {
method TestParseCertAndKey (line 38) | func (certSuite) TestParseCertAndKey(c *gc.C) {
FILE: command.go
function RunCommand (line 11) | func RunCommand(command string, args ...string) (output string, err erro...
FILE: command_test.go
type EnvironmentPatcher (line 17) | type EnvironmentPatcher interface
function patchExecutable (line 21) | func patchExecutable(patcher EnvironmentPatcher, dir, execName, script s...
type commandSuite (line 27) | type commandSuite struct
method TestRunCommandCombinesOutput (line 33) | func (s *commandSuite) TestRunCommandCombinesOutput(c *gc.C) {
method TestRunCommandNonZeroExit (line 58) | func (s *commandSuite) TestRunCommandNonZeroExit(c *gc.C) {
FILE: context.go
type timerCtx (line 19) | type timerCtx struct
method Deadline (line 35) | func (ctx *timerCtx) Deadline() (time.Time, bool) {
method Err (line 39) | func (ctx *timerCtx) Err() error {
method Value (line 45) | func (ctx *timerCtx) Value(key any) any {
method Done (line 49) | func (ctx *timerCtx) Done() <-chan struct{} {
method cancel (line 53) | func (ctx *timerCtx) cancel(err error) {
method String (line 70) | func (ctx *timerCtx) String() string {
function ContextWithTimeout (line 77) | func ContextWithTimeout(parent context.Context, clk clock.Clock, timeout...
function ContextWithDeadline (line 84) | func ContextWithDeadline(parent context.Context, clk clock.Clock, deadli...
FILE: context_test.go
type contextSuite (line 19) | type contextSuite struct
method TestDeadline (line 26) | func (*contextSuite) TestDeadline(c *gc.C) {
method TestTimeout (line 52) | func (*contextSuite) TestTimeout(c *gc.C) {
method TestCanceledTimeout (line 68) | func (*contextSuite) TestCanceledTimeout(c *gc.C) {
function testContextDeadline (line 83) | func testContextDeadline(c *gc.C, ctx context.Context, name string, clk ...
type otherContext (line 97) | type otherContext struct
FILE: du/diskusage.go
type DiskUsage (line 13) | type DiskUsage struct
method Free (line 27) | func (this *DiskUsage) Free() uint64 {
method Available (line 32) | func (this *DiskUsage) Available() uint64 {
method Size (line 37) | func (this *DiskUsage) Size() uint64 {
method Used (line 42) | func (this *DiskUsage) Used() uint64 {
method Usage (line 47) | func (this *DiskUsage) Usage() float32 {
function NewDiskUsage (line 19) | func NewDiskUsage(volumePath string) *DiskUsage {
FILE: du/diskusage_windows.go
type DiskUsage (line 14) | type DiskUsage struct
method Free (line 39) | func (this *DiskUsage) Free() uint64 {
method Available (line 44) | func (this *DiskUsage) Available() uint64 {
method Size (line 49) | func (this *DiskUsage) Size() uint64 {
method Used (line 54) | func (this *DiskUsage) Used() uint64 {
method Usage (line 59) | func (this *DiskUsage) Usage() float32 {
function NewDiskUsage (line 22) | func NewDiskUsage(volumePath string) *DiskUsage {
FILE: errors.go
type RcPassthroughError (line 12) | type RcPassthroughError struct
method Error (line 17) | func (e *RcPassthroughError) Error() string {
function IsRcPassthroughError (line 22) | func IsRcPassthroughError(err error) bool {
function NewRcPassthroughError (line 30) | func NewRcPassthroughError(code int) error {
FILE: exec/exec.go
type RunParams (line 32) | type RunParams struct
method Run (line 129) | func (r *RunParams) Run() error {
method Process (line 176) | func (r *RunParams) Process() *os.Process {
method Wait (line 187) | func (r *RunParams) Wait() (*ExecResponse, error) {
method WaitWithCancel (line 231) | func (r *RunParams) WaitWithCancel(cancel <-chan struct{}) (*ExecRespo...
type ExecResponse (line 48) | type ExecResponse struct
function mergeEnvironment (line 59) | func mergeEnvironment(env []string) []string {
function shellAndArgs (line 85) | func shellAndArgs(tempDir, script, user string) (string, []string, error) {
constant timeWaitForKill (line 220) | timeWaitForKill = 30 * time.Second
type resultWithError (line 222) | type resultWithError struct
function RunCommands (line 272) | func RunCommands(run RunParams) (*ExecResponse, error) {
FILE: exec/exec_internal_test.go
type execSuite (line 16) | type execSuite struct
method TestShellAndArgsNoUserSpecified (line 22) | func (*execSuite) TestShellAndArgsNoUserSpecified(c *gc.C) {
method TestShellAndArgsAsUser (line 41) | func (*execSuite) TestShellAndArgsAsUser(c *gc.C) {
FILE: exec/exec_linux_test.go
constant cancelErrCode (line 15) | cancelErrCode = 0
method TestRunCommands (line 17) | func (*execSuite) TestRunCommands(c *gc.C) {
method TestExecUnknownCommand (line 91) | func (*execSuite) TestExecUnknownCommand(c *gc.C) {
FILE: exec/exec_test.go
type execSuite (line 20) | type execSuite struct
method TestWaitWithCancel (line 26) | func (*execSuite) TestWaitWithCancel(c *gc.C) {
method TestKillAbortedIfUnsuccessfull (line 46) | func (s *execSuite) TestKillAbortedIfUnsuccessfull(c *gc.C) {
type mockClock (line 76) | type mockClock struct
method After (line 81) | func (m *mockClock) After(t time.Duration) <-chan time.Time {
FILE: exec/exec_unix.go
function KillProcess (line 20) | func KillProcess(proc *os.Process) error {
method populateSysProcAttr (line 30) | func (r *RunParams) populateSysProcAttr() {
FILE: exec/exec_windows.go
function KillProcess (line 15) | func KillProcess(proc *os.Process) error {
method populateSysProcAttr (line 20) | func (r *RunParams) populateSysProcAttr() {}
FILE: exec/exec_windows_test.go
constant cancelErrCode (line 18) | cancelErrCode = 1
function longPath (line 22) | func longPath(path string) ([]uint16, error) {
function longPathAsString (line 45) | func longPathAsString(path string) (string, error) {
method TestRunCommands (line 53) | func (*execSuite) TestRunCommands(c *gc.C) {
method TestExecUnknownCommand (line 123) | func (*execSuite) TestExecUnknownCommand(c *gc.C) {
FILE: exec/package_test.go
function Test (line 12) | func Test(t *testing.T) {
FILE: export_test.go
function ExposeBackoffTimerDuration (line 16) | func ExposeBackoffTimerDuration(bot *BackoffTimer) time.Duration {
FILE: file.go
function UserHomeDir (line 20) | func UserHomeDir(userName string) (hDir string, err error) {
function NormalizePath (line 44) | func NormalizePath(dir string) (string, error) {
function ExpandPath (line 57) | func ExpandPath(path string) (string, error) {
function EnsureBaseDir (line 68) | func EnsureBaseDir(baseDir, path string) string {
function JoinServerPath (line 79) | func JoinServerPath(elem ...string) string {
function UniqueDirectory (line 85) | func UniqueDirectory(path, name string) (string, error) {
function CopyFile (line 103) | func CopyFile(dest, source string) error {
function AtomicWriteFileAndChange (line 120) | func AtomicWriteFileAndChange(filename string, contents []byte, change f...
function AtomicWriteFile (line 157) | func AtomicWriteFile(filename string, contents []byte, perms os.FileMode...
FILE: file_test.go
type fileSuite (line 20) | type fileSuite struct
method TestNormalizePath (line 26) | func (*fileSuite) TestNormalizePath(c *gc.C) {
method TestExpandPath (line 76) | func (*fileSuite) TestExpandPath(c *gc.C) {
method TestCopyFile (line 137) | func (*fileSuite) TestCopyFile(c *gc.C) {
method TestAtomicWriteFile (line 197) | func (*fileSuite) TestAtomicWriteFile(c *gc.C) {
method TestMoveFile (line 248) | func (*fileSuite) TestMoveFile(c *gc.C) {
FILE: file_unix.go
function homeDir (line 20) | func homeDir(userName string) (string, error) {
function MoveFile (line 34) | func MoveFile(source, destination string) (bool, error) {
function ReplaceFile (line 49) | func ReplaceFile(source, destination string) error {
function MakeFileURL (line 54) | func MakeFileURL(in string) string {
function ChownPath (line 63) | func ChownPath(path, username string) error {
function IsFileOwner (line 81) | func IsFileOwner(path, username string) (bool, error) {
FILE: file_unix_test.go
type unixFileSuite (line 22) | type unixFileSuite struct
method TestEnsureBaseDir (line 27) | func (s *unixFileSuite) TestEnsureBaseDir(c *gc.C) {
method TestFileOwner (line 33) | func (s *unixFileSuite) TestFileOwner(c *gc.C) {
method TestFileOwnerUsingRoot (line 46) | func (s *unixFileSuite) TestFileOwnerUsingRoot(c *gc.C) {
method TestFileOwnerWithInvalidPath (line 56) | func (s *unixFileSuite) TestFileOwnerWithInvalidPath(c *gc.C) {
method TestFileOwnerWithInvalidUsername (line 66) | func (s *unixFileSuite) TestFileOwnerWithInvalidUsername(c *gc.C) {
FILE: file_windows.go
constant movefile_replace_existing (line 20) | movefile_replace_existing = 0x1
constant movefile_write_through (line 21) | movefile_write_through = 0x8
function MoveFile (line 29) | func MoveFile(source, destination string) (bool, error) {
function ReplaceFile (line 49) | func ReplaceFile(source, destination string) error {
function MakeFileURL (line 67) | func MakeFileURL(in string) string {
function getUserSID (line 81) | func getUserSID(username string) (string, error) {
function readRegString (line 90) | func readRegString(h syscall.Handle, key string) (value string, err erro...
function homeFromRegistry (line 108) | func homeFromRegistry(sid string) (string, error) {
function homeDir (line 129) | func homeDir(user string) (string, error) {
function ChownPath (line 138) | func ChownPath(path, username string) error {
function IsFileOwner (line 146) | func IsFileOwner(path, username string) (bool, error) {
FILE: file_windows_test.go
type windowsFileSuite (line 16) | type windowsFileSuite struct
method TestMakeFileURL (line 21) | func (s *windowsFileSuite) TestMakeFileURL(c *gc.C) {
method TestEnsureBaseDir (line 45) | func (s *windowsFileSuite) TestEnsureBaseDir(c *gc.C) {
method TestFileOwner (line 53) | func (s *windowsFileSuite) TestFileOwner(c *gc.C) {
FILE: filepath/common.go
function splitSuffix (line 6) | func splitSuffix(path string) (string, string) {
FILE: filepath/common_test.go
type commonSuite (line 15) | type commonSuite struct
method TestSplitSuffixHasSuffix (line 19) | func (s commonSuite) TestSplitSuffixHasSuffix(c *gc.C) {
method TestSplitSuffixNoSuffix (line 26) | func (s commonSuite) TestSplitSuffixNoSuffix(c *gc.C) {
method TestSplitSuffixEmpty (line 33) | func (s commonSuite) TestSplitSuffixEmpty(c *gc.C) {
method TestSplitSuffixDotFilePlain (line 40) | func (s commonSuite) TestSplitSuffixDotFilePlain(c *gc.C) {
method TestSplitSuffixDofileWithSuffix (line 47) | func (s commonSuite) TestSplitSuffixDofileWithSuffix(c *gc.C) {
FILE: filepath/filepath.go
type Renderer (line 21) | type Renderer interface
function NewRenderer (line 71) | func NewRenderer(os string) (Renderer, error) {
FILE: filepath/filepath_test.go
type filepathSuite (line 18) | type filepathSuite struct
method SetupTest (line 27) | func (s *filepathSuite) SetupTest(c *gc.C) {
method checkRenderer (line 34) | func (s filepathSuite) checkRenderer(c *gc.C, renderer filepath.Render...
method TestNewRendererDefault (line 45) | func (s filepathSuite) TestNewRendererDefault(c *gc.C) {
method TestNewRendererGOOS (line 58) | func (s filepathSuite) TestNewRendererGOOS(c *gc.C) {
method TestNewRendererWindows (line 71) | func (s filepathSuite) TestNewRendererWindows(c *gc.C) {
method TestNewRendererUnix (line 78) | func (s filepathSuite) TestNewRendererUnix(c *gc.C) {
method TestNewRendererDistros (line 88) | func (s filepathSuite) TestNewRendererDistros(c *gc.C) {
method TestNewRendererUnknown (line 99) | func (s filepathSuite) TestNewRendererUnknown(c *gc.C) {
FILE: filepath/package_test.go
function Test (line 12) | func Test(t *testing.T) {
FILE: filepath/stdlib.go
function Base (line 16) | func Base(sep uint8, volumeName func(string) string, path string) string {
type lazybuf (line 45) | type lazybuf struct
method index (line 53) | func (b *lazybuf) index(i int) byte {
method append (line 60) | func (b *lazybuf) append(c byte) {
method string (line 73) | func (b *lazybuf) string() string {
function Clean (line 81) | func Clean(sep uint8, volumeName func(string) string, path string) string {
function Dir (line 156) | func Dir(sep uint8, volumeName func(string) string, path string) string {
function Ext (line 167) | func Ext(sep uint8, path string) string {
function FromSlash (line 177) | func FromSlash(sep uint8, path string) string {
function Join (line 185) | func Join(sep uint8, volumeName func(string) string, elem ...string) str...
function Split (line 195) | func Split(sep uint8, volumeName func(string) string, path string) (dir,...
function ToSlash (line 205) | func ToSlash(sep uint8, path string) string {
FILE: filepath/stdlib_test.go
type stdlibSuite (line 21) | type stdlibSuite struct
method SetUpTest (line 30) | func (s *stdlibSuite) SetUpTest(c *gc.C) {
method TestBase (line 45) | func (s stdlibSuite) TestBase(c *gc.C) {
method TestClean (line 53) | func (s stdlibSuite) TestClean(c *gc.C) {
method TestDir (line 68) | func (s stdlibSuite) TestDir(c *gc.C) {
method TestExt (line 81) | func (s stdlibSuite) TestExt(c *gc.C) {
method TestFromSlash (line 89) | func (s stdlibSuite) TestFromSlash(c *gc.C) {
method TestJoin (line 98) | func (s stdlibSuite) TestJoin(c *gc.C) {
method TestSplit (line 107) | func (s stdlibSuite) TestSplit(c *gc.C) {
method TestToSlash (line 122) | func (s stdlibSuite) TestToSlash(c *gc.C) {
method TestMatchTrue (line 130) | func (s stdlibSuite) TestMatchTrue(c *gc.C) {
method TestMatchFalse (line 152) | func (s stdlibSuite) TestMatchFalse(c *gc.C) {
method TestMatchBadPattern (line 172) | func (s stdlibSuite) TestMatchBadPattern(c *gc.C) {
FILE: filepath/stdlibmatch.go
function Match (line 42) | func Match(sep uint8, pattern, name string) (matched bool, err error) {
function scanChunk (line 89) | func scanChunk(sep uint8, pattern string) (star bool, chunk, rest string) {
function matchChunk (line 122) | func matchChunk(sep uint8, chunk, s string) (rest string, ok bool, err e...
function getEsc (line 201) | func getEsc(sep uint8, chunk string) (r rune, nchunk string, err error) {
FILE: filepath/unix.go
constant UnixSeparator (line 13) | UnixSeparator = '/'
constant UnixListSeparator (line 14) | UnixListSeparator = ':'
type UnixRenderer (line 18) | type UnixRenderer struct
method Base (line 21) | func (ur UnixRenderer) Base(path string) string {
method Clean (line 26) | func (ur UnixRenderer) Clean(path string) string {
method Dir (line 31) | func (ur UnixRenderer) Dir(path string) string {
method Ext (line 36) | func (UnixRenderer) Ext(path string) string {
method FromSlash (line 41) | func (UnixRenderer) FromSlash(path string) string {
method IsAbs (line 46) | func (UnixRenderer) IsAbs(path string) bool {
method Join (line 51) | func (ur UnixRenderer) Join(path ...string) string {
method Match (line 56) | func (UnixRenderer) Match(pattern, name string) (matched bool, err err...
method Split (line 61) | func (ur UnixRenderer) Split(path string) (dir, file string) {
method SplitList (line 66) | func (UnixRenderer) SplitList(path string) []string {
method ToSlash (line 74) | func (UnixRenderer) ToSlash(path string) string {
method VolumeName (line 79) | func (UnixRenderer) VolumeName(path string) string {
method NormCase (line 84) | func (UnixRenderer) NormCase(path string) string {
method SplitSuffix (line 89) | func (UnixRenderer) SplitSuffix(path string) (string, string) {
FILE: filepath/unix_test.go
type unixBaseSuite (line 19) | type unixBaseSuite struct
method SetUpTest (line 26) | func (s *unixBaseSuite) SetUpTest(c *gc.C) {
method matchesRuntime (line 33) | func (s *unixBaseSuite) matchesRuntime() bool {
type unixSuite (line 37) | type unixSuite struct
method TestIsAbs (line 41) | func (s unixSuite) TestIsAbs(c *gc.C) {
method TestSplitList (line 50) | func (s unixSuite) TestSplitList(c *gc.C) {
method TestVolumeName (line 60) | func (s unixSuite) TestVolumeName(c *gc.C) {
method TestNormCaseLower (line 66) | func (s unixSuite) TestNormCaseLower(c *gc.C) {
method TestNormCaseUpper (line 72) | func (s unixSuite) TestNormCaseUpper(c *gc.C) {
method TestNormCaseMixed (line 78) | func (s unixSuite) TestNormCaseMixed(c *gc.C) {
method TestNormCaseCapitalized (line 84) | func (s unixSuite) TestNormCaseCapitalized(c *gc.C) {
method TestNormCasePunctuation (line 90) | func (s unixSuite) TestNormCasePunctuation(c *gc.C) {
method TestSplitSuffix (line 96) | func (s unixSuite) TestSplitSuffix(c *gc.C) {
type unixThinWrapperSuite (line 109) | type unixThinWrapperSuite struct
method TestBase (line 113) | func (s unixThinWrapperSuite) TestBase(c *gc.C) {
method TestClean (line 123) | func (s unixThinWrapperSuite) TestClean(c *gc.C) {
method TestDir (line 140) | func (s unixThinWrapperSuite) TestDir(c *gc.C) {
method TestExt (line 150) | func (s unixThinWrapperSuite) TestExt(c *gc.C) {
method TestFromSlash (line 160) | func (s unixThinWrapperSuite) TestFromSlash(c *gc.C) {
method TestJoin (line 171) | func (s unixThinWrapperSuite) TestJoin(c *gc.C) {
method TestSplit (line 181) | func (s unixThinWrapperSuite) TestSplit(c *gc.C) {
method TestToSlash (line 193) | func (s unixThinWrapperSuite) TestToSlash(c *gc.C) {
method TestMatchTrue (line 203) | func (s unixThinWrapperSuite) TestMatchTrue(c *gc.C) {
method TestMatchFalse (line 227) | func (s unixThinWrapperSuite) TestMatchFalse(c *gc.C) {
method TestMatchBadPattern (line 249) | func (s unixThinWrapperSuite) TestMatchBadPattern(c *gc.C) {
FILE: filepath/win.go
constant WindowsSeparator (line 13) | WindowsSeparator = '\\'
constant WindowsListSeparator (line 14) | WindowsListSeparator = ';'
type WindowsRenderer (line 18) | type WindowsRenderer struct
method Base (line 21) | func (ur WindowsRenderer) Base(path string) string {
method Clean (line 26) | func (ur WindowsRenderer) Clean(path string) string {
method Dir (line 31) | func (ur WindowsRenderer) Dir(path string) string {
method Ext (line 36) | func (WindowsRenderer) Ext(path string) string {
method FromSlash (line 41) | func (WindowsRenderer) FromSlash(path string) string {
method IsAbs (line 46) | func (WindowsRenderer) IsAbs(path string) bool {
method Join (line 59) | func (ur WindowsRenderer) Join(path ...string) string {
method Match (line 64) | func (WindowsRenderer) Match(pattern, name string) (matched bool, err ...
method Split (line 69) | func (ur WindowsRenderer) Split(path string) (dir, file string) {
method SplitList (line 74) | func (WindowsRenderer) SplitList(path string) []string {
method ToSlash (line 105) | func (WindowsRenderer) ToSlash(path string) string {
method VolumeName (line 110) | func (WindowsRenderer) VolumeName(path string) string {
method NormCase (line 115) | func (WindowsRenderer) NormCase(path string) string {
method SplitSuffix (line 120) | func (WindowsRenderer) SplitSuffix(path string) (string, string) {
function isSlash (line 124) | func isSlash(c uint8) bool {
function volumeNameLen (line 130) | func volumeNameLen(path string) int {
FILE: filepath/win_test.go
type windowsBaseSuite (line 19) | type windowsBaseSuite struct
method SetUpTest (line 26) | func (s *windowsBaseSuite) SetUpTest(c *gc.C) {
method matchesRuntime (line 33) | func (s *windowsBaseSuite) matchesRuntime() bool {
type windowsSuite (line 37) | type windowsSuite struct
method TestIsAbs (line 41) | func (s windowsSuite) TestIsAbs(c *gc.C) {
method TestSplitList (line 50) | func (s windowsSuite) TestSplitList(c *gc.C) {
method TestVolumeName (line 60) | func (s windowsSuite) TestVolumeName(c *gc.C) {
method TestNormCaseLower (line 70) | func (s windowsSuite) TestNormCaseLower(c *gc.C) {
method TestNormCaseUpper (line 76) | func (s windowsSuite) TestNormCaseUpper(c *gc.C) {
method TestNormCaseMixed (line 82) | func (s windowsSuite) TestNormCaseMixed(c *gc.C) {
method TestNormCaseCapitalized (line 88) | func (s windowsSuite) TestNormCaseCapitalized(c *gc.C) {
method TestNormCasePunctuation (line 94) | func (s windowsSuite) TestNormCasePunctuation(c *gc.C) {
method TestSplitSuffix (line 100) | func (s windowsSuite) TestSplitSuffix(c *gc.C) {
type windowsThinWrapperSuite (line 113) | type windowsThinWrapperSuite struct
method TestBase (line 117) | func (s windowsThinWrapperSuite) TestBase(c *gc.C) {
method TestClean (line 127) | func (s windowsThinWrapperSuite) TestClean(c *gc.C) {
method TestDir (line 144) | func (s windowsThinWrapperSuite) TestDir(c *gc.C) {
method TestExt (line 154) | func (s windowsThinWrapperSuite) TestExt(c *gc.C) {
method TestFromSlash (line 164) | func (s windowsThinWrapperSuite) TestFromSlash(c *gc.C) {
method TestJoin (line 175) | func (s windowsThinWrapperSuite) TestJoin(c *gc.C) {
method TestSplit (line 185) | func (s windowsThinWrapperSuite) TestSplit(c *gc.C) {
method TestToSlash (line 197) | func (s windowsThinWrapperSuite) TestToSlash(c *gc.C) {
method TestMatchTrue (line 207) | func (s windowsThinWrapperSuite) TestMatchTrue(c *gc.C) {
method TestMatchFalse (line 231) | func (s windowsThinWrapperSuite) TestMatchFalse(c *gc.C) {
method TestMatchBadPattern (line 253) | func (s windowsThinWrapperSuite) TestMatchBadPattern(c *gc.C) {
FILE: filestorage/fakes_test.go
type FakeMetadataStorage (line 18) | type FakeMetadataStorage struct
method Check (line 31) | func (s *FakeMetadataStorage) Check(c *gc.C, id string, meta filestora...
method Doc (line 37) | func (s *FakeMetadataStorage) Doc(id string) (filestorage.Document, er...
method ListDocs (line 46) | func (s *FakeMetadataStorage) ListDocs() ([]filestorage.Document, erro...
method AddDoc (line 58) | func (s *FakeMetadataStorage) AddDoc(doc filestorage.Document) (string...
method RemoveDoc (line 68) | func (s *FakeMetadataStorage) RemoveDoc(id string) error {
method Close (line 74) | func (s *FakeMetadataStorage) Close() error {
method Metadata (line 79) | func (s *FakeMetadataStorage) Metadata(id string) (filestorage.Metadat...
method ListMetadata (line 88) | func (s *FakeMetadataStorage) ListMetadata() ([]filestorage.Metadata, ...
method AddMetadata (line 96) | func (s *FakeMetadataStorage) AddMetadata(meta filestorage.Metadata) (...
method RemoveMetadata (line 105) | func (s *FakeMetadataStorage) RemoveMetadata(id string) error {
method SetStored (line 111) | func (s *FakeMetadataStorage) SetStored(id string) error {
type FakeRawFileStorage (line 118) | type FakeRawFileStorage struct
method Check (line 130) | func (s *FakeRawFileStorage) Check(c *gc.C, id string, file io.Reader,...
method CheckNotUsed (line 138) | func (s *FakeRawFileStorage) CheckNotUsed(c *gc.C) {
method File (line 142) | func (s *FakeRawFileStorage) File(id string) (io.ReadCloser, error) {
method AddFile (line 151) | func (s *FakeRawFileStorage) AddFile(id string, file io.Reader, size i...
method RemoveFile (line 159) | func (s *FakeRawFileStorage) RemoveFile(id string) error {
method Close (line 165) | func (s *FakeRawFileStorage) Close() error {
FILE: filestorage/interfaces.go
type FileStorage (line 12) | type FileStorage interface
type Document (line 36) | type Document interface
type Metadata (line 46) | type Metadata interface
type DocStorage (line 74) | type DocStorage interface
type RawFileStorage (line 98) | type RawFileStorage interface
type MetadataStorage (line 118) | type MetadataStorage interface
FILE: filestorage/metadata.go
type RawDoc (line 13) | type RawDoc struct
type Doc (line 19) | type Doc struct
method ID (line 24) | func (d *Doc) ID() string {
method SetID (line 30) | func (d *Doc) SetID(id string) bool {
type RawFileMetadata (line 39) | type RawFileMetadata struct
type FileMetadata (line 51) | type FileMetadata struct
method Size (line 62) | func (m *FileMetadata) Size() int64 {
method Checksum (line 66) | func (m *FileMetadata) Checksum() string {
method ChecksumFormat (line 70) | func (m *FileMetadata) ChecksumFormat() string {
method Stored (line 74) | func (m *FileMetadata) Stored() *time.Time {
method SetFileInfo (line 78) | func (m *FileMetadata) SetFileInfo(size int64, checksum, format string...
method SetStored (line 113) | func (m *FileMetadata) SetStored(timestamp *time.Time) {
function NewMetadata (line 57) | func NewMetadata() *FileMetadata {
FILE: filestorage/metadata_store.go
function Convert (line 11) | func Convert(doc Document) (Metadata, error) {
type MetadataDocStorage (line 23) | type MetadataDocStorage struct
method Metadata (line 28) | func (s *MetadataDocStorage) Metadata(id string) (Metadata, error) {
method ListMetadata (line 38) | func (s *MetadataDocStorage) ListMetadata() ([]Metadata, error) {
method AddMetadata (line 58) | func (s *MetadataDocStorage) AddMetadata(meta Metadata) (string, error) {
method RemoveMetadata (line 64) | func (s *MetadataDocStorage) RemoveMetadata(id string) error {
FILE: filestorage/metadata_test.go
type MetadataSuite (line 22) | type MetadataSuite struct
method TestFileMetadataNewMetadata (line 26) | func (s *MetadataSuite) TestFileMetadataNewMetadata(c *gc.C) {
method TestFileMetadataSetIDInitial (line 36) | func (s *MetadataSuite) TestFileMetadataSetIDInitial(c *gc.C) {
method TestFileMetadataSetIDAlreadySetSame (line 46) | func (s *MetadataSuite) TestFileMetadataSetIDAlreadySetSame(c *gc.C) {
method TestFileMetadataSetIDAlreadySetDifferent (line 57) | func (s *MetadataSuite) TestFileMetadataSetIDAlreadySetDifferent(c *gc...
method TestFileMetadataSetFileInfo (line 68) | func (s *MetadataSuite) TestFileMetadataSetFileInfo(c *gc.C) {
method TestFileMetadataSetStored (line 82) | func (s *MetadataSuite) TestFileMetadataSetStored(c *gc.C) {
method TestFileMetadataSetStoredDefault (line 90) | func (s *MetadataSuite) TestFileMetadataSetStoredDefault(c *gc.C) {
FILE: filestorage/package_test.go
function TestPackage (line 12) | func TestPackage(t *testing.T) {
FILE: filestorage/wrapper.go
type fileStorage (line 15) | type fileStorage struct
method Metadata (line 37) | func (s *fileStorage) Metadata(id string) (Metadata, error) {
method Get (line 49) | func (s *fileStorage) Get(id string) (Metadata, io.ReadCloser, error) {
method List (line 65) | func (s *fileStorage) List() ([]Metadata, error) {
method addFile (line 69) | func (s *fileStorage) addFile(id string, size int64, file io.Reader) e...
method Add (line 90) | func (s *fileStorage) Add(meta Metadata, file io.Reader) (string, erro...
method SetFile (line 118) | func (s *fileStorage) SetFile(id string, file io.Reader) error {
method Remove (line 137) | func (s *fileStorage) Remove(id string) error {
method Close (line 150) | func (s *fileStorage) Close() error {
function NewFileStorage (line 27) | func NewFileStorage(meta MetadataStorage, files RawFileStorage) FileStor...
FILE: filestorage/wrapper_test.go
type WrapperSuite (line 21) | type WrapperSuite struct
method SetUpTest (line 28) | func (s *WrapperSuite) SetUpTest(c *gc.C) {
method metadata (line 36) | func (s *WrapperSuite) metadata() filestorage.Metadata {
method setMeta (line 42) | func (s *WrapperSuite) setMeta() (string, filestorage.Metadata) {
method setFile (line 51) | func (s *WrapperSuite) setFile(data string) (string, filestorage.Metad...
method TestFileStorageNewFileStorage (line 59) | func (s *WrapperSuite) TestFileStorageNewFileStorage(c *gc.C) {
method TestFileStorageMetadata (line 65) | func (s *WrapperSuite) TestFileStorageMetadata(c *gc.C) {
method TestFileStorageGet (line 75) | func (s *WrapperSuite) TestFileStorageGet(c *gc.C) {
method TestFileStorageListEmpty (line 84) | func (s *WrapperSuite) TestFileStorageListEmpty(c *gc.C) {
method TestFileStorageListOne (line 91) | func (s *WrapperSuite) TestFileStorageListOne(c *gc.C) {
method TestFileStorageListTwo (line 101) | func (s *WrapperSuite) TestFileStorageListTwo(c *gc.C) {
method TestFileStorageAddMeta (line 117) | func (s *WrapperSuite) TestFileStorageAddMeta(c *gc.C) {
method TestFileStorageAddFile (line 131) | func (s *WrapperSuite) TestFileStorageAddFile(c *gc.C) {
method TestFileStorageAddIDNotSet (line 148) | func (s *WrapperSuite) TestFileStorageAddIDNotSet(c *gc.C) {
method TestFileStorageAddMetaOnly (line 157) | func (s *WrapperSuite) TestFileStorageAddMetaOnly(c *gc.C) {
method TestFileStorageAddIDAlreadySet (line 166) | func (s *WrapperSuite) TestFileStorageAddIDAlreadySet(c *gc.C) {
method TestFileStorageAddFileFailureDropsMetadata (line 174) | func (s *WrapperSuite) TestFileStorageAddFileFailureDropsMetadata(c *g...
method TestFileStorageSetFile (line 189) | func (s *WrapperSuite) TestFileStorageSetFile(c *gc.C) {
method TestFileStorageRemove (line 202) | func (s *WrapperSuite) TestFileStorageRemove(c *gc.C) {
method TestClose (line 211) | func (s *WrapperSuite) TestClose(c *gc.C) {
FILE: fs/copy.go
function Copy (line 19) | func Copy(src, dst string) error {
function copySymLink (line 44) | func copySymLink(src, dst string) error {
function copyFile (line 52) | func copyFile(src, dst string, mode os.FileMode) error {
function copyDir (line 74) | func copyDir(src, dst string, mode os.FileMode) error {
FILE: fs/copy_test.go
type copySuite (line 16) | type copySuite struct
method TestCopy (line 70) | func (*copySuite) TestCopy(c *gc.C) {
function TestPackage (line 20) | func TestPackage(t *testing.T) {
FILE: gomaxprocs.go
function UseMultipleCPUs (line 16) | func UseMultipleCPUs() {
FILE: gomaxprocs_test.go
type gomaxprocsSuite (line 16) | type gomaxprocsSuite struct
method SetUpTest (line 25) | func (s *gomaxprocsSuite) SetUpTest(c *gc.C) {
method TestUseMultipleCPUsDoesNothingWhenGOMAXPROCSSet (line 40) | func (s *gomaxprocsSuite) TestUseMultipleCPUsDoesNothingWhenGOMAXPROCS...
method TestUseMultipleCPUsWhenEnabled (line 47) | func (s *gomaxprocsSuite) TestUseMultipleCPUsWhenEnabled(c *gc.C) {
FILE: hash/fingerprint.go
type Fingerprint (line 16) | type Fingerprint struct
method String (line 102) | func (fp Fingerprint) String() string {
method Hex (line 107) | func (fp Fingerprint) Hex() string {
method Base64 (line 112) | func (fp Fingerprint) Base64() string {
method Bytes (line 117) | func (fp Fingerprint) Bytes() []byte {
method IsZero (line 122) | func (fp Fingerprint) IsZero() bool {
method Validate (line 127) | func (fp Fingerprint) Validate() error {
function NewFingerprint (line 22) | func NewFingerprint(sum []byte, validate func([]byte) error) (Fingerprin...
function NewValidFingerprint (line 35) | func NewValidFingerprint(hash hash.Hash) Fingerprint {
function newFingerprint (line 40) | func newFingerprint(sum []byte) Fingerprint {
function GenerateFingerprint (line 47) | func GenerateFingerprint(reader io.Reader, newHash func() hash.Hash) (Fi...
function ParseHexFingerprint (line 67) | func ParseHexFingerprint(hexSum string, validate func([]byte) error) (Fi...
function ParseBase64Fingerprint (line 85) | func ParseBase64Fingerprint(b64Sum string, validate func([]byte) error) ...
FILE: hash/fingerprint_test.go
type FingerprintSuite (line 22) | type FingerprintSuite struct
method SetUpTest (line 27) | func (s *FingerprintSuite) SetUpTest(c *gc.C) {
method newHash (line 32) | func (s *FingerprintSuite) newHash() stdhash.Hash {
method validate (line 39) | func (s *FingerprintSuite) validate(sum []byte) error {
method TestNewFingerprintOkay (line 48) | func (s *FingerprintSuite) TestNewFingerprintOkay(c *gc.C) {
method TestNewFingerprintInvalid (line 59) | func (s *FingerprintSuite) TestNewFingerprintInvalid(c *gc.C) {
method TestNewValidFingerprint (line 70) | func (s *FingerprintSuite) TestNewValidFingerprint(c *gc.C) {
method TestGenerateFingerprintOkay (line 81) | func (s *FingerprintSuite) TestGenerateFingerprintOkay(c *gc.C) {
method TestGenerateFingerprintNil (line 95) | func (s *FingerprintSuite) TestGenerateFingerprintNil(c *gc.C) {
method TestParseHexFingerprint (line 102) | func (s *FingerprintSuite) TestParseHexFingerprint(c *gc.C) {
method TestString (line 113) | func (s *FingerprintSuite) TestString(c *gc.C) {
method TestHex (line 123) | func (s *FingerprintSuite) TestHex(c *gc.C) {
method TestBytes (line 133) | func (s *FingerprintSuite) TestBytes(c *gc.C) {
method TestValidateOkay (line 143) | func (s *FingerprintSuite) TestValidateOkay(c *gc.C) {
method TestValidateZero (line 153) | func (s *FingerprintSuite) TestValidateZero(c *gc.C) {
function newFingerprint (line 161) | func newFingerprint(c *gc.C, data string) ([]byte, string) {
FILE: hash/hash.go
function SHA384 (line 57) | func SHA384() (newHash func() hash.Hash, validate func([]byte) error) {
function newSizeChecker (line 63) | func newSizeChecker(size int) func([]byte) error {
FILE: hash/hash_test.go
type HashSuite (line 22) | type HashSuite struct
method TestHashingWriter (line 26) | func (s *HashSuite) TestHashingWriter(c *gc.C) {
method TestHashingReader (line 43) | func (s *HashSuite) TestHashingReader(c *gc.C) {
type fakeStream (line 68) | type fakeStream struct
method Read (line 73) | func (f *fakeStream) Read(data []byte) (int, error) {
FILE: hash/package_test.go
function Test (line 12) | func Test(t *stdtesting.T) {
FILE: hash/writer.go
type HashingWriter (line 20) | type HashingWriter struct
method Base64Sum (line 43) | func (hw HashingWriter) Base64Sum() string {
method Write (line 49) | func (hw *HashingWriter) Write(data []byte) (int, error) {
function NewHashingWriter (line 35) | func NewHashingWriter(writer io.Writer, hasher hash.Hash) *HashingWriter {
FILE: hash/writer_test.go
type WriterSuite (line 20) | type WriterSuite struct
method SetUpTest (line 30) | func (s *WriterSuite) SetUpTest(c *gc.C) {
method TestHashingWriterWriteEmpty (line 43) | func (s *WriterSuite) TestHashingWriterWriteEmpty(c *gc.C) {
method TestHashingWriterWriteSmall (line 54) | func (s *WriterSuite) TestHashingWriterWriteSmall(c *gc.C) {
method TestHashingWriterWriteFileError (line 65) | func (s *WriterSuite) TestHashingWriterWriteFileError(c *gc.C) {
method TestHashingWriterBase64Sum (line 76) | func (s *WriterSuite) TestHashingWriterBase64Sum(c *gc.C) {
FILE: home_unix.go
function Home (line 16) | func Home() string {
function SetHome (line 26) | func SetHome(s string) error {
FILE: home_unix_test.go
type homeSuite (line 15) | type homeSuite struct
method TestHomeLinux (line 21) | func (s *homeSuite) TestHomeLinux(c *gc.C) {
method TestHomeConfined (line 27) | func (s *homeSuite) TestHomeConfined(c *gc.C) {
FILE: home_windows.go
function Home (line 12) | func Home() string {
function SetHome (line 17) | func SetHome(s string) error {
FILE: home_windows_test.go
type homeSuite (line 15) | type homeSuite struct
method TestHome (line 21) | func (s *homeSuite) TestHome(c *gc.C) {
FILE: isubuntu.go
function IsUbuntu (line 11) | func IsUbuntu() bool {
FILE: isubuntu_test.go
type IsUbuntuSuite (line 17) | type IsUbuntuSuite struct
method patchLsbRelease (line 23) | func (s *IsUbuntuSuite) patchLsbRelease(c *gc.C, name string) {
method TestIsUbuntu (line 36) | func (s *IsUbuntuSuite) TestIsUbuntu(c *gc.C) {
method TestIsNotUbuntu (line 41) | func (s *IsUbuntuSuite) TestIsNotUbuntu(c *gc.C) {
method TestIsNotUbuntuLsbReleaseNotFound (line 46) | func (s *IsUbuntuSuite) TestIsNotUbuntuLsbReleaseNotFound(c *gc.C) {
FILE: jsonhttp/jsonhttp.go
type ErrorToResponse (line 19) | type ErrorToResponse
type ErrorHandler (line 25) | type ErrorHandler
function HandleErrors (line 30) | func HandleErrors(errToResp ErrorToResponse) func(handle ErrorHandler) h...
type responseWriter (line 56) | type responseWriter struct
method Write (line 61) | func (w *responseWriter) Write(data []byte) (int, error) {
method WriteHeader (line 66) | func (w *responseWriter) WriteHeader(code int) {
method Flush (line 72) | func (w *responseWriter) Flush() {
function WriteError (line 85) | func WriteError(errToResp ErrorToResponse) func(w http.ResponseWriter, e...
function WriteJSON (line 94) | func WriteJSON(w http.ResponseWriter, code int, val any) error {
type JSONHandler (line 115) | type JSONHandler
function HandleJSON (line 121) | func HandleJSON(errToResp ErrorToResponse) func(handle JSONHandler) http...
FILE: jsonhttp/jsonhttp_test.go
type suite (line 16) | type suite struct
method TestWriteJSON (line 20) | func (*suite) TestWriteJSON(c *gc.C) {
method TestWriteError (line 86) | func (s *suite) TestWriteError(c *gc.C) {
method TestHandleErrors (line 105) | func (s *suite) TestHandleErrors(c *gc.C) {
method TestHandleErrorsWithErrorAfterWriteHeader (line 152) | func (s *suite) TestHandleErrorsWithErrorAfterWriteHeader(c *gc.C) {
method TestHandleJSON (line 167) | func (s *suite) TestHandleJSON(c *gc.C) {
type errorResponse (line 39) | type errorResponse struct
function errorToResponse (line 43) | func errorToResponse(err error) (int, any) {
function parseErrorResponse (line 98) | func parseErrorResponse(c *gc.C, body []byte) *errorResponse {
FILE: jsonhttp/package_test.go
function TestPackage (line 12) | func TestPackage(t *testing.T) {
FILE: keyvalues/keyvalues.go
type DuplicateError (line 16) | type DuplicateError
method Error (line 18) | func (e DuplicateError) Error() string {
function Parse (line 24) | func Parse(src []string, allowEmptyValues bool) (map[string]string, erro...
FILE: keyvalues/keyvalues_test.go
type keyValuesSuite (line 12) | type keyValuesSuite struct
method TestMapParsing (line 126) | func (keyValuesSuite) TestMapParsing(c *gc.C) {
FILE: keyvalues/package_test.go
function TestPackage (line 12) | func TestPackage(t *testing.T) {
FILE: limiter.go
type empty (line 14) | type empty struct
type limiter (line 15) | type limiter struct
method Acquire (line 60) | func (l limiter) Acquire() bool {
method AcquireWait (line 76) | func (l limiter) AcquireWait() {
method Release (line 82) | func (l limiter) Release() error {
method pause (line 91) | func (l limiter) pause() {
type Limiter (line 23) | type Limiter interface
function NewLimiter (line 37) | func NewLimiter(maxAllowed int) Limiter {
function NewLimiterWithPause (line 43) | func NewLimiterWithPause(maxAllowed int, minPause, maxPause time.Duratio...
FILE: limiter_test.go
constant longWait (line 18) | longWait = 10 * time.Second
type limiterSuite (line 20) | type limiterSuite struct
method TestAcquireUntilFull (line 26) | func (*limiterSuite) TestAcquireUntilFull(c *gc.C) {
method TestBadRelease (line 33) | func (*limiterSuite) TestBadRelease(c *gc.C) {
method TestAcquireAndRelease (line 38) | func (*limiterSuite) TestAcquireAndRelease(c *gc.C) {
method TestAcquireWaitBlocksUntilRelease (line 50) | func (*limiterSuite) TestAcquireWaitBlocksUntilRelease(c *gc.C) {
method TestAcquirePauses (line 84) | func (*limiterSuite) TestAcquirePauses(c *gc.C) {
FILE: multireader.go
type SizeReaderAt (line 14) | type SizeReaderAt interface
function NewMultiReaderAt (line 29) | func NewMultiReaderAt(parts ...SizeReaderAt) SizeReaderAt {
type offsetAndSource (line 42) | type offsetAndSource struct
type multiReaderAt (line 47) | type multiReaderAt struct
method Size (line 52) | func (m *multiReaderAt) Size() int64 {
method ReadAt (line 56) | func (m *multiReaderAt) ReadAt(p []byte, off int64) (n int, err error) {
function NewMultiReaderSeeker (line 101) | func NewMultiReaderSeeker(readers ...io.ReadSeeker) io.ReadSeeker {
function newSizeReaderAt (line 121) | func newSizeReaderAt(r io.ReadSeeker) (SizeReaderAt, error) {
type sizeReaderAt (line 134) | type sizeReaderAt struct
method ReadAt (line 141) | func (r *sizeReaderAt) ReadAt(buf []byte, off int64) (n int, err error) {
method Size (line 155) | func (r *sizeReaderAt) Size() int64 {
type readSeeker (line 160) | type readSeeker struct
method Seek (line 166) | func (r *readSeeker) Seek(off int64, whence int) (int64, error) {
method Read (line 182) | func (r *readSeeker) Read(buf []byte) (int, error) {
FILE: multireader_test.go
type multiReaderSeekerSuite (line 17) | type multiReaderSeekerSuite struct
method TestSequentialRead (line 21) | func (*multiReaderSeekerSuite) TestSequentialRead(c *gc.C) {
method TestSeekStart (line 34) | func (*multiReaderSeekerSuite) TestSeekStart(c *gc.C) {
method TestSeekEnd (line 55) | func (*multiReaderSeekerSuite) TestSeekEnd(c *gc.C) {
method TestSeekCur (line 76) | func (*multiReaderSeekerSuite) TestSeekCur(c *gc.C) {
method TestSeekAfterRead (line 107) | func (*multiReaderSeekerSuite) TestSeekAfterRead(c *gc.C) {
method TestSeekNegative (line 129) | func (*multiReaderSeekerSuite) TestSeekNegative(c *gc.C) {
method TestSeekPastEnd (line 154) | func (*multiReaderSeekerSuite) TestSeekPastEnd(c *gc.C) {
type multiReaderAtSuite (line 182) | type multiReaderAtSuite struct
method TestReadComplete (line 186) | func (*multiReaderAtSuite) TestReadComplete(c *gc.C) {
method TestReadPartial (line 203) | func (*multiReaderAtSuite) TestReadPartial(c *gc.C) {
function newMultiStringReader (line 220) | func newMultiStringReader(parts []string) io.ReadSeeker {
type stringReader (line 228) | type stringReader struct
method Size (line 234) | func (r stringReader) Size() int64 {
function newMultistringReaderAt (line 238) | func newMultistringReaderAt(parts []string) io.ReaderAt {
FILE: naturalsort.go
function SortStringsNaturally (line 14) | func SortStringsNaturally(s []string) []string {
type naturally (line 19) | type naturally
method Len (line 21) | func (n naturally) Len() int {
method Swap (line 25) | func (n naturally) Swap(a, b int) {
method Less (line 31) | func (n naturally) Less(a, b int) bool {
function splitAtNumber (line 65) | func splitAtNumber(str string) (string, int, string) {
function indexOfDigit (line 79) | func indexOfDigit(str string) int {
function indexOfNonDigit (line 88) | func indexOfNonDigit(str string) int {
FILE: naturalsort_test.go
type naturalSortSuite (line 15) | type naturalSortSuite struct
method TestEmpty (line 21) | func (s *naturalSortSuite) TestEmpty(c *gc.C) {
method TestAlpha (line 25) | func (s *naturalSortSuite) TestAlpha(c *gc.C) {
method TestNumVsString (line 29) | func (s *naturalSortSuite) TestNumVsString(c *gc.C) {
method TestStringVsStringNum (line 33) | func (s *naturalSortSuite) TestStringVsStringNum(c *gc.C) {
method TestCommonPrefix (line 37) | func (s *naturalSortSuite) TestCommonPrefix(c *gc.C) {
method TestDifferentNumberLengths (line 41) | func (s *naturalSortSuite) TestDifferentNumberLengths(c *gc.C) {
method TestZeroPadding (line 45) | func (s *naturalSortSuite) TestZeroPadding(c *gc.C) {
method TestMixed (line 49) | func (s *naturalSortSuite) TestMixed(c *gc.C) {
method TestSeveralNumericParts (line 53) | func (s *naturalSortSuite) TestSeveralNumericParts(c *gc.C) {
method TestUnitNameLike (line 78) | func (s *naturalSortSuite) TestUnitNameLike(c *gc.C) {
method TestMachineIdLike (line 82) | func (s *naturalSortSuite) TestMachineIdLike(c *gc.C) {
method TestIPs (line 101) | func (s *naturalSortSuite) TestIPs(c *gc.C) {
function checkCorrectSort (line 112) | func checkCorrectSort(c *gc.C, expected []string) {
function checkSort (line 119) | func checkSort(c *gc.C, expected []string, xform func([]string)) {
function copyStrSlice (line 127) | func copyStrSlice(in []string) []string {
function shuffle (line 133) | func shuffle(a []string) {
function reverse (line 141) | func reverse(a []string) {
FILE: network.go
function GetIPv4Address (line 17) | func GetIPv4Address(addresses []net.Addr) (string, error) {
function GetIPv6Address (line 35) | func GetIPv6Address(addresses []net.Addr) (string, error) {
function GetAddressForInterface (line 51) | func GetAddressForInterface(interfaceName string) (string, error) {
function GetV4OrV6AddressForInterface (line 68) | func GetV4OrV6AddressForInterface(interfaceName string) (string, error) {
FILE: network_test.go
type networkSuite (line 15) | type networkSuite struct
method TestGetIPv4Address (line 40) | func (*networkSuite) TestGetIPv4Address(c *gc.C) {
method TestGetIPv6Address (line 79) | func (*networkSuite) TestGetIPv6Address(c *gc.C) {
type fakeAddress (line 21) | type fakeAddress struct
method Network (line 25) | func (fake fakeAddress) Network() string {
method String (line 29) | func (fake fakeAddress) String() string {
function makeAddresses (line 33) | func makeAddresses(values ...string) (result []net.Addr) {
FILE: os.go
constant OSWindows (line 8) | OSWindows = "windows"
constant OSDarwin (line 9) | OSDarwin = "darwin"
constant OSDragonfly (line 10) | OSDragonfly = "dragonfly"
constant OSFreebsd (line 11) | OSFreebsd = "freebsd"
constant OSLinux (line 12) | OSLinux = "linux"
constant OSNacl (line 13) | OSNacl = "nacl"
constant OSNetbsd (line 14) | OSNetbsd = "netbsd"
constant OSOpenbsd (line 15) | OSOpenbsd = "openbsd"
constant OSSolaris (line 16) | OSSolaris = "solaris"
function OSIsUnix (line 34) | func OSIsUnix(os string) bool {
FILE: os_test.go
type osSuite (line 16) | type osSuite struct
method TestOSIsUnixKnown (line 20) | func (osSuite) TestOSIsUnixKnown(c *gc.C) {
method TestOSIsUnixWindows (line 29) | func (osSuite) TestOSIsUnixWindows(c *gc.C) {
method TestOSIsUnixUnknown (line 35) | func (osSuite) TestOSIsUnixUnknown(c *gc.C) {
FILE: package_test.go
function TestPackage (line 12) | func TestPackage(t *testing.T) {
FILE: parallel/package_test.go
function TestPackage (line 12) | func TestPackage(t *testing.T) {
FILE: parallel/parallel.go
type Run (line 14) | type Run struct
method Do (line 53) | func (r *Run) Do(f func() error) {
method Wait (line 71) | func (r *Run) Wait() error {
method runner (line 84) | func (r *Run) runner() {
type Errors (line 23) | type Errors
method Error (line 25) | func (errs Errors) Error() string {
function NewRun (line 38) | func NewRun(max int) *Run {
FILE: parallel/parallel_test.go
type parallelSuite (line 19) | type parallelSuite struct
method TestParallelMaxPar (line 25) | func (*parallelSuite) TestParallelMaxPar(c *gc.C) {
method TestConcurrentDo (line 86) | func (*parallelSuite) TestConcurrentDo(c *gc.C) {
method TestParallelError (line 112) | func (*parallelSuite) TestParallelError(c *gc.C) {
method TestZeroWorkerPanics (line 144) | func (*parallelSuite) TestZeroWorkerPanics(c *gc.C) {
function nothing (line 64) | func nothing() error {
function BenchmarkRunSingle (line 68) | func BenchmarkRunSingle(b *stdtesting.B) {
function BenchmarkRun1000p100 (line 76) | func BenchmarkRun1000p100(b *stdtesting.B) {
type intError (line 106) | type intError
method Error (line 108) | func (intError) Error() string {
FILE: parallel/try.go
type Try (line 21) | type Try struct
method loop (line 78) | func (t *Try) loop() (io.Closer, error) {
method Start (line 122) | func (t *Try) Start(try func(stop <-chan struct{}) (io.Closer, error))...
method Close (line 163) | func (t *Try) Close() {
method Dead (line 175) | func (t *Try) Dead() <-chan struct{} {
method Wait (line 181) | func (t *Try) Wait() error {
method Result (line 191) | func (t *Try) Result() (io.Closer, error) {
method Kill (line 197) | func (t *Try) Kill() {
function NewTry (line 43) | func NewTry(maxParallel int, combineErrors func(err0, err1 error) error)...
function chooseLastError (line 69) | func chooseLastError(err0, err1 error) error {
type result (line 73) | type result struct
FILE: parallel/try_test.go
constant shortWait (line 22) | shortWait = 50 * time.Millisecond
constant longWait (line 23) | longWait = 10 * time.Second
type result (line 26) | type result
method Close (line 28) | func (r result) Close() error {
type trySuite (line 32) | type trySuite struct
method TestOneSuccess (line 45) | func (*trySuite) TestOneSuccess(c *gc.C) {
method TestOneFailure (line 53) | func (*trySuite) TestOneFailure(c *gc.C) {
method TestStartReturnsErrorAfterClose (line 74) | func (*trySuite) TestStartReturnsErrorAfterClose(c *gc.C) {
method TestOutOfOrderResults (line 89) | func (*trySuite) TestOutOfOrderResults(c *gc.C) {
method TestMaxParallel (line 98) | func (*trySuite) TestMaxParallel(c *gc.C) {
method TestStartBlocksForMaxParallel (line 129) | func (*trySuite) TestStartBlocksForMaxParallel(c *gc.C) {
method TestAllConcurrent (line 208) | func (*trySuite) TestAllConcurrent(c *gc.C) {
method TestErrorCombine (line 251) | func (*trySuite) TestErrorCombine(c *gc.C) {
method TestTriesAreStopped (line 275) | func (*trySuite) TestTriesAreStopped(c *gc.C) {
method TestCloseTwice (line 295) | func (*trySuite) TestCloseTwice(c *gc.C) {
method TestExtraResultsAreClosed (line 313) | func (*trySuite) TestExtraResultsAreClosed(c *gc.C) {
method TestEverything (line 347) | func (*trySuite) TestEverything(c *gc.C) {
function tryFunc (line 38) | func tryFunc(delay time.Duration, val io.Closer, err error) func(<-chan ...
type gradedError (line 230) | type gradedError
method Error (line 232) | func (e gradedError) Error() string {
function gradedErrorCombine (line 236) | func gradedErrorCombine(err0, err1 error) error {
type multiError (line 243) | type multiError struct
method Error (line 247) | func (e *multiError) Error() string {
type closeResult (line 304) | type closeResult struct
method Close (line 308) | func (r *closeResult) Close() error {
FILE: password.go
constant randomPasswordBytes (line 20) | randomPasswordBytes = 18
function RandomBytes (line 28) | func RandomBytes(n int) ([]byte, error) {
function RandomPassword (line 38) | func RandomPassword() (string, error) {
function RandomSalt (line 49) | func RandomSalt() (string, error) {
function UserPasswordHash (line 65) | func UserPasswordHash(password string, salt string) string {
function AgentPasswordHash (line 87) | func AgentPasswordHash(password string) string {
FILE: password_test.go
type passwordSuite (line 14) | type passwordSuite struct
method TestRandomBytes (line 24) | func (*passwordSuite) TestRandomBytes(c *gc.C) {
method TestRandomPassword (line 37) | func (*passwordSuite) TestRandomPassword(c *gc.C) {
method TestRandomSalt (line 46) | func (*passwordSuite) TestRandomSalt(c *gc.C) {
method TestUserPasswordHash (line 60) | func (*passwordSuite) TestUserPasswordHash(c *gc.C) {
method TestAgentPasswordHash (line 79) | func (*passwordSuite) TestAgentPasswordHash(c *gc.C) {
FILE: proxy/package_test.go
function TestPackage (line 12) | func TestPackage(t *testing.T) {
FILE: proxy/proxy.go
constant http_proxy (line 16) | http_proxy = "http_proxy"
constant https_proxy (line 17) | https_proxy = "https_proxy"
constant ftp_proxy (line 18) | ftp_proxy = "ftp_proxy"
constant no_proxy (line 19) | no_proxy = "no_proxy"
type Settings (line 25) | type Settings struct
method AsScriptEnvironment (line 54) | func (s *Settings) AsScriptEnvironment() string {
method AsEnvironmentValues (line 74) | func (s *Settings) AsEnvironmentValues() []string {
method AsSystemdDefaultEnv (line 93) | func (s *Settings) AsSystemdDefaultEnv() string {
method SetEnvironmentValues (line 113) | func (s *Settings) SetEnvironmentValues() {
method FullNoProxy (line 125) | func (s *Settings) FullNoProxy() string {
function getSetting (line 33) | func getSetting(key string) string {
function DetectProxies (line 42) | func DetectProxies() Settings {
FILE: proxy/proxy_test.go
type proxySuite (line 15) | type proxySuite struct
method TestDetectNoSettings (line 21) | func (s *proxySuite) TestDetectNoSettings(c *gc.C) {
method TestDetectPrimary (line 38) | func (s *proxySuite) TestDetectPrimary(c *gc.C) {
method TestDetectFallback (line 60) | func (s *proxySuite) TestDetectFallback(c *gc.C) {
method TestDetectPrimaryPreference (line 82) | func (s *proxySuite) TestDetectPrimaryPreference(c *gc.C) {
method TestAsScriptEnvironmentEmpty (line 104) | func (s *proxySuite) TestAsScriptEnvironmentEmpty(c *gc.C) {
method TestAsScriptEnvironmentOneValue (line 109) | func (s *proxySuite) TestAsScriptEnvironmentOneValue(c *gc.C) {
method TestAsScriptEnvironmentAllValue (line 119) | func (s *proxySuite) TestAsScriptEnvironmentAllValue(c *gc.C) {
method TestAsEnvironmentValuesEmpty (line 138) | func (s *proxySuite) TestAsEnvironmentValuesEmpty(c *gc.C) {
method TestAsEnvironmentValuesOneValue (line 143) | func (s *proxySuite) TestAsEnvironmentValuesOneValue(c *gc.C) {
method TestAsEnvironmentValuesAllValue (line 154) | func (s *proxySuite) TestAsEnvironmentValuesAllValue(c *gc.C) {
method TestAsSystemdDefaultEnv (line 174) | func (s *proxySuite) TestAsSystemdDefaultEnv(c *gc.C) {
method TestSetEnvironmentValues (line 191) | func (s *proxySuite) TestSetEnvironmentValues(c *gc.C) {
method TestAutoNoProxy (line 223) | func (s *proxySuite) TestAutoNoProxy(c *gc.C) {
FILE: randomstring.go
function init (line 24) | func init() {
function RandomString (line 32) | func RandomString(n int, validRunes []rune) string {
FILE: randomstring_test.go
type randomStringSuite (line 14) | type randomStringSuite struct
method TestLength (line 25) | func (randomStringSuite) TestLength(c *gc.C) {
method TestContentInValidRunes (line 30) | func (randomStringSuite) TestContentInValidRunes(c *gc.C) {
FILE: registry/package_test.go
function TestAll (line 12) | func TestAll(t *testing.T) {
FILE: registry/registry.go
type TypedNameVersion (line 19) | type TypedNameVersion struct
method Register (line 47) | func (r *TypedNameVersion) Register(name string, version int, obj any)...
method List (line 82) | func (r *TypedNameVersion) List() []Description {
method Get (line 98) | func (r *TypedNameVersion) Get(name string, version int) (any, error) {
function NewTypedNameVersion (line 25) | func NewTypedNameVersion(requiredType reflect.Type) *TypedNameVersion {
type Description (line 33) | type Description struct
type Versions (line 39) | type Versions
function descriptionFromVersions (line 69) | func descriptionFromVersions(name string, versions Versions) Description {
FILE: registry/registry_test.go
type registrySuite (line 17) | type registrySuite struct
method TestDescriptionFromVersions (line 45) | func (s *registrySuite) TestDescriptionFromVersions(c *gc.C) {
method TestDescriptionFromVersionsAreSorted (line 62) | func (s *registrySuite) TestDescriptionFromVersionsAreSorted(c *gc.C) {
method TestRegisterAndList (line 79) | func (s *registrySuite) TestRegisterAndList(c *gc.C) {
method TestRegisterAndListMultiple (line 87) | func (s *registrySuite) TestRegisterAndListMultiple(c *gc.C) {
method TestRegisterWrongType (line 99) | func (s *registrySuite) TestRegisterWrongType(c *gc.C) {
method TestRegisterAlreadyPresent (line 105) | func (s *registrySuite) TestRegisterAlreadyPresent(c *gc.C) {
method TestGet (line 122) | func (s *registrySuite) TestGet(c *gc.C) {
method TestGetUnknown (line 136) | func (s *registrySuite) TestGetUnknown(c *gc.C) {
method TestGetUnknownVersion (line 144) | func (s *registrySuite) TestGetUnknownVersion(c *gc.C) {
type Factory (line 23) | type Factory
function nilFactory (line 25) | func nilFactory() (any, error) {
type testFacade (line 31) | type testFacade struct
method TestMethod (line 40) | func (t *testFacade) TestMethod() stringVal {
type stringVal (line 36) | type stringVal struct
FILE: relativeurl.go
function RelativeURLPath (line 21) | func RelativeURLPath(basePath, targPath string) (string, error) {
FILE: relativeurl_test.go
type relativeURLSuite (line 15) | type relativeURLSuite struct
method TestRelativeURL (line 132) | func (*relativeURLSuite) TestRelativeURL(c *gc.C) {
FILE: setenv.go
function Setenv (line 19) | func Setenv(env []string, entry string) []string {
FILE: setenv_test.go
type SetenvSuite (line 12) | type SetenvSuite struct
method TestSetenv (line 27) | func (*SetenvSuite) TestSetenv(c *gc.C) {
FILE: shell/bash.go
type BashRenderer (line 11) | type BashRenderer struct
method RenderScript (line 16) | func (*BashRenderer) RenderScript(commands []string) []byte {
FILE: shell/bash_test.go
type bashSuite (line 17) | type bashSuite struct
method SetUpTest (line 27) | func (s *bashSuite) SetUpTest(c *gc.C) {
method TestExeSuffix (line 35) | func (s bashSuite) TestExeSuffix(c *gc.C) {
method TestShQuote (line 41) | func (s bashSuite) TestShQuote(c *gc.C) {
method TestChmod (line 47) | func (s bashSuite) TestChmod(c *gc.C) {
method TestWriteFile (line 55) | func (s bashSuite) TestWriteFile(c *gc.C) {
method TestMkdir (line 69) | func (s bashSuite) TestMkdir(c *gc.C) {
method TestMkdirAll (line 77) | func (s bashSuite) TestMkdirAll(c *gc.C) {
method TestChown (line 85) | func (s bashSuite) TestChown(c *gc.C) {
method TestTouchDefault (line 93) | func (s bashSuite) TestTouchDefault(c *gc.C) {
method TestTouchTimestamp (line 101) | func (s bashSuite) TestTouchTimestamp(c *gc.C) {
method TestRedirectFD (line 110) | func (s bashSuite) TestRedirectFD(c *gc.C) {
method TestRedirectOutput (line 118) | func (s bashSuite) TestRedirectOutput(c *gc.C) {
method TestRedirectOutputReset (line 126) | func (s bashSuite) TestRedirectOutputReset(c *gc.C) {
method TestScriptFilename (line 134) | func (s bashSuite) TestScriptFilename(c *gc.C) {
method TestScriptPermissions (line 140) | func (s bashSuite) TestScriptPermissions(c *gc.C) {
FILE: shell/command.go
type CommandRenderer (line 13) | type CommandRenderer interface
FILE: shell/output.go
type OutputRenderer (line 19) | type OutputRenderer interface
function ResolveFD (line 58) | func ResolveFD(name string) (int, bool) {
FILE: shell/package_test.go
function Test (line 12) | func Test(t *testing.T) {
FILE: shell/powershell.go
type PowershellRenderer (line 19) | type PowershellRenderer struct
method Quote (line 24) | func (pr *PowershellRenderer) Quote(str string) string {
method Chmod (line 29) | func (pr *PowershellRenderer) Chmod(path string, perm os.FileMode) []s...
method WriteFile (line 35) | func (pr *PowershellRenderer) WriteFile(filename string, data []byte) ...
method Mkdir (line 43) | func (pr *PowershellRenderer) Mkdir(dirname string) []string {
method MkdirAll (line 51) | func (pr *PowershellRenderer) MkdirAll(dirname string) []string {
method ScriptFilename (line 56) | func (pr *PowershellRenderer) ScriptFilename(name, dirname string) str...
constant psRemoteWrapper (line 67) | psRemoteWrapper = "powershell.exe -Sta -NonInteractive -ExecutionPolicy ...
function newEncodedPSScript (line 71) | func newEncodedPSScript(script string) (string, error) {
function NewPSEncodedCommand (line 84) | func NewPSEncodedCommand(script string) (string, error) {
FILE: shell/powershell_test.go
type powershellSuite (line 16) | type powershellSuite struct
method SetUpTest (line 24) | func (s *powershellSuite) SetUpTest(c *gc.C) {
method TestExeSuffix (line 32) | func (s powershellSuite) TestExeSuffix(c *gc.C) {
method TestShQuote (line 38) | func (s powershellSuite) TestShQuote(c *gc.C) {
method TestChmod (line 44) | func (s powershellSuite) TestChmod(c *gc.C) {
method TestWriteFile (line 50) | func (s powershellSuite) TestWriteFile(c *gc.C) {
method TestMkdir (line 65) | func (s powershellSuite) TestMkdir(c *gc.C) {
method TestMkdirAll (line 73) | func (s powershellSuite) TestMkdirAll(c *gc.C) {
method TestNewPSEncodedCommand (line 81) | func (s powershellSuite) TestNewPSEncodedCommand(c *gc.C) {
FILE: shell/renderer.go
type PathRenderer (line 18) | type PathRenderer interface
type Renderer (line 33) | type Renderer interface
function NewRenderer (line 40) | func NewRenderer(name string) (Renderer, error) {
FILE: shell/renderer_test.go
type rendererSuite (line 18) | type rendererSuite struct
method SetUpTest (line 27) | func (s *rendererSuite) SetUpTest(c *gc.C) {
method checkRenderer (line 34) | func (s rendererSuite) checkRenderer(c *gc.C, renderer shell.Renderer,...
method TestNewRendererDefault (line 45) | func (s rendererSuite) TestNewRendererDefault(c *gc.C) {
method TestNewRendererGOOS (line 58) | func (s rendererSuite) TestNewRendererGOOS(c *gc.C) {
method TestNewRendererWindows (line 71) | func (s rendererSuite) TestNewRendererWindows(c *gc.C) {
method TestNewRendererUnix (line 78) | func (s rendererSuite) TestNewRendererUnix(c *gc.C) {
method TestNewRendererDistros (line 88) | func (s rendererSuite) TestNewRendererDistros(c *gc.C) {
method TestNewRendererUnknown (line 99) | func (s rendererSuite) TestNewRendererUnknown(c *gc.C) {
FILE: shell/script.go
function DumpFileOnErrorScript (line 16) | func DumpFileOnErrorScript(filename string) string {
type ScriptRenderer (line 33) | type ScriptRenderer interface
type ScriptWriter (line 42) | type ScriptWriter interface
function WriteScript (line 68) | func WriteScript(renderer ScriptWriter, name, dirname string, script []s...
FILE: shell/script_test.go
type scriptSuite (line 21) | type scriptSuite struct
method TestDumpFileOnErrorScriptOutput (line 27) | func (*scriptSuite) TestDumpFileOnErrorScriptOutput(c *gc.C) {
method TestDumpFileOnErrorScript (line 41) | func (*scriptSuite) TestDumpFileOnErrorScript(c *gc.C) {
method TestWriteScriptUnix (line 74) | func (*scriptSuite) TestWriteScriptUnix(c *gc.C) {
method TestWriteScriptWindows (line 97) | func (*scriptSuite) TestWriteScriptWindows(c *gc.C) {
FILE: shell/unix.go
type unixRenderer (line 16) | type unixRenderer struct
method Quote (line 21) | func (unixRenderer) Quote(str string) string {
method ExeSuffix (line 27) | func (unixRenderer) ExeSuffix() string {
method Mkdir (line 32) | func (ur unixRenderer) Mkdir(dirname string) []string {
method MkdirAll (line 40) | func (ur unixRenderer) MkdirAll(dirname string) []string {
method Chmod (line 48) | func (ur unixRenderer) Chmod(path string, perm os.FileMode) []string {
method Chown (line 56) | func (ur unixRenderer) Chown(path, owner, group string) []string {
method Touch (line 64) | func (ur unixRenderer) Touch(path string, timestamp *time.Time) []stri...
method WriteFile (line 76) | func (ur unixRenderer) WriteFile(filename string, data []byte) []string {
method outFD (line 84) | func (unixRenderer) outFD(name string) (int, bool) {
method RedirectFD (line 93) | func (ur unixRenderer) RedirectFD(dst, src string) []string {
method RedirectOutput (line 108) | func (ur unixRenderer) RedirectOutput(filename string) []string {
method RedirectOutputReset (line 117) | func (ur unixRenderer) RedirectOutputReset(filename string) []string {
method ScriptFilename (line 126) | func (ur *unixRenderer) ScriptFilename(name, dirname string) string {
method ScriptPermissions (line 131) | func (ur *unixRenderer) ScriptPermissions() os.FileMode {
FILE: shell/win.go
type windowsRenderer (line 15) | type windowsRenderer struct
method ExeSuffix (line 20) | func (w *windowsRenderer) ExeSuffix() string {
method ScriptPermissions (line 25) | func (w *windowsRenderer) ScriptPermissions() os.FileMode {
method RenderScript (line 30) | func (w *windowsRenderer) RenderScript(commands []string) []byte {
method Chown (line 35) | func (w windowsRenderer) Chown(path, owner, group string) []string {
method Touch (line 41) | func (w windowsRenderer) Touch(path string, timestamp *time.Time) []st...
method RedirectFD (line 47) | func (w windowsRenderer) RedirectFD(dst, src string) []string {
method RedirectOutput (line 53) | func (w windowsRenderer) RedirectOutput(filename string) []string {
method RedirectOutputReset (line 59) | func (w windowsRenderer) RedirectOutputReset(filename string) []string {
FILE: shell/wincmd.go
type WinCmdRenderer (line 15) | type WinCmdRenderer struct
method Quote (line 20) | func (wcr *WinCmdRenderer) Quote(str string) string {
method Chmod (line 25) | func (wcr *WinCmdRenderer) Chmod(path string, perm os.FileMode) []stri...
method WriteFile (line 31) | func (wcr *WinCmdRenderer) WriteFile(filename string, data []byte) []s...
method Mkdir (line 42) | func (wcr *WinCmdRenderer) Mkdir(dirname string) []string {
method MkdirAll (line 50) | func (wcr *WinCmdRenderer) MkdirAll(dirname string) []string {
method ScriptFilename (line 59) | func (wcr *WinCmdRenderer) ScriptFilename(name, dirname string) string {
FILE: shell/wincmd_test.go
type winCmdSuite (line 16) | type winCmdSuite struct
method SetUpTest (line 24) | func (s *winCmdSuite) SetUpTest(c *gc.C) {
method TestExeSuffix (line 32) | func (s winCmdSuite) TestExeSuffix(c *gc.C) {
method TestShQuote (line 38) | func (s winCmdSuite) TestShQuote(c *gc.C) {
method TestChmod (line 44) | func (s winCmdSuite) TestChmod(c *gc.C) {
method TestWriteFile (line 50) | func (s winCmdSuite) TestWriteFile(c *gc.C) {
method TestMkdir (line 61) | func (s winCmdSuite) TestMkdir(c *gc.C) {
method TestMkdirAll (line 69) | func (s winCmdSuite) TestMkdirAll(c *gc.C) {
FILE: size.go
function ParseSize (line 20) | func ParseSize(str string) (MB uint64, err error) {
function sizeSuffixMultiplier (line 54) | func sizeSuffixMultiplier(i int) int {
type SizeTracker (line 63) | type SizeTracker struct
method Size (line 69) | func (st SizeTracker) Size() int64 {
method Write (line 74) | func (st *SizeTracker) Write(data []byte) (n int, err error) {
FILE: size_test.go
type sizeSuite (line 20) | type sizeSuite struct
method TestParseSize (line 24) | func (*sizeSuite) TestParseSize(c *gc.C) {
method TestSizingReaderOkay (line 86) | func (*sizeSuite) TestSizingReaderOkay(c *gc.C) {
method TestSizingReaderMixedEOF (line 101) | func (*sizeSuite) TestSizingReaderMixedEOF(c *gc.C) {
method TestSizingWriter (line 121) | func (*sizeSuite) TestSizingWriter(c *gc.C) {
type fakeStream (line 137) | type fakeStream struct
method Read (line 142) | func (f *fakeStream) Read(data []byte) (int, error) {
FILE: ssh/authorisedkeys.go
type ListMode (line 26) | type ListMode
constant defaultAuthKeysFile (line 34) | defaultAuthKeysFile = "authorized_keys"
type AuthorisedKey (line 37) | type AuthorisedKey struct
function authKeysDir (line 43) | func authKeysDir(username string) (string, error) {
function ParseAuthorisedKey (line 58) | func ParseAuthorisedKey(line string) (*AuthorisedKey, error) {
function ConcatAuthorisedKeys (line 76) | func ConcatAuthorisedKeys(a, b string) string {
function SplitAuthorisedKeys (line 91) | func SplitAuthorisedKeys(keyData string) []string {
function readAuthorisedKeys (line 106) | func readAuthorisedKeys(username, filename string) ([]string, error) {
function writeAuthorisedKeys (line 130) | func writeAuthorisedKeys(username, filename string, keys []string) error {
function AddKeys (line 194) | func AddKeys(user string, newKeys ...string) error {
function DeleteKeys (line 207) | func DeleteKeys(user string, keyIds ...string) error {
function ReplaceKeys (line 220) | func ReplaceKeys(user string, newKeys ...string) error {
function ListKeys (line 239) | func ListKeys(user string, mode ListMode) ([]string, error) {
constant JujuCommentPrefix (line 252) | JujuCommentPrefix = "Juju:"
function EnsureJujuComment (line 254) | func EnsureJujuComment(key string) string {
function AddKeysToFile (line 275) | func AddKeysToFile(user, file string, newKeys []string) error {
function DeleteKeysFromFile (line 290) | func DeleteKeysFromFile(user, file string, keyIds []string) error {
function ListKeysFromFile (line 301) | func ListKeysFromFile(user, file string, mode ListMode) ([]string, error) {
function addKeys (line 311) | func addKeys(user, file string, newKeys, existingKeys []string) error {
function deleteKeys (line 341) | func deleteKeys(user, file string, existingKeys, keyIdsToDelete []string...
function listKeys (line 382) | func listKeys(existingKeys []string, mode ListMode) ([]string, error) {
FILE: ssh/authorisedkeys_test.go
type AuthorisedKeysKeysSuite (line 18) | type AuthorisedKeysKeysSuite struct
method TestListKeys (line 37) | func (s *AuthorisedKeysKeysSuite) TestListKeys(c *gc.C) {
method TestListKeysFull (line 50) | func (s *AuthorisedKeysKeysSuite) TestListKeysFull(c *gc.C) {
method TestAddNewKey (line 61) | func (s *AuthorisedKeysKeysSuite) TestAddNewKey(c *gc.C) {
method TestAddMoreKeys (line 70) | func (s *AuthorisedKeysKeysSuite) TestAddMoreKeys(c *gc.C) {
method TestAddDuplicateKey (line 84) | func (s *AuthorisedKeysKeysSuite) TestAddDuplicateKey(c *gc.C) {
method TestAddDuplicateComment (line 96) | func (s *AuthorisedKeysKeysSuite) TestAddDuplicateComment(c *gc.C) {
method TestAddKeyWithoutComment (line 108) | func (s *AuthorisedKeysKeysSuite) TestAddKeyWithoutComment(c *gc.C) {
method TestAddKeepsUnrecognised (line 117) | func (s *AuthorisedKeysKeysSuite) TestAddKeepsUnrecognised(c *gc.C) {
method TestDeleteKeys (line 127) | func (s *AuthorisedKeysKeysSuite) TestDeleteKeys(c *gc.C) {
method TestDeleteKeysKeepsUnrecognised (line 139) | func (s *AuthorisedKeysKeysSuite) TestDeleteKeysKeepsUnrecognised(c *g...
method TestDeleteNonExistentComment (line 149) | func (s *AuthorisedKeysKeysSuite) TestDeleteNonExistentComment(c *gc.C) {
method TestDeleteNonExistentFingerprint (line 156) | func (s *AuthorisedKeysKeysSuite) TestDeleteNonExistentFingerprint(c *...
method TestDeleteLastKeyForbidden (line 163) | func (s *AuthorisedKeysKeysSuite) TestDeleteLastKeyForbidden(c *gc.C) {
method TestReplaceKeys (line 173) | func (s *AuthorisedKeysKeysSuite) TestReplaceKeys(c *gc.C) {
method TestReplaceKeepsUnrecognised (line 191) | func (s *AuthorisedKeysKeysSuite) TestReplaceKeepsUnrecognised(c *gc.C) {
method TestEnsureJujuComment (line 201) | func (s *AuthorisedKeysKeysSuite) TestEnsureJujuComment(c *gc.C) {
method TestSplitAuthorisedKeys (line 218) | func (s *AuthorisedKeysKeysSuite) TestSplitAuthorisedKeys(c *gc.C) {
method TestParseAuthorisedKey (line 243) | func (s *AuthorisedKeysKeysSuite) TestParseAuthorisedKey(c *gc.C) {
method TestConcatAuthorisedKeys (line 283) | func (s *AuthorisedKeysKeysSuite) TestConcatAuthorisedKeys(c *gc.C) {
method TestAddKeysToFileToDifferentFiles (line 294) | func (s *AuthorisedKeysKeysSuite) TestAddKeysToFileToDifferentFiles(c ...
method TestAddKeysToFileMultipleKeys (line 312) | func (s *AuthorisedKeysKeysSuite) TestAddKeysToFileMultipleKeys(c *gc....
method TestDeleteAllKeysFromFile (line 323) | func (s *AuthorisedKeysKeysSuite) TestDeleteAllKeysFromFile(c *gc.C) {
method TestDeleteSomeKeysFromFile (line 335) | func (s *AuthorisedKeysKeysSuite) TestDeleteSomeKeysFromFile(c *gc.C) {
constant testSSHUser (line 24) | testSSHUser = ""
constant authKeysFile (line 25) | authKeysFile = "authorized_keys"
constant alternativeKeysFile2 (line 26) | alternativeKeysFile2 = "authorized_keys2"
constant alternativeKeysFile3 (line 27) | alternativeKeysFile3 = "authorized_keys3"
function writeAuthKeysFile (line 32) | func writeAuthKeysFile(c *gc.C, keys []string, file string) {
function b64decode (line 237) | func b64decode(c *gc.C, s string) []byte {
FILE: ssh/clientkeys.go
constant clientKeyName (line 20) | clientKeyName = "juju_id_ed25519"
constant PublicKeySuffix (line 23) | PublicKeySuffix = ".pub"
function LoadClientKeys (line 46) | func LoadClientKeys(dir string) error {
function ClearClientKeys (line 77) | func ClearClientKeys() {
function generateClientKey (line 83) | func generateClientKey(dir string) (keyfile string, key ssh.Signer, err ...
function loadClientKeys (line 103) | func loadClientKeys(dir string) (map[string]ssh.Signer, error) {
function privateKeys (line 124) | func privateKeys() (signers []ssh.Signer) {
function PrivateKeyFiles (line 135) | func PrivateKeyFiles() []string {
function PublicKeyFiles (line 147) | func PublicKeyFiles() []string {
function publicKeyFiles (line 158) | func publicKeyFiles(clientKeysDir string) ([]string, error) {
FILE: ssh/clientkeys_test.go
type ClientKeysSuite (line 18) | type ClientKeysSuite struct
method SetUpTest (line 24) | func (s *ClientKeysSuite) SetUpTest(c *gc.C) {
method TestPublicKeyFiles (line 50) | func (s *ClientKeysSuite) TestPublicKeyFiles(c *gc.C) {
method TestPrivateKeyFiles (line 73) | func (s *ClientKeysSuite) TestPrivateKeyFiles(c *gc.C) {
method TestLoadClientKeysDirExists (line 100) | func (s *ClientKeysSuite) TestLoadClientKeysDirExists(c *gc.C) {
function checkFiles (line 31) | func checkFiles(c *gc.C, obtained, expected []string) {
function checkPublicKeyFiles (line 40) | func checkPublicKeyFiles(c *gc.C, expected ...string) {
function checkPrivateKeyFiles (line 45) | func checkPrivateKeyFiles(c *gc.C, expected ...string) {
FILE: ssh/export_test.go
type ReadLineWriter (line 25) | type ReadLineWriter
function PatchTerminal (line 27) | func PatchTerminal(s *testing.CleanupSuite, rlw ReadLineWriter) {
function PatchNilTerminal (line 41) | func PatchNilTerminal(s *testing.CleanupSuite) {
FILE: ssh/fakes_test.go
type fakeClient (line 17) | type fakeClient struct
method checkCalls (line 29) | func (cl *fakeClient) checkCalls(c *gc.C, host string, command []strin...
method Command (line 37) | func (cl *fakeClient) Command(host string, command []string, options *...
method Copy (line 49) | func (cl *fakeClient) Copy(args []string, options *ssh.Options) error {
type bufferWriter (line 56) | type bufferWriter struct
method Close (line 60) | func (*bufferWriter) Close() error {
type fakeCommandImpl (line 64) | type fakeCommandImpl struct
method checkCalls (line 79) | func (ci *fakeCommandImpl) checkCalls(c *gc.C, stdin io.Reader, stdout...
method checkStdin (line 86) | func (ci *fakeCommandImpl) checkStdin(c *gc.C, data string) {
method Start (line 90) | func (ci *fakeCommandImpl) Start() error {
method Wait (line 95) | func (ci *fakeCommandImpl) Wait() error {
method Kill (line 100) | func (ci *fakeCommandImpl) Kill() error {
method SetStdio (line 105) | func (ci *fakeCommandImpl) SetStdio(stdin io.Reader, stdout, stderr io...
method StdinPipe (line 112) | func (ci *fakeCommandImpl) StdinPipe() (io.WriteCloser, io.Reader, err...
method StdoutPipe (line 117) | func (ci *fakeCommandImpl) StdoutPipe() (io.ReadCloser, io.Writer, err...
method StderrPipe (line 122) | func (ci *fakeCommandImpl) StderrPipe() (io.ReadCloser, io.Writer, err...
FILE: ssh/fingerprint.go
function KeyFingerprint (line 17) | func KeyFingerprint(key string) (fingerprint, comment string, err error) {
FILE: ssh/fingerprint_test.go
type FingerprintSuite (line 15) | type FingerprintSuite struct
method TestKeyFingerprint (line 21) | func (s *FingerprintSuite) TestKeyFingerprint(c *gc.C) {
method TestKeyFingerprintError (line 34) | func (s *FingerprintSuite) TestKeyFingerprintError(c *gc.C) {
FILE: ssh/generate.go
function GenerateKey (line 24) | func GenerateKey(comment string) (private, public string, err error) {
function PublicKey (line 47) | func PublicKey(privateKey []byte, comment string) (string, error) {
FILE: ssh/generate_test.go
type GenerateSuite (line 18) | type GenerateSuite struct
method TestGenerate (line 65) | func (s *GenerateSuite) TestGenerate(c *gc.C) {
function overrideGenerateKey (line 30) | func overrideGenerateKey() testing.Restorer {
function generateED25519Key (line 45) | func generateED25519Key(random io.Reader) (ed25519.PublicKey, ed25519.Pr...
function generateDSAKey (line 54) | func generateDSAKey(random io.Reader) (*dsa.PrivateKey, error) {
FILE: ssh/package_test.go
function TestPackage (line 12) | func TestPackage(t *testing.T) {
FILE: ssh/run.go
type ExecParams (line 20) | type ExecParams struct
function StartCommandOnMachine (line 31) | func StartCommandOnMachine(params ExecParams) (*RunningCmd, error) {
type RunningCmd (line 59) | type RunningCmd struct
method Wait (line 69) | func (cmd *RunningCmd) Wait() (result utilexec.ExecResponse, _ error) {
method WaitWithCancel (line 100) | func (cmd *RunningCmd) WaitWithCancel(cancel <-chan struct{}) (utilexe...
function getExitCode (line 124) | func getExitCode(err error) (int, error) {
function ExecuteCommandOnMachine (line 147) | func ExecuteCommandOnMachine(args ExecParams) (utilexec.ExecResponse, er...
FILE: ssh/run_test.go
constant shortWait (line 21) | shortWait = 50 * time.Millisecond
constant longWait (line 22) | longWait = 10 * time.Second
type ExecuteSSHCommandSuite (line 25) | type ExecuteSSHCommandSuite struct
method SetUpSuite (line 34) | func (s *ExecuteSSHCommandSuite) SetUpSuite(c *gc.C) {
method SetUpTest (line 39) | func (s *ExecuteSSHCommandSuite) SetUpTest(c *gc.C) {
method fakeSSH (line 51) | func (s *ExecuteSSHCommandSuite) fakeSSH(c *gc.C, cmd string) {
method TestCaptureOutput (line 56) | func (s *ExecuteSSHCommandSuite) TestCaptureOutput(c *gc.C) {
method TestIdentityFile (line 72) | func (s *ExecuteSSHCommandSuite) TestIdentityFile(c *gc.C) {
method TestTimoutCaptureOutput (line 85) | func (s *ExecuteSSHCommandSuite) TestTimoutCaptureOutput(c *gc.C) {
method TestCapturesReturnCode (line 101) | func (s *ExecuteSSHCommandSuite) TestCapturesReturnCode(c *gc.C) {
FILE: ssh/ssh.go
type StrictHostChecksOption (line 22) | type StrictHostChecksOption
constant StrictHostChecksDefault (line 34) | StrictHostChecksDefault StrictHostChecksOption = iota
constant StrictHostChecksNo (line 37) | StrictHostChecksNo
constant StrictHostChecksYes (line 42) | StrictHostChecksYes
constant StrictHostChecksAsk (line 46) | StrictHostChecksAsk
type Options (line 50) | type Options struct
method SetProxyCommand (line 84) | func (o *Options) SetProxyCommand(command ...string) {
method SetPort (line 89) | func (o *Options) SetPort(port int) {
method EnablePTY (line 97) | func (o *Options) EnablePTY() {
method SetKnownHostsFile (line 104) | func (o *Options) SetKnownHostsFile(file string) {
method SetStrictHostKeyChecking (line 111) | func (o *Options) SetStrictHostKeyChecking(value StrictHostChecksOptio...
method AllowPasswordAuthentication (line 119) | func (o *Options) AllowPasswordAuthentication() {
method SetIdentities (line 127) | func (o *Options) SetIdentities(identityFiles ...string) {
method SetHostKeyAlgorithms (line 134) | func (o *Options) SetHostKeyAlgorithms(algos ...string) {
type Client (line 139) | type Client interface
type Cmd (line 156) | type Cmd struct
method CombinedOutput (line 170) | func (c *Cmd) CombinedOutput() ([]byte, error) {
method Output (line 186) | func (c *Cmd) Output() ([]byte, error) {
method Run (line 197) | func (c *Cmd) Run() error {
method Start (line 214) | func (c *Cmd) Start() error {
method Wait (line 221) | func (c *Cmd) Wait() error {
method Kill (line 226) | func (c *Cmd) Kill() error {
method StdinPipe (line 233) | func (c *Cmd) StdinPipe() (io.WriteCloser, error) {
method StdoutPipe (line 245) | func (c *Cmd) StdoutPipe() (io.ReadCloser, error) {
method StderrPipe (line 257) | func (c *Cmd) StderrPipe() (io.ReadCloser, error) {
function newCmd (line 163) | func newCmd(impl command) *Cmd {
type command (line 268) | type command interface
function init (line 289) | func init() {
function initDefaultClient (line 293) | func initDefaultClient() {
function Command (line 304) | func Command(host string, command []string, options *Options) *Cmd {
function Copy (line 310) | func Copy(args []string, options *Options) error {
function CopyReader (line 316) | func CopyReader(host, filename string, r io.Reader, options *Options) er...
function copyReader (line 321) | func copyReader(client Client, host, filename string, r io.Reader, optio...
FILE: ssh/ssh_gocrypto.go
constant sshDefaultPort (line 30) | sshDefaultPort = 22
type GoCryptoClient (line 39) | type GoCryptoClient struct
method Command (line 52) | func (c *GoCryptoClient) Command(host string, command []string, option...
method Copy (line 89) | func (c *GoCryptoClient) Copy(args []string, options *Options) error {
function NewGoCryptoClient (line 47) | func NewGoCryptoClient(signers ...ssh.Signer) (*GoCryptoClient, error) {
type goCryptoCommand (line 93) | type goCryptoCommand struct
method ensureSession (line 145) | func (c *goCryptoCommand) ensureSession() (*ssh.Session, error) {
method Start (line 186) | func (c *goCryptoCommand) Start() error {
method Close (line 197) | func (c *goCryptoCommand) Close() error {
method Wait (line 211) | func (c *goCryptoCommand) Wait() error {
method Kill (line 220) | func (c *goCryptoCommand) Kill() error {
method SetStdio (line 227) | func (c *goCryptoCommand) SetStdio(stdin io.Reader, stdout, stderr io....
method StdinPipe (line 233) | func (c *goCryptoCommand) StdinPipe() (io.WriteCloser, io.Reader, erro...
method StdoutPipe (line 242) | func (c *goCryptoCommand) StdoutPipe() (io.ReadCloser, io.Writer, erro...
method StderrPipe (line 251) | func (c *goCryptoCommand) StderrPipe() (io.ReadCloser, io.Writer, erro...
method hostKeyCallback (line 260) | func (c *goCryptoCommand) hostKeyCallback(hostname string, remote net....
type readLineWriter (line 379) | type readLineWriter interface
function checkHostKey (line 399) | func checkHostKey(
function splitUserHost (line 479) | func splitUserHost(s string) (user, host string) {
function GoCryptoKnownHostsFile (line 494) | func GoCryptoKnownHostsFile() string {
function SetGoCryptoKnownHostsFile (line 502) | func SetGoCryptoKnownHostsFile(file string) {
FILE: ssh/ssh_gocrypto_test.go
type sshServer (line 36) | type sshServer struct
method run (line 42) | func (s *sshServer) run(errorCh chan error, done chan bool) {
function newClient (line 118) | func newClient(c *gc.C) (*ssh.GoCryptoClient, cryptossh.PublicKey) {
type SSHGoCryptoCommandSuite (line 131) | type SSHGoCryptoCommandSuite struct
method SetUpSuite (line 143) | func (s *SSHGoCryptoCommandSuite) SetUpSuite(c *gc.C) {
method SetUpTest (line 180) | func (s *SSHGoCryptoCommandSuite) SetUpTest(c *gc.C) {
method newServer (line 191) | func (s *SSHGoCryptoCommandSuite) newServer(c *gc.C, serverConfig cryp...
method TestNewGoCryptoClient (line 202) | func (s *SSHGoCryptoCommandSuite) TestNewGoCryptoClient(c *gc.C) {
method TestClientNoKeys (line 216) | func (s *SSHGoCryptoCommandSuite) TestClientNoKeys(c *gc.C) {
method TestCommand (line 245) | func (s *SSHGoCryptoCommandSuite) TestCommand(c *gc.C) {
method TestCopy (line 279) | func (s *SSHGoCryptoCommandSuite) TestCopy(c *gc.C) {
method TestProxyCommand (line 286) | func (s *SSHGoCryptoCommandSuite) TestProxyCommand(c *gc.C) {
method TestStrictHostChecksYes (line 321) | func (s *SSHGoCryptoCommandSuite) TestStrictHostChecksYes(c *gc.C) {
method TestStrictHostChecksAskNonTerminal (line 344) | func (s *SSHGoCryptoCommandSuite) TestStrictHostChecksAskNonTerminal(c...
method TestStrictHostChecksAskTerminalYes (line 364) | func (s *SSHGoCryptoCommandSuite) TestStrictHostChecksAskTerminalYes(c...
method TestStrictHostChecksAskTerminalNo (line 401) | func (s *SSHGoCryptoCommandSuite) TestStrictHostChecksAskTerminalNo(c ...
method TestStrictHostChecksNoMismatch (line 432) | func (s *SSHGoCryptoCommandSuite) TestStrictHostChecksNoMismatch(c *gc...
method TestStrictHostChecksDifferentKeyTypes (line 481) | func (s *SSHGoCryptoCommandSuite) TestStrictHostChecksDifferentKeyType...
function waitForServer (line 235) | func waitForServer(c *gc.C, errorCh chan error) error {
type mockReadLineWriter (line 532) | type mockReadLineWriter struct
method addLine (line 538) | func (m *mockReadLineWriter) addLine(line string) {
method ReadLine (line 542) | func (m *mockReadLineWriter) ReadLine() (string, error) {
method Write (line 552) | func (m *mockReadLineWriter) Write(data []byte) (int, error) {
FILE: ssh/ssh_openssh.go
type opensshCommandKind (line 29) | type opensshCommandKind
constant sshKind (line 32) | sshKind opensshCommandKind = iota
constant scpKind (line 33) | scpKind
function sshpassWrap (line 39) | func sshpassWrap(cmd string, args []string) (string, []string) {
type OpenSSHClient (line 50) | type OpenSSHClient struct
method Command (line 145) | func (c *OpenSSHClient) Command(host string, command []string, options...
method Copy (line 157) | func (c *OpenSSHClient) Copy(args []string, userOptions *Options) error {
function NewOpenSSHClient (line 55) | func NewOpenSSHClient() (*OpenSSHClient, error) {
function opensshOptions (line 66) | func opensshOptions(options *Options, commandKind opensshCommandKind) []...
type opensshCmd (line 180) | type opensshCmd struct
method SetStdio (line 184) | func (c *opensshCmd) SetStdio(stdin io.Reader, stdout, stderr io.Write...
method StdinPipe (line 188) | func (c *opensshCmd) StdinPipe() (io.WriteCloser, io.Reader, error) {
method StdoutPipe (line 196) | func (c *opensshCmd) StdoutPipe() (io.ReadCloser, io.Writer, error) {
method StderrPipe (line 204) | func (c *opensshCmd) StderrPipe() (io.ReadCloser, io.Writer, error) {
method Kill (line 212) | func (c *opensshCmd) Kill() error {
FILE: ssh/ssh_test.go
constant echoCommand (line 26) | echoCommand = "/bin/echo"
constant echoScript (line 27) | echoScript = "#!/bin/sh\n" + echoCommand + " $0 \"$@\" | /usr/bin/tee $...
type SSHCommandSuite (line 30) | type SSHCommandSuite struct
method SetUpTest (line 41) | func (s *SSHCommandSuite) SetUpTest(c *gc.C) {
method command (line 56) | func (s *SSHCommandSuite) command(args ...string) *ssh.Cmd {
method commandOptions (line 60) | func (s *SSHCommandSuite) commandOptions(args []string, opts *ssh.Opti...
method assertCommandArgs (line 64) | func (s *SSHCommandSuite) assertCommandArgs(c *gc.C, cmd *ssh.Cmd, exp...
method TestDefaultClient (line 70) | func (s *SSHCommandSuite) TestDefaultClient(c *gc.C) {
method TestCommandSSHPass (line 78) | func (s *SSHCommandSuite) TestCommandSSHPass(c *gc.C) {
method TestCommand (line 101) | func (s *SSHCommandSuite) TestCommand(c *gc.C) {
method TestCommandEnablePTY (line 108) | func (s *SSHCommandSuite) TestCommandEnablePTY(c *gc.C) {
method TestCommandSetKnownHostsFile (line 117) | func (s *SSHCommandSuite) TestCommandSetKnownHostsFile(c *gc.C) {
method TestSetStrictHostKeyChecking (line 126) | func (s *SSHCommandSuite) TestSetStrictHostKeyChecking(c *gc.C) {
method TestCommandAllowPasswordAuthentication (line 152) | func (s *SSHCommandSuite) TestCommandAllowPasswordAuthentication(c *gc...
method TestCommandIdentities (line 161) | func (s *SSHCommandSuite) TestCommandIdentities(c *gc.C) {
method TestCommandPort (line 170) | func (s *SSHCommandSuite) TestCommandPort(c *gc.C) {
method TestCopy (line 179) | func (s *SSHCommandSuite) TestCopy(c *gc.C) {
method TestCommandClientKeys (line 207) | func (s *SSHCommandSuite) TestCommandClientKeys(c *gc.C) {
method TestCommandError (line 222) | func (s *SSHCommandSuite) TestCommandError(c *gc.C) {
method TestCommandDefaultIdentities (line 231) | func (s *SSHCommandSuite) TestCommandDefaultIdentities(c *gc.C) {
method TestCopyReader (line 253) | func (s *SSHCommandSuite) TestCopyReader(c *gc.C) {
FILE: ssh/stream.go
type stripCR (line 12) | type stripCR struct
method Read (line 30) | func (s *stripCR) Read(bufOut []byte) (int, error) {
function StripCRReader (line 17) | func StripCRReader(reader io.Reader) io.Reader {
FILE: ssh/stream_test.go
type SSHStreamSuite (line 18) | type SSHStreamSuite struct
method TestNewStripCRNil (line 24) | func (s *SSHStreamSuite) TestNewStripCRNil(c *gc.C) {
method TestStripCR (line 29) | func (s *SSHStreamSuite) TestStripCR(c *gc.C) {
method TestStripCROneByte (line 36) | func (s *SSHStreamSuite) TestStripCROneByte(c *gc.C) {
method TestStripCRError (line 43) | func (s *SSHStreamSuite) TestStripCRError(c *gc.C) {
FILE: ssh/stream_wrapper_unix.go
function WrapStdin (line 14) | func WrapStdin(reader io.Reader) io.Reader {
FILE: ssh/stream_wrapper_windows.go
function WrapStdin (line 11) | func WrapStdin(reader io.Reader) io.Reader {
FILE: ssh/testing/keys.go
type SSHKey (line 6) | type SSHKey struct
FILE: symlink/symlink.go
function Replace (line 15) | func Replace(link, newpath string) error {
FILE: symlink/symlink_posix.go
function New (line 16) | func New(oldname, newname string) error {
function Read (line 21) | func Read(link string) (string, error) {
function IsSymlink (line 25) | func IsSymlink(path string) (bool, error) {
function getLongPathAsString (line 35) | func getLongPathAsString(path string) (string, error) {
FILE: symlink/symlink_test.go
type SymlinkSuite (line 19) | type SymlinkSuite struct
method TestReplace (line 27) | func (*SymlinkSuite) TestReplace(c *gc.C) {
method TestIsSymlinkFile (line 54) | func (*SymlinkSuite) TestIsSymlinkFile(c *gc.C) {
method TestIsSymlinkFolder (line 75) | func (*SymlinkSuite) TestIsSymlinkFolder(c *gc.C) {
method TestIsSymlinkFalseFile (line 92) | func (*SymlinkSuite) TestIsSymlinkFalseFile(c *gc.C) {
method TestIsSymlinkFalseFolder (line 107) | func (*SymlinkSuite) TestIsSymlinkFalseFolder(c *gc.C) {
method TestIsSymlinkFileDoesNotExist (line 119) | func (*SymlinkSuite) TestIsSymlinkFileDoesNotExist(c *gc.C) {
function Test (line 23) | func Test(t *testing.T) {
FILE: symlink/symlink_windows.go
constant SYMBOLIC_LINK_FLAG_DIRECTORY (line 19) | SYMBOLIC_LINK_FLAG_DIRECTORY = 1
constant GENERIC_EXECUTION (line 22) | GENERIC_EXECUTION = 33554432
constant FILE_ATTRIBUTE_REPARSE_POINT (line 26) | FILE_ATTRIBUTE_REPARSE_POINT = 0x00000400
function New (line 34) | func New(oldname, newname string) error {
function Read (line 63) | func Read(link string) (string, error) {
function IsSymlink (line 109) | func IsSymlink(path string) (bool, error) {
function getLongPath (line 124) | func getLongPath(path string) ([]uint16, error) {
function getLongPathAsString (line 147) | func getLongPathAsString(path string) (string, error) {
FILE: symlink/symlink_windows_test.go
method TestLongPath (line 17) | func (*SymlinkSuite) TestLongPath(c *gc.C) {
method TestCreateSymLink (line 25) | func (*SymlinkSuite) TestCreateSymLink(c *gc.C) {
method TestReadData (line 42) | func (*SymlinkSuite) TestReadData(c *gc.C) {
FILE: symlink/zsymlink_windows_386.go
function createSymbolicLink (line 20) | func createSymbolicLink(symlinkname *uint16, targetname *uint16, flags u...
function getFinalPathNameByHandle (line 32) | func getFinalPathNameByHandle(handle syscall.Handle, buf *uint16, buflen...
FILE: symlink/zsymlink_windows_amd64.go
function createSymbolicLink (line 20) | func createSymbolicLink(symlinkname *uint16, targetname *uint16, flags u...
function getFinalPathNameByHandle (line 32) | func getFinalPathNameByHandle(handle syscall.Handle, buf *uint16, buflen...
FILE: systemerrmessages_unix.go
constant NoSuchUserErrRegexp (line 14) | NoSuchUserErrRegexp = `user: unknown user [a-z0-9_-]*`
constant NoSuchFileErrRegexp (line 15) | NoSuchFileErrRegexp = `no such file or directory`
constant MkdirFailErrRegexp (line 16) | MkdirFailErrRegexp = `.* not a directory`
FILE: systemerrmessages_windows.go
constant NoSuchUserErrRegexp (line 11) | NoSuchUserErrRegexp = `No mapping between account names and security IDs...
constant NoSuchFileErrRegexp (line 12) | NoSuchFileErrRegexp = `The system cannot find the (file|path) specified\.`
constant MkdirFailErrRegexp (line 13) | MkdirFailErrRegexp = `mkdir .*` + NoSuchFileErrRegexp
FILE: tailer/package_test.go
function TestPackage (line 12) | func TestPackage(t *testing.T) {
FILE: tailer/tailer.go
constant defaultBufferSize (line 17) | defaultBufferSize = 4096
constant polltime (line 18) | polltime = time.Second
constant delimiter (line 19) | delimiter = '\n'
type TailerFilterFunc (line 29) | type TailerFilterFunc
type Tailer (line 33) | type Tailer struct
method Stop (line 70) | func (t *Tailer) Stop() error {
method Wait (line 77) | func (t *Tailer) Wait() error {
method Dead (line 83) | func (t *Tailer) Dead() <-chan struct{} {
method Err (line 88) | func (t *Tailer) Err() error {
method loop (line 95) | func (t *Tailer) loop() error {
method readLine (line 210) | func (t *Tailer) readLine() ([]byte, error) {
method isValid (line 241) | func (t *Tailer) isValid(line []byte) bool {
function NewTailer (line 47) | func NewTailer(readSeeker io.ReadSeeker, writer io.Writer, filter Tailer...
function newTailer (line 53) | func newTailer(readSeeker io.ReadSeeker, writer io.Writer,
function SeekLastLines (line 129) | func SeekLastLines(readSeeker io.ReadSeeker, lines uint, filter TailerFi...
FILE: tailer/tailer_test.go
type tailerSuite (line 20) | type tailerSuite struct
method TestTailer (line 326) | func (s *tailerSuite) TestTailer(c *gc.C) {
function startReading (line 370) | func startReading(c *gc.C, tailer *tailer.Tailer, reader *io.PipeReader,...
function assertCollected (line 404) | func assertCollected(c *gc.C, linec chan string, compare []string, injec...
function startReadSeeker (line 441) | func startReadSeeker(c *gc.C, data []string, initialLeg int, sigc chan s...
type readSeeker (line 460) | type readSeeker struct
method write (line 467) | func (r *readSeeker) write(s string) {
method setError (line 473) | func (r *readSeeker) setError(err error) {
method Read (line 479) | func (r *readSeeker) Read(p []byte) (n int, err error) {
method Seek (line 493) | func (r *readSeeker) Seek(offset int64, whence int) (ret int64, err er...
FILE: tar/tar.go
function FindFile (line 28) | func FindFile(tarFile io.Reader, filename string) (*tar.Header, io.Reade...
function TarFiles (line 57) | func TarFiles(fileList []string, target io.Writer, strip string) (shaSum...
function tarAndHashFiles (line 66) | func tarAndHashFiles(fileList []string, target io.Writer, strip string, ...
function writeContents (line 86) | func writeContents(fileName, strip string, tarw *tar.Writer) error {
function createAndFill (line 145) | func createAndFill(filePath string, mode int64, content io.Reader) error {
function UntarFiles (line 170) | func UntarFiles(tarFile io.Reader, outputFolder string) error {
FILE: tar/tar_test.go
function TestPackage (line 24) | func TestPackage(t *stdtesting.T) {
type TarSuite (line 30) | type TarSuite struct
method SetUpTest (line 36) | func (t *TarSuite) SetUpTest(c *gc.C) {
method createTestFiles (line 41) | func (t *TarSuite) createTestFiles(c *gc.C) {
method removeTestFiles (line 83) | func (t *TarSuite) removeTestFiles(c *gc.C) {
method assertTarContents (line 111) | func (t *TarSuite) assertTarContents(c *gc.C, expectedContents []expec...
method assertFilesWhereUntared (line 142) | func (t *TarSuite) assertFilesWhereUntared(c *gc.C,
method TestTarFiles (line 196) | func (t *TarSuite) TestTarFiles(c *gc.C) {
method TestSymlinksTar (line 209) | func (t *TarSuite) TestSymlinksTar(c *gc.C) {
method TestUnTarFilesUncompressed (line 244) | func (t *TarSuite) TestUnTarFilesUncompressed(c *gc.C) {
method TestFindFileFound (line 260) | func (t *TarSuite) TestFindFileFound(c *gc.C) {
method TestFindFileNotFound (line 277) | func (t *TarSuite) TestFindFileNotFound(c *gc.C) {
method TestUntarFilesHeadersIgnored (line 290) | func (t *TarSuite) TestUntarFilesHeadersIgnored(c *gc.C) {
method TestUntarFilesWithMissingDirectories (line 312) | func (t *TarSuite) TestUntarFilesWithMissingDirectories(c *gc.C) {
type expectedTarContents (line 90) | type expectedTarContents struct
function shaSumFile (line 188) | func shaSumFile(c *gc.C, fileToSum io.Reader) string {
FILE: timer.go
type Countdown (line 20) | type Countdown interface
function NewBackoffTimer (line 37) | func NewBackoffTimer(config BackoffTimerConfig) *BackoffTimer {
type BackoffTimer (line 50) | type BackoffTimer struct
method Start (line 87) | func (t *BackoffTimer) Start() {
method Reset (line 99) | func (t *BackoffTimer) Reset() {
method increaseDuration (line 111) | func (t *BackoffTimer) increaseDuration() {
type BackoffTimerConfig (line 61) | type BackoffTimerConfig struct
FILE: timer_test.go
type TestStdTimer (line 19) | type TestStdTimer struct
method Stop (line 23) | func (t *TestStdTimer) Stop() bool {
method Reset (line 28) | func (t *TestStdTimer) Reset(d time.Duration) bool {
method Chan (line 33) | func (t *TestStdTimer) Chan() <-chan time.Time {
type timerSuite (line 37) | type timerSuite struct
method SetUpTest (line 73) | func (s *timerSuite) SetUpTest(c *gc.C) {
method setup (line 79) | func (s *timerSuite) setup(c *gc.C) {
method TestStart (line 109) | func (s *timerSuite) TestStart(c *gc.C) {
method TestMultipleStarts (line 115) | func (s *timerSuite) TestMultipleStarts(c *gc.C) {
method TestResetNoStart (line 129) | func (s *timerSuite) TestResetNoStart(c *gc.C) {
method TestResetAndStart (line 136) | func (s *timerSuite) TestResetAndStart(c *gc.C) {
method testStart (line 181) | func (s *timerSuite) testStart(c *gc.C, afterFuncCalls int64, duration...
method checkStopCalls (line 192) | func (s *timerSuite) checkStopCalls(c *gc.C, number int) {
type mockClock (line 51) | type mockClock struct
method Now (line 59) | func (c *mockClock) Now() time.Time { return t...
method After (line 60) | func (c *mockClock) After(d time.Duration) <-chan time.Time { return t...
method NewTimer (line 61) | func (c *mockClock) NewTimer(d time.Duration) clock.Timer {
method AfterFunc (line 65) | func (c *mockClock) AfterFunc(d time.Duration, f func()) clock.Timer {
FILE: trivial.go
function ShQuote (line 21) | func ShQuote(s string) string {
function WinPSQuote (line 28) | func WinPSQuote(s string) string {
function WinCmdQuote (line 38) | func WinCmdQuote(s string) string {
function winCmdQuote (line 44) | func winCmdQuote(s string) string {
function winCmdEscapeMeta (line 56) | func winCmdEscapeMeta(str string) string {
function CommandString (line 72) | func CommandString(args ...string) string {
function Gzip (line 101) | func Gzip(data []byte) []byte {
function Gunzip (line 117) | func Gunzip(data []byte) ([]byte, error) {
function ReadSHA256 (line 127) | func ReadSHA256(source io.Reader) (string, int64, error) {
function ReadFileSHA256 (line 139) | func ReadFileSHA256(filename string) (string, int64, error) {
FILE: trivial_test.go
type utilsSuite (line 19) | type utilsSuite struct
method TestCompression (line 25) | func (*utilsSuite) TestCompression(c *gc.C) {
method TestWinCmdQuote (line 58) | func (*utilsSuite) TestWinCmdQuote(c *gc.C) {
method TestWinPSQuote (line 71) | func (*utilsSuite) TestWinPSQuote(c *gc.C) {
method TestCommandString (line 84) | func (*utilsSuite) TestCommandString(c *gc.C) {
method TestReadSHA256AndReadFileSHA256 (line 108) | func (*utilsSuite) TestReadSHA256AndReadFileSHA256(c *gc.C) {
function checkQuoting (line 49) | func checkQuoting(c *gc.C, shQuote func(string) string, tests map[string...
FILE: uptime/uptime_nix.go
function Uptime (line 15) | func Uptime() (int64, error) {
FILE: uptime/uptime_windows.go
function Uptime (line 14) | func Uptime() (int64, error) {
FILE: uptime/zuptime_windows_386.go
function getTickCount64 (line 18) | func getTickCount64() (uptime uint64, err error) {
FILE: uptime/zuptime_windows_amd64.go
function getTickCount64 (line 18) | func getTickCount64() (uptime uint64, err error) {
FILE: username.go
function ResolveSudo (line 15) | func ResolveSudo(username string) string {
function resolveSudo (line 19) | func resolveSudo(username string, getenvFunc func(string) string) string {
function EnvUsername (line 31) | func EnvUsername() (string, error) {
function OSUsername (line 36) | func OSUsername() (string, error) {
function ResolveUsername (line 52) | func ResolveUsername(resolveSudo func(string) string, usernameFuncs ...f...
function LocalUsername (line 71) | func LocalUsername() (string, error) {
FILE: username_test.go
type usernameSuite (line 17) | type usernameSuite struct
method TestResolveUsername (line 21) | func (s *usernameSuite) TestResolveUsername(c *gc.C) {
FILE: uuid.go
type UUID (line 16) | type UUID
method Copy (line 76) | func (uuid UUID) Copy() UUID {
method Raw (line 82) | func (uuid UUID) Raw() [16]byte {
method String (line 88) | func (uuid UUID) String() string {
function UUIDFromString (line 33) | func UUIDFromString(s string) (UUID, error) {
function IsValidUUIDString (line 48) | func IsValidUUIDString(s string) bool {
function MustNewUUID (line 53) | func MustNewUUID() UUID {
function NewUUID (line 62) | func NewUUID() (UUID, error) {
FILE: uuid_test.go
type uuidSuite (line 14) | type uuidSuite struct
method TestUUID (line 20) | func (*uuidSuite) TestUUID(c *gc.C) {
method TestIsValidUUIDFailsWhenNotValid (line 38) | func (*uuidSuite) TestIsValidUUIDFailsWhenNotValid(c *gc.C) {
method TestUUIDFromString (line 74) | func (*uuidSuite) TestUUIDFromString(c *gc.C) {
FILE: voyeur/package_test.go
function TestPackage (line 12) | func TestPackage(t *testing.T) {
FILE: voyeur/value.go
type Value (line 16) | type Value struct
method needsInit (line 36) | func (v *Value) needsInit() bool {
method init (line 40) | func (v *Value) init() {
method Set (line 47) | func (v *Value) Set(val any) {
method Close (line 58) | func (v *Value) Close() error {
method Closed (line 68) | func (v *Value) Closed() bool {
method Get (line 75) | func (v *Value) Get() any {
method Watch (line 82) | func (v *Value) Watch() *Watcher {
function NewValue (line 26) | func NewValue(initial any) *Value {
type Watcher (line 87) | type Watcher struct
method Next (line 98) | func (w *Watcher) Next() bool {
method Close (line 135) | func (w *Watcher) Close() {
method Value (line 145) | func (w *Watcher) Value() any {
FILE: voyeur/value_test.go
type suite (line 14) | type suite struct
method TestValueGetSet (line 46) | func (s *suite) TestValueGetSet(c *gc.C) {
method TestValueInitial (line 55) | func (s *suite) TestValueInitial(c *gc.C) {
method TestValueClose (line 63) | func (s *suite) TestValueClose(c *gc.C) {
method TestWatcher (line 77) | func (s *suite) TestWatcher(c *gc.C) {
method TestDoubleSet (line 114) | func (s *suite) TestDoubleSet(c *gc.C) {
method TestTwoReceivers (line 145) | func (s *suite) TestTwoReceivers(c *gc.C) {
method TestCloseWatcher (line 181) | func (s *suite) TestCloseWatcher(c *gc.C) {
method TestWatchZeroValue (line 213) | func (s *suite) TestWatchZeroValue(c *gc.C) {
function ExampleWatcher_Next (line 20) | func ExampleWatcher_Next() {
FILE: yaml.go
function WriteYaml (line 18) | func WriteYaml(path string, obj any) error {
function ReadYaml (line 62) | func ReadYaml(path string, obj any) error {
function ConformYAML (line 74) | func ConformYAML(input any) (any, error) {
FILE: yaml_test.go
type yamlSuite (line 15) | type yamlSuite struct
method TestYamlRoundTrip (line 20) | func (*yamlSuite) TestYamlRoundTrip(c *gc.C) {
method TestReadYamlReturnsNotFound (line 47) | func (*yamlSuite) TestReadYamlReturnsNotFound(c *gc.C) {
method TestWriteYamlMissingDirectory (line 62) | func (*yamlSuite) TestWriteYamlMissingDirectory(c *gc.C) {
method TestWriteYamlWriteGarbage (line 75) | func (*yamlSuite) TestWriteYamlWriteGarbage(c *gc.C) {
type ConformSuite (line 88) | type ConformSuite struct
method TestConformYAML (line 92) | func (s *ConformSuite) TestConformYAML(c *gc.C) {
FILE: zfile_windows.go
function moveFileEx (line 18) | func moveFileEx(lpExistingFileName *uint16, lpNewFileName *uint16, dwFla...
FILE: zip/package_test.go
function TestPackage (line 12) | func TestPackage(t *testing.T) {
FILE: zip/zip.go
function FindAll (line 18) | func FindAll(reader *zip.Reader) ([]string, error) {
function Find (line 24) | func Find(reader *zip.Reader, pattern string) ([]string, error) {
function ExtractAll (line 43) | func ExtractAll(reader *zip.Reader, targetRoot string) error {
function Extract (line 51) | func Extract(reader *zip.Reader, targetRoot, sourceRoot string) error {
type extractor (line 69) | type extractor struct
method targetPath (line 76) | func (x extractor) targetPath(zipFile *zip.File) (string, bool) {
method extract (line 96) | func (x extractor) extract(zipFile *zip.File) error {
method writeDir (line 119) | func (x extractor) writeDir(targetPath string, modePerm os.FileMode) e...
method writeFile (line 139) | func (x extractor) writeFile(targetPath string, zipFile *zip.File, mod...
method writeSymlink (line 165) | func (x extractor) writeSymlink(targetPath string, zipFile *zip.File) ...
method checkSymlink (line 178) | func (x extractor) checkSymlink(targetPath string, zipFile *zip.File) ...
function copyTo (line 199) | func copyTo(writer io.Writer, zipFile *zip.File) error {
function isSanePath (line 209) | func isSanePath(path string) bool {
FILE: zip/zip_test.go
type ZipSuite (line 24) | type ZipSuite struct
method makeZip (line 30) | func (s *ZipSuite) makeZip(c *gc.C, entries ...ft.Entry) *stdzip.Reader {
method TestFind (line 55) | func (s *ZipSuite) TestFind(c *gc.C) {
method TestFindError (line 116) | func (s *ZipSuite) TestFindError(c *gc.C) {
method TestExtractAll (line 122) | func (s *ZipSuite) TestExtractAll(c *gc.C) {
method TestExtractAllOverwriteFiles (line 142) | func (s *ZipSuite) TestExtractAllOverwriteFiles(c *gc.C) {
method TestExtractAllOverwriteSymlinks (line 159) | func (s *ZipSuite) TestExtractAllOverwriteSymlinks(c *gc.C) {
method TestExtractAllOverwriteDirs (line 179) | func (s *ZipSuite) TestExtractAllOverwriteDirs(c *gc.C) {
method TestExtractAllMergeDirs (line 196) | func (s *ZipSuite) TestExtractAllMergeDirs(c *gc.C) {
method TestExtractAllSymlinkErrors (line 223) | func (s *ZipSuite) TestExtractAllSymlinkErrors(c *gc.C) {
method TestExtractDir (line 252) | func (s *ZipSuite) TestExtractDir(c *gc.C) {
method TestExtractSingleFile (line 289) | func (s *ZipSuite) TestExtractSingleFile(c *gc.C) {
method TestClosesFile (line 306) | func (s *ZipSuite) TestClosesFile(c *gc.C) {
method TestExtractSymlinkErrors (line 319) | func (s *ZipSuite) TestExtractSymlinkErrors(c *gc.C) {
method TestExtractSourceError (line 353) | func (s *ZipSuite) TestExtractSourceError(c *gc.C) {
Condensed preview — 204 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (613K chars).
[
{
"path": ".gitignore",
"chars": 287,
"preview": "# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.so\n*.dylib\n\n# Test binary, built with `go test -c`\n*.test\n\n# Ou"
},
{
"path": "ISSUE_TEMPLATE.md",
"chars": 102,
"preview": "## Issues tracked in Launchpad\n\nPlease file an issue against https://bugs.launchpad.net/juju/+filebug\n"
},
{
"path": "LICENSE",
"chars": 9025,
"preview": "All files in this repository are licensed as follows. If you contribute\nto this repository, it is assumed that you licen"
},
{
"path": "LICENSE.golang",
"chars": 1573,
"preview": "This licence applies to the following files:\n\n* filepath/stdlib.go\n* filepath/stdlibmatch.go\n\nCopyright (c) 2010 The Go "
},
{
"path": "Makefile",
"chars": 1526,
"preview": "PROJECT := github.com/juju/utils/v4\n\n.PHONY: check-licence check-go check\n\ncheck: check-licence check-go\n\tgo test -v $(P"
},
{
"path": "README.md",
"chars": 87,
"preview": "juju/utils\n============\n\nThis package provides general utility packages and functions.\n"
},
{
"path": "SECURITY.md",
"chars": 838,
"preview": "# Security policy\n\n## Reporting a vulnerability\n\nPlease provide a description of the issue, the steps you took to\ncreate"
},
{
"path": "arch/arch.go",
"chars": 2304,
"preview": "// Copyright 2014-2016 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage arch\n\nimport "
},
{
"path": "arch/arch_test.go",
"chars": 1386,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage arch_test\n\nimport "
},
{
"path": "arch/package_test.go",
"chars": 205,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage arch_test\n\nimport "
},
{
"path": "attempt.go",
"chars": 1943,
"preview": "// Copyright 2011, 2012, 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils\n"
},
{
"path": "attempt_test.go",
"chars": 2430,
"preview": "// Copyright 2011, 2012, 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils_"
},
{
"path": "bzr/bzr.go",
"chars": 4475,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\n// Package bzr offers an i"
},
{
"path": "bzr/bzr_test.go",
"chars": 4312,
"preview": "// Copyright 2014 Canonical Ltd.\n// Copyright 2014 Cloudbase Solutions SRL\n// Licensed under the LGPLv3, see LICENCE fil"
},
{
"path": "bzr/bzr_unix_test.go",
"chars": 220,
"preview": "// Copyright 2014 Canonical Ltd.\n// Copyright 2014 Cloudbase Solutions SRL\n// Licensed under the LGPLv3, see LICENCE fil"
},
{
"path": "bzr/bzr_windows_test.go",
"chars": 221,
"preview": "// Copyright 2014 Canonical Ltd.\n// Copyright 2014 Cloudbase Solutions SRL\n// Licensed under the LGPLv3, see LICENCE fil"
},
{
"path": "cache/cache.go",
"chars": 5541,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\n// Package cache provides "
},
{
"path": "cache/cache_test.go",
"chars": 7963,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage cache_test\n\nimport"
},
{
"path": "cache/export_test.go",
"chars": 194,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage cache\n\nvar GetAtTi"
},
{
"path": "cache/package_test.go",
"chars": 213,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage cache_test\n\nimport"
},
{
"path": "cert/cert.go",
"chars": 2494,
"preview": "// Copyright 2012, 2013 Canonical Ltd.\n// Copyright 2016 Cloudbase solutions\n// Licensed under the LGPLv3, see LICENCE f"
},
{
"path": "cert/cert_test.go",
"chars": 2491,
"preview": "// Copyright 2012, 2013 Canonical Ltd.\n// Copyright 2016 Cloudbase solutions\n// Licensed under the LGPLv3, see LICENCE f"
},
{
"path": "cert/exports_test.go",
"chars": 145,
"preview": "// Copyright 2016 Canonical ltd.\n// Copyright 2016 Cloudbase solutions\n// Licensed under the LGPLv3, see LICENCE file fo"
},
{
"path": "command.go",
"chars": 434,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils\n\nimport (\n\t\""
},
{
"path": "command_test.go",
"chars": 1753,
"preview": "// Copyright 2012, 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils_test\n\n"
},
{
"path": "context.go",
"chars": 2541,
"preview": "// Copyright 2018 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils\n\nimport (\n\t\""
},
{
"path": "context_test.go",
"chars": 3753,
"preview": "// Copyright 2018 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils_test\n\nimport"
},
{
"path": "du/LICENSE.ricochet2200",
"chars": 1210,
"preview": "This is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, c"
},
{
"path": "du/diskusage.go",
"chars": 1249,
"preview": "// Copied from https://github.com/ricochet2200/go-disk-usage\n// Copyright 2011 Rick Smith.\n// Use of this source code is"
},
{
"path": "du/diskusage_windows.go",
"chars": 1432,
"preview": "// Copied from https://github.com/ricochet2200/go-disk-usage\n// Copyright 2011 Rick Smith.\n// Use of this source code is"
},
{
"path": "errors.go",
"chars": 888,
"preview": "// Copyright 2024 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils\n\nimport (\n\t\""
},
{
"path": "exec/exec.go",
"chars": 8020,
"preview": "// Copyright 2016 Canonical Ltd.\n// Copyright 2016 Cloudbase Solutions\n// Licensed under the LGPLv3, see LICENCE file fo"
},
{
"path": "exec/exec_internal_test.go",
"chars": 1691,
"preview": "// Copyright 2017 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage exec\n\nimport (\n\t\"o"
},
{
"path": "exec/exec_linux_test.go",
"chars": 2728,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage exec_test\n\nimport "
},
{
"path": "exec/exec_test.go",
"chars": 1925,
"preview": "// Copyright 2016 Canonical Ltd.\n// Copyright 2016 Cloudbase Solutions\n// Licensed under the LGPLv3, see LICENCE file fo"
},
{
"path": "exec/exec_unix.go",
"chars": 957,
"preview": "// Copyright 2016 Canonical Ltd.\n// Copyright 2016 Cloudbase Solutions SRL\n// Licensed under the LGPLv3, see LICENCE fil"
},
{
"path": "exec/exec_windows.go",
"chars": 413,
"preview": "// Copyright 2016 Canonical Ltd.\n// Copyright 2016 Cloudbase Solutions SRL\n// Licensed under the LGPLv3, see LICENCE fil"
},
{
"path": "exec/exec_windows_test.go",
"chars": 3586,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage exec_test\n\nimport "
},
{
"path": "exec/package_test.go",
"chars": 205,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage exec_test\n\nimport "
},
{
"path": "export_test.go",
"chars": 330,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils\n\nimport (\n\t\""
},
{
"path": "file.go",
"chars": 5058,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils\n\nimport (\n\t\""
},
{
"path": "file_test.go",
"chars": 7230,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils_test\n\nimport"
},
{
"path": "file_unix.go",
"chars": 2502,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\n//go:build !windows\n// +bu"
},
{
"path": "file_unix_test.go",
"chars": 1960,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\n//go:build !windows\n// +bu"
},
{
"path": "file_windows.go",
"chars": 4328,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\n//go:build windows\n// +bui"
},
{
"path": "file_windows_test.go",
"chars": 1401,
"preview": "// Copyright 2013 Canonical Ltd.\n// Copyright 2014 Cloudbase Solutions SRL\n// Licensed under the LGPLv3, see LICENCE fil"
},
{
"path": "filepath/common.go",
"chars": 286,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage filepath\n\nfunc spl"
},
{
"path": "filepath/common_test.go",
"chars": 1186,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage filepath_test\n\nimp"
},
{
"path": "filepath/export_test.go",
"chars": 147,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage filepath\n\nvar (\n\tS"
},
{
"path": "filepath/filepath.go",
"chars": 2414,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage filepath\n\nimport ("
},
{
"path": "filepath/filepath_test.go",
"chars": 2441,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage filepath_test\n\nimp"
},
{
"path": "filepath/interface_test.go",
"chars": 191,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage filepath\n\nvar _ Re"
},
{
"path": "filepath/package_test.go",
"chars": 209,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage filepath_test\n\nimp"
},
{
"path": "filepath/stdlib.go",
"chars": 4982,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n// Copyright 2009 The Go Au"
},
{
"path": "filepath/stdlib_test.go",
"chars": 4673,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage filepath_test\n\nimp"
},
{
"path": "filepath/stdlibmatch.go",
"chars": 5332,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n// Copyright 2009 The Go Au"
},
{
"path": "filepath/unix.go",
"chars": 2269,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage filepath\n\nimport ("
},
{
"path": "filepath/unix_test.go",
"chars": 6262,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage filepath_test\n\nimp"
},
{
"path": "filepath/win.go",
"chars": 3808,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage filepath\n\nimport ("
},
{
"path": "filepath/win_test.go",
"chars": 6493,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage filepath_test\n\nimp"
},
{
"path": "filestorage/doc.go",
"chars": 1654,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\n/*\nutils/filestorage provi"
},
{
"path": "filestorage/export_test.go",
"chars": 114,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage filestorage\n"
},
{
"path": "filestorage/fakes_test.go",
"chars": 3847,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage filestorage_test\n\n"
},
{
"path": "filestorage/interfaces.go",
"chars": 4810,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage filestorage\n\nimpor"
},
{
"path": "filestorage/metadata.go",
"chars": 2738,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage filestorage\n\nimpor"
},
{
"path": "filestorage/metadata_store.go",
"chars": 1703,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage filestorage\n\nimpor"
},
{
"path": "filestorage/metadata_test.go",
"chars": 2610,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage filestorage_test\n\n"
},
{
"path": "filestorage/package_test.go",
"chars": 219,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage filestorage_test\n\n"
},
{
"path": "filestorage/wrapper.go",
"chars": 4850,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage filestorage\n\nimpor"
},
{
"path": "filestorage/wrapper_test.go",
"chars": 5557,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage filestorage_test\n\n"
},
{
"path": "fs/copy.go",
"chars": 2545,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage fs\n\nimport (\n\t\"fmt"
},
{
"path": "fs/copy_test.go",
"chars": 1675,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage fs_test\n\nimport (\n"
},
{
"path": "go.mod",
"chars": 774,
"preview": "module github.com/juju/utils/v4\n\ngo 1.24.4\n\nrequire (\n\tgithub.com/juju/clock v1.0.3\n\tgithub.com/juju/collections v1.0.4\n"
},
{
"path": "go.sum",
"chars": 4422,
"preview": "github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/golang/mock v1.5.0 h1:jlY"
},
{
"path": "gomaxprocs.go",
"chars": 649,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils\n\nimport (\n\t\""
},
{
"path": "gomaxprocs_test.go",
"chars": 1286,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils_test\n\nimport"
},
{
"path": "hash/fingerprint.go",
"chars": 3413,
"preview": "// Copyright 2016 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage hash\n\nimport (\n\t\"e"
},
{
"path": "hash/fingerprint_test.go",
"chars": 4209,
"preview": "// Copyright 2016 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage hash_test\n\nimport "
},
{
"path": "hash/hash.go",
"chars": 2160,
"preview": "// Copyright 2016 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\n// The hash package provid"
},
{
"path": "hash/hash_test.go",
"chars": 1803,
"preview": "// Copyright 2016 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage hash_test\n\nimport "
},
{
"path": "hash/package_test.go",
"chars": 219,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage hash_test\n\nimport "
},
{
"path": "hash/writer.go",
"chars": 1393,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage hash\n\nimport (\n\t\"e"
},
{
"path": "hash/writer_test.go",
"chars": 2056,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage hash_test\n\nimport "
},
{
"path": "home_unix.go",
"chars": 738,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n//go:build !windows\n// +bui"
},
{
"path": "home_unix_test.go",
"chars": 681,
"preview": "// Copyright 2011, 2012, 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n//go:build !win"
},
{
"path": "home_windows.go",
"chars": 565,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils\n\nimport (\n\t\""
},
{
"path": "home_windows_test.go",
"chars": 919,
"preview": "// Copyright 2011, 2012, 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils_"
},
{
"path": "isubuntu.go",
"chars": 364,
"preview": "// Copyright 2011, 2012, 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils\n"
},
{
"path": "isubuntu_test.go",
"chars": 1164,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils_test\n\nimport"
},
{
"path": "jsonhttp/jsonhttp.go",
"chars": 4635,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\n// Package jsonhttp provid"
},
{
"path": "jsonhttp/jsonhttp_test.go",
"chars": 5143,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage jsonhttp_test\n\nimp"
},
{
"path": "jsonhttp/package_test.go",
"chars": 216,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage jsonhttp_test\n\nimp"
},
{
"path": "keyvalues/keyvalues.go",
"chars": 1337,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\n// The keyvalues package i"
},
{
"path": "keyvalues/keyvalues_test.go",
"chars": 3892,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage keyvalues_test\n\nim"
},
{
"path": "keyvalues/package_test.go",
"chars": 217,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage keyvalues_test\n\nim"
},
{
"path": "limiter.go",
"chars": 2605,
"preview": "// Copyright 2011, 2012, 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils\n"
},
{
"path": "limiter_test.go",
"chars": 2806,
"preview": "// Copyright 2011, 2012, 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils_"
},
{
"path": "multireader.go",
"chars": 4138,
"preview": "// Copyright 2016 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils\n\nimport (\n\t\""
},
{
"path": "multireader_test.go",
"chars": 5643,
"preview": "// Copyright 2016 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils_test\n\nimport"
},
{
"path": "naturalsort.go",
"chars": 2039,
"preview": "// Copyright 2016 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils\n\nimport (\n\t\""
},
{
"path": "naturalsort_test.go",
"chars": 2976,
"preview": "// Copyright 2016 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils_test\n\nimport"
},
{
"path": "network.go",
"chars": 2371,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils\n\nimport (\n\t\""
},
{
"path": "network_test.go",
"chars": 2609,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils_test\n\nimport"
},
{
"path": "os.go",
"chars": 889,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils\n\n// These ar"
},
{
"path": "os_test.go",
"chars": 734,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils_test\n\nimport"
},
{
"path": "package_test.go",
"chars": 213,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils_test\n\nimport"
},
{
"path": "parallel/package_test.go",
"chars": 216,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage parallel_test\n\nimp"
},
{
"path": "parallel/parallel.go",
"chars": 2069,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\n// The parallel package pr"
},
{
"path": "parallel/parallel_test.go",
"chars": 2899,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage parallel_test\n\nimp"
},
{
"path": "parallel/try.go",
"chars": 4872,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage parallel\n\nimport ("
},
{
"path": "parallel/try_test.go",
"chars": 8506,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage parallel_test\n\nimp"
},
{
"path": "password.go",
"chars": 3150,
"preview": "// Copyright 2012, 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils\n\nimpor"
},
{
"path": "password_test.go",
"chars": 2618,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils_test\n\nimport"
},
{
"path": "proxy/package_test.go",
"chars": 213,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage proxy_test\n\nimport"
},
{
"path": "proxy/proxy.go",
"chars": 3810,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage proxy\n\nimport (\n\t\""
},
{
"path": "proxy/proxy_test.go",
"chars": 7458,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage proxy_test\n\nimport"
},
{
"path": "randomstring.go",
"chars": 862,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils\n\nimport (\n\t\""
},
{
"path": "randomstring_test.go",
"chars": 770,
"preview": "// Copyright 2015 Canonical Ltd.\n// Copyright 2015 Cloudbase Solutions SRL\n// Licensed under the LGPLv3, see LICENCE fil"
},
{
"path": "registry/export_test.go",
"chars": 177,
"preview": "// Copyright 2012, 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage registry\n\nva"
},
{
"path": "registry/package_test.go",
"chars": 212,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage registry_test\n\nimp"
},
{
"path": "registry/registry.go",
"chars": 3289,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage registry\n\nimport ("
},
{
"path": "registry/registry_test.go",
"chars": 3986,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage registry_test\n\nimp"
},
{
"path": "relativeurl.go",
"chars": 1923,
"preview": "// Copyright 2016 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils\n\nimport (\n\t\""
},
{
"path": "relativeurl_test.go",
"chars": 3025,
"preview": "// Copyright 2016 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils_test\n\nimport"
},
{
"path": "setenv.go",
"chars": 821,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils\n\nimport (\n\t\""
},
{
"path": "setenv_test.go",
"chars": 804,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils_test\n\nimport"
},
{
"path": "shell/bash.go",
"chars": 437,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage shell\n\nimport (\n\t\""
},
{
"path": "shell/bash_test.go",
"chars": 2983,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage shell_test\n\nimport"
},
{
"path": "shell/command.go",
"chars": 1719,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage shell\n\nimport (\n\t\""
},
{
"path": "shell/interface_test.go",
"chars": 231,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage shell\n\nvar _ Rende"
},
{
"path": "shell/output.go",
"chars": 2733,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage shell\n\nimport (\n\t\""
},
{
"path": "shell/package_test.go",
"chars": 206,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage shell_test\n\nimport"
},
{
"path": "shell/powershell.go",
"chars": 3009,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage shell\n\nimport (\n\t\""
},
{
"path": "shell/powershell_test.go",
"chars": 2042,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage shell_test\n\nimport"
},
{
"path": "shell/renderer.go",
"chars": 1648,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage shell\n\nimport (\n\t\""
},
{
"path": "shell/renderer_test.go",
"chars": 2420,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage shell_test\n\nimport"
},
{
"path": "shell/script.go",
"chars": 2606,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage shell\n\nimport (\n\t\""
},
{
"path": "shell/script_test.go",
"chars": 2453,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage shell_test\n\nimport"
},
{
"path": "shell/unix.go",
"chars": 2966,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage shell\n\nimport (\n\t\""
},
{
"path": "shell/win.go",
"chars": 1496,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage shell\n\nimport (\n\t\""
},
{
"path": "shell/wincmd.go",
"chars": 1533,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage shell\n\nimport (\n\t\""
},
{
"path": "shell/wincmd_test.go",
"chars": 1598,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage shell_test\n\nimport"
},
{
"path": "size.go",
"chars": 1955,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils\n\nimport (\n\t\""
},
{
"path": "size_test.go",
"chars": 3086,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils_test\n\nimport"
},
{
"path": "ssh/authorisedkeys.go",
"chars": 11443,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage ssh\n\nimport (\n\t\"fm"
},
{
"path": "ssh/authorisedkeys_test.go",
"chars": 12527,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage ssh_test\n\nimport ("
},
{
"path": "ssh/clientkeys.go",
"chars": 4805,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage ssh\n\nimport (\n\t\"fm"
},
{
"path": "ssh/clientkeys_test.go",
"chars": 3794,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage ssh_test\n\nimport ("
},
{
"path": "ssh/export_test.go",
"chars": 1064,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage ssh\n\nimport (\n\t\"sy"
},
{
"path": "ssh/fakes_test.go",
"chars": 3026,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage ssh_test\n\nimport ("
},
{
"path": "ssh/fingerprint.go",
"chars": 833,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage ssh\n\nimport (\n\t\"by"
},
{
"path": "ssh/fingerprint_test.go",
"chars": 941,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage ssh_test\n\nimport ("
},
{
"path": "ssh/generate.go",
"chars": 1746,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage ssh\n\nimport (\n\t\"cr"
},
{
"path": "ssh/generate_test.go",
"chars": 2016,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage ssh_test\n\nimport ("
},
{
"path": "ssh/package_test.go",
"chars": 211,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage ssh_test\n\nimport ("
},
{
"path": "ssh/run.go",
"chars": 4690,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage ssh\n\nimport (\n\t\"by"
},
{
"path": "ssh/run_test.go",
"chars": 3423,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage ssh_test\n\nimport ("
},
{
"path": "ssh/ssh.go",
"chars": 9563,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\n// Package ssh contains ut"
},
{
"path": "ssh/ssh_gocrypto.go",
"chars": 13488,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage ssh\n\nimport (\n\t\"by"
},
{
"path": "ssh/ssh_gocrypto_test.go",
"chars": 18308,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage ssh_test\n\nimport ("
},
{
"path": "ssh/ssh_openssh.go",
"chars": 5730,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage ssh\n\nimport (\n\t\"by"
},
{
"path": "ssh/ssh_test.go",
"chars": 9010,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\n//go:build !windows\n// +bu"
},
{
"path": "ssh/stream.go",
"chars": 913,
"preview": "// Copyright 2016 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage ssh\n\nimport (\n\t\"by"
},
{
"path": "ssh/stream_test.go",
"chars": 1211,
"preview": "// Copyright 2016 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage ssh_test\n\nimport ("
},
{
"path": "ssh/stream_wrapper_unix.go",
"chars": 292,
"preview": "// Copyright 2016 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\n//go:build !windows\n// +bu"
},
{
"path": "ssh/stream_wrapper_windows.go",
"chars": 272,
"preview": "// Copyright 2016 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage ssh\n\nimport (\n\t\"io"
},
{
"path": "ssh/testing/keys.go",
"chars": 3682,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage testing\n\ntype SSHK"
},
{
"path": "symlink/export_test.go",
"chars": 171,
"preview": "// Copyright 2014 Cloudbase Solutions SRL\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage symlink\n\n"
},
{
"path": "symlink/symlink.go",
"chars": 1033,
"preview": "// Copyright 2014 Cloudbase Solutions SRL\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage symlink\n\n"
},
{
"path": "symlink/symlink_posix.go",
"chars": 818,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\n//go:build linux || darwin"
},
{
"path": "symlink/symlink_test.go",
"chars": 2982,
"preview": "// Copyright 2014 Cloudbase Solutions SRL\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage symlink_t"
},
{
"path": "symlink/symlink_windows.go",
"chars": 4026,
"preview": "// Copyright 2014 Canonical Ltd.\n// Copyright 2014 Cloudbase Solutions SRL\n// Licensed under the LGPLv3, see LICENCE fil"
},
{
"path": "symlink/symlink_windows_test.go",
"chars": 1395,
"preview": "// Copyright 2014 Canonical Ltd.\n// Copyright 2014 Cloudbase Solutions SRL\n// Licensed under the LGPLv3, see LICENCE fil"
},
{
"path": "symlink/zsymlink_windows_386.go",
"chars": 1259,
"preview": "// Copyright 2014 Canonical Ltd.\n// Copyright 2014 Cloudbase Solutions SRL\n// Licensed under the LGPLv3, see LICENCE fil"
},
{
"path": "symlink/zsymlink_windows_amd64.go",
"chars": 1254,
"preview": "// Copyright 2014 Canonical Ltd.\n// Copyright 2014 Cloudbase Solutions SRL\n// Licensed under the LGPLv3, see LICENCE fil"
},
{
"path": "systemerrmessages_unix.go",
"chars": 570,
"preview": "// Copyright 2014 Canonical Ltd.\n// Copyright 2014 Cloudbase Solutions SRL\n// Licensed under the LGPLv3, see LICENCE fil"
},
{
"path": "systemerrmessages_windows.go",
"chars": 600,
"preview": "// Copyright 2014 Canonical Ltd.\n// Copyright 2014 Cloudbase Solutions SRL\n// Licensed under the LGPLv3, see LICENCE fil"
},
{
"path": "tailer/export_test.go",
"chars": 174,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage tailer\n\nvar (\n\tBuf"
},
{
"path": "tailer/package_test.go",
"chars": 214,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage tailer_test\n\nimpor"
},
{
"path": "tailer/tailer.go",
"chars": 6208,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage tailer\n\nimport (\n\t"
},
{
"path": "tailer/tailer_test.go",
"chars": 13091,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage tailer_test\n\nimpor"
},
{
"path": "tar/tar.go",
"chars": 6408,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\n// This package provides c"
},
{
"path": "tar/tar_test.go",
"chars": 9490,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage tar\n\nimport (\n\t\"ar"
},
{
"path": "timer.go",
"chars": 3626,
"preview": "// Copyright 2015 Canonical Ltd.\n// Copyright 2015 Cloudbase Solutions SRL\n// Licensed under the LGPLv3, see LICENCE fil"
},
{
"path": "timer_test.go",
"chars": 4789,
"preview": "// Copyright 2015 Canonical Ltd.\n// Copyright 2015 Cloudbase Solutions SRL\n// Licensed under the LGPLv3, see LICENCE fil"
},
{
"path": "trivial.go",
"chars": 3775,
"preview": "// Copyright 2012, 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils\n\nimpor"
},
{
"path": "trivial_test.go",
"chars": 4041,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils_test\n\nimport"
},
{
"path": "uptime/uptime_nix.go",
"chars": 446,
"preview": "// Copyright 2014 Cloudbase Solutions SRL\n// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE fil"
},
{
"path": "uptime/uptime_windows.go",
"chars": 498,
"preview": "// Copyright 2014 Cloudbase Solutions SRL\n// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE fil"
},
{
"path": "uptime/zuptime_windows_386.go",
"chars": 638,
"preview": "// Copyright 2014 Canonical Ltd.\n// Copyright 2014 Cloudbase Solutions SRL\n// Licensed under the LGPLv3, see LICENCE fil"
},
{
"path": "uptime/zuptime_windows_amd64.go",
"chars": 633,
"preview": "// Copyright 2014 Canonical Ltd.\n// Copyright 2014 Cloudbase Solutions SRL\n// Licensed under the LGPLv3, see LICENCE fil"
},
{
"path": "username.go",
"chars": 2323,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils\n\nimport (\n\t\""
},
{
"path": "username_test.go",
"chars": 1719,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils_test\n\nimport"
},
{
"path": "uuid.go",
"chars": 2189,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils\n\nimport (\n\t\""
},
{
"path": "uuid_test.go",
"chars": 1717,
"preview": "// Copyright 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils_test\n\nimport"
},
{
"path": "voyeur/package_test.go",
"chars": 209,
"preview": "// Copyright 2014 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage voyeur\n\nimport (\n\t"
},
{
"path": "voyeur/value.go",
"chars": 3312,
"preview": "// Copyright 2012, 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\n// Package voyeur im"
},
{
"path": "voyeur/value_test.go",
"chars": 4451,
"preview": "// Copyright 2012, 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage voyeur\n\nimpo"
},
{
"path": "yaml.go",
"chars": 3215,
"preview": "// Copyright 2012, 2013 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils\n\nimpor"
},
{
"path": "yaml_test.go",
"chars": 4772,
"preview": "// Copyright 2015 Canonical Ltd.\n// Licensed under the LGPLv3, see LICENCE file for details.\n\npackage utils\n\nimport (\n\t\""
}
]
// ... and 4 more files (download for full content)
About this extraction
This page contains the full source code of the juju/utils GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 204 files (543.2 KB), approximately 162.6k tokens, and a symbol index with 1319 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.