[mqtt] Implements mqtt publish tooling
This commit is contained in:
parent
1b62903474
commit
17beea7e8a
284
.gitignore
vendored
Normal file
284
.gitignore
vendored
Normal file
@ -0,0 +1,284 @@
|
|||||||
|
# Created by .ignore support plugin (hsz.mobi)
|
||||||
|
### Emacs template
|
||||||
|
# -*- mode: gitignore; -*-
|
||||||
|
*~
|
||||||
|
\#*\#
|
||||||
|
/.emacs.desktop
|
||||||
|
/.emacs.desktop.lock
|
||||||
|
*.elc
|
||||||
|
auto-save-list
|
||||||
|
tramp
|
||||||
|
.\#*
|
||||||
|
|
||||||
|
# Org-mode
|
||||||
|
.org-id-locations
|
||||||
|
*_archive
|
||||||
|
|
||||||
|
# flymake-mode
|
||||||
|
*_flymake.*
|
||||||
|
|
||||||
|
# eshell files
|
||||||
|
/eshell/history
|
||||||
|
/eshell/lastdir
|
||||||
|
|
||||||
|
# elpa packages
|
||||||
|
/elpa/
|
||||||
|
|
||||||
|
# reftex files
|
||||||
|
*.rel
|
||||||
|
|
||||||
|
# AUCTeX auto folder
|
||||||
|
/auto/
|
||||||
|
|
||||||
|
# cask packages
|
||||||
|
.cask/
|
||||||
|
dist/
|
||||||
|
|
||||||
|
# Flycheck
|
||||||
|
flycheck_*.el
|
||||||
|
|
||||||
|
# server auth directory
|
||||||
|
/server/
|
||||||
|
|
||||||
|
# projectiles files
|
||||||
|
.projectile
|
||||||
|
|
||||||
|
# directory configuration
|
||||||
|
.dir-locals.el
|
||||||
|
|
||||||
|
# network security
|
||||||
|
/network-security.data
|
||||||
|
|
||||||
|
|
||||||
|
### JetBrains template
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff
|
||||||
|
.idea/**/workspace.xml
|
||||||
|
.idea/**/tasks.xml
|
||||||
|
.idea/**/usage.statistics.xml
|
||||||
|
.idea/**/dictionaries
|
||||||
|
.idea/**/shelf
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
.idea/**/contentModel.xml
|
||||||
|
|
||||||
|
# Sensitive or high-churn files
|
||||||
|
.idea/**/dataSources/
|
||||||
|
.idea/**/dataSources.ids
|
||||||
|
.idea/**/dataSources.local.xml
|
||||||
|
.idea/**/sqlDataSources.xml
|
||||||
|
.idea/**/dynamic.xml
|
||||||
|
.idea/**/uiDesigner.xml
|
||||||
|
.idea/**/dbnavigator.xml
|
||||||
|
|
||||||
|
# Gradle
|
||||||
|
.idea/**/gradle.xml
|
||||||
|
.idea/**/libraries
|
||||||
|
|
||||||
|
# Gradle and Maven with auto-import
|
||||||
|
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||||
|
# since they will be recreated, and may cause churn. Uncomment if using
|
||||||
|
# auto-import.
|
||||||
|
# .idea/artifacts
|
||||||
|
# .idea/compiler.xml
|
||||||
|
# .idea/modules.xml
|
||||||
|
# .idea/*.iml
|
||||||
|
# .idea/modules
|
||||||
|
# *.iml
|
||||||
|
# *.ipr
|
||||||
|
|
||||||
|
# CMake
|
||||||
|
cmake-build-*/
|
||||||
|
|
||||||
|
# Mongo Explorer plugin
|
||||||
|
.idea/**/mongoSettings.xml
|
||||||
|
|
||||||
|
# File-based project format
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
out/
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
.idea_modules/
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Cursive Clojure plugin
|
||||||
|
.idea/replstate.xml
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
com_crashlytics_export_strings.xml
|
||||||
|
crashlytics.properties
|
||||||
|
crashlytics-build.properties
|
||||||
|
fabric.properties
|
||||||
|
|
||||||
|
# Editor-based Rest Client
|
||||||
|
.idea/httpRequests
|
||||||
|
|
||||||
|
# Android studio 3.1+ serialized cache file
|
||||||
|
.idea/caches/build_file_checksums.ser
|
||||||
|
|
||||||
|
### Vim template
|
||||||
|
# Swap
|
||||||
|
[._]*.s[a-v][a-z]
|
||||||
|
!*.svg # comment out if you don't need vector files
|
||||||
|
[._]*.sw[a-p]
|
||||||
|
[._]s[a-rt-v][a-z]
|
||||||
|
[._]ss[a-gi-z]
|
||||||
|
[._]sw[a-p]
|
||||||
|
|
||||||
|
# Session
|
||||||
|
Session.vim
|
||||||
|
Sessionx.vim
|
||||||
|
|
||||||
|
# Temporary
|
||||||
|
.netrwhist
|
||||||
|
*~
|
||||||
|
# Auto-generated tag files
|
||||||
|
tags
|
||||||
|
# Persistent undo
|
||||||
|
[._]*.un~
|
||||||
|
|
||||||
|
### macOS template
|
||||||
|
# General
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Icon must end with two \r
|
||||||
|
Icon
|
||||||
|
|
||||||
|
# Thumbnails
|
||||||
|
._*
|
||||||
|
|
||||||
|
# Files that might appear in the root of a volume
|
||||||
|
.DocumentRevisions-V100
|
||||||
|
.fseventsd
|
||||||
|
.Spotlight-V100
|
||||||
|
.TemporaryItems
|
||||||
|
.Trashes
|
||||||
|
.VolumeIcon.icns
|
||||||
|
.com.apple.timemachine.donotpresent
|
||||||
|
|
||||||
|
# Directories potentially created on remote AFP share
|
||||||
|
.AppleDB
|
||||||
|
.AppleDesktop
|
||||||
|
Network Trash Folder
|
||||||
|
Temporary Items
|
||||||
|
.apdisk
|
||||||
|
|
||||||
|
### Xcode template
|
||||||
|
# Xcode
|
||||||
|
#
|
||||||
|
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
|
||||||
|
|
||||||
|
## User settings
|
||||||
|
xcuserdata/
|
||||||
|
|
||||||
|
## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
|
||||||
|
*.xcscmblueprint
|
||||||
|
*.xccheckout
|
||||||
|
|
||||||
|
## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
|
||||||
|
build/
|
||||||
|
DerivedData/
|
||||||
|
*.moved-aside
|
||||||
|
*.pbxuser
|
||||||
|
!default.pbxuser
|
||||||
|
*.mode1v3
|
||||||
|
!default.mode1v3
|
||||||
|
*.mode2v3
|
||||||
|
!default.mode2v3
|
||||||
|
*.perspectivev3
|
||||||
|
!default.perspectivev3
|
||||||
|
|
||||||
|
## Gcc Patch
|
||||||
|
/*.gcno
|
||||||
|
|
||||||
|
### Go template
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# Dependency directories (remove the comment below to include it)
|
||||||
|
# vendor/
|
||||||
|
|
||||||
|
### Eclipse template
|
||||||
|
.metadata
|
||||||
|
bin/
|
||||||
|
tmp/
|
||||||
|
*.tmp
|
||||||
|
*.bak
|
||||||
|
*.swp
|
||||||
|
*~.nib
|
||||||
|
local.properties
|
||||||
|
.settings/
|
||||||
|
.loadpath
|
||||||
|
.recommenders
|
||||||
|
|
||||||
|
# External tool builders
|
||||||
|
.externalToolBuilders/
|
||||||
|
|
||||||
|
# Locally stored "Eclipse launch configurations"
|
||||||
|
*.launch
|
||||||
|
|
||||||
|
# PyDev specific (Python IDE for Eclipse)
|
||||||
|
*.pydevproject
|
||||||
|
|
||||||
|
# CDT-specific (C/C++ Development Tooling)
|
||||||
|
.cproject
|
||||||
|
|
||||||
|
# CDT- autotools
|
||||||
|
.autotools
|
||||||
|
|
||||||
|
# Java annotation processor (APT)
|
||||||
|
.factorypath
|
||||||
|
|
||||||
|
# PDT-specific (PHP Development Tools)
|
||||||
|
.buildpath
|
||||||
|
|
||||||
|
# sbteclipse plugin
|
||||||
|
.target
|
||||||
|
|
||||||
|
# Tern plugin
|
||||||
|
.tern-project
|
||||||
|
|
||||||
|
# TeXlipse plugin
|
||||||
|
.texlipse
|
||||||
|
|
||||||
|
# STS (Spring Tool Suite)
|
||||||
|
.springBeans
|
||||||
|
|
||||||
|
# Code Recommenders
|
||||||
|
.recommenders/
|
||||||
|
|
||||||
|
# Annotation Processing
|
||||||
|
.apt_generated/
|
||||||
|
.apt_generated_test/
|
||||||
|
|
||||||
|
# Scala IDE specific (Scala & Java development for Eclipse)
|
||||||
|
.cache-main
|
||||||
|
.scala_dependencies
|
||||||
|
.worksheet
|
||||||
|
|
||||||
|
### VisualStudioCode template
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
*.code-workspace
|
||||||
|
|
10
go.mod
Normal file
10
go.mod
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
module github.com/cyrilix/robocar-base
|
||||||
|
|
||||||
|
go 1.13
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/Microsoft/hcsshim v0.8.6 // indirect
|
||||||
|
github.com/eclipse/paho.mqtt.golang v1.2.0
|
||||||
|
github.com/testcontainers/testcontainers-go v0.0.9
|
||||||
|
golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933 // indirect
|
||||||
|
)
|
118
go.sum
Normal file
118
go.sum
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
|
||||||
|
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||||
|
github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6nK2Q=
|
||||||
|
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||||
|
github.com/Microsoft/hcsshim v0.8.6 h1:ZfF0+zZeYdzMIVMZHKtDKJvLHj76XCuVae/jNkjj0IA=
|
||||||
|
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
|
||||||
|
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
|
||||||
|
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||||
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc h1:TP+534wVlf61smEIq1nwLLAjQVEK2EADoW3CX9AuT+8=
|
||||||
|
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible h1:dvc1KSkIYTVjZgHf/CTC2diTYC8PzhaA5sFISRfNVrE=
|
||||||
|
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||||
|
github.com/docker/docker v0.7.3-0.20190506211059-b20a14b54661 h1:ZuxGvIvF01nfc/G9RJ5Q7Va1zQE2WJyG18Zv3DqCEf4=
|
||||||
|
github.com/docker/docker v0.7.3-0.20190506211059-b20a14b54661/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||||
|
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||||
|
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||||
|
github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk=
|
||||||
|
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
|
github.com/eclipse/paho.mqtt.golang v1.2.0 h1:1F8mhG9+aO5/xpdtFkW4SxOJB67ukuDC3t2y2qayIX0=
|
||||||
|
github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
github.com/go-redis/redis v6.15.6+incompatible h1:H9evprGPLI8+ci7fxQx6WNZHJSb7be8FqJQRhdQZ5Sg=
|
||||||
|
github.com/go-redis/redis v6.15.6+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
||||||
|
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
|
||||||
|
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||||
|
github.com/gogo/protobuf v1.2.0 h1:xU6/SpYbvkNYiptHJYEDRseDLvYE7wSqhYYNy0QSUzI=
|
||||||
|
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
|
||||||
|
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||||
|
github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk=
|
||||||
|
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
|
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||||
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE=
|
||||||
|
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
|
||||||
|
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
|
||||||
|
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
|
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
|
||||||
|
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||||
|
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
|
||||||
|
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||||
|
github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y=
|
||||||
|
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||||
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||||
|
github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
|
||||||
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
|
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/testcontainers/testcontainers-go v0.0.9 h1:mwvFz+FkuQMqQ9oLkG4cVzPsZTRmrCo2NcaerJNaptA=
|
||||||
|
github.com/testcontainers/testcontainers-go v0.0.9/go.mod h1:0Qe9qqjNZgxHzzdHPWwmQ2D49FFO7920hLdJ4yUJXJI=
|
||||||
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933 h1:e6HwijUxhDe+hPNjZQQn9bA5PW3vNmnN64U2ZW759Lk=
|
||||||
|
golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180810170437-e96c4e24768d/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
|
||||||
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/grpc v1.17.0 h1:TRJYBgMclJvGYn2rIMjj+h9KtMt5r1Ij7ODVRIZkwhk=
|
||||||
|
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||||
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
|
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.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
||||||
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gotest.tools v0.0.0-20181223230014-1083505acf35 h1:zpdCK+REwbk+rqjJmHhiCN6iBIigrZ39glqSF0P3KF0=
|
||||||
|
gotest.tools v0.0.0-20181223230014-1083505acf35/go.mod h1:R//lfYlUuTOTfblYI3lGoAAAebUdzjvbmQsuB7Ykd90=
|
||||||
|
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
37
mode/mode.go
Normal file
37
mode/mode.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package mode
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DriveMode int
|
||||||
|
|
||||||
|
const (
|
||||||
|
DriveModeInvalid = -1
|
||||||
|
DriveModeUser = iota
|
||||||
|
DriveModePilot
|
||||||
|
)
|
||||||
|
|
||||||
|
func ToString(mode DriveMode) string {
|
||||||
|
switch mode {
|
||||||
|
case DriveModeUser:
|
||||||
|
return "user"
|
||||||
|
case DriveModePilot:
|
||||||
|
return "pilot"
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseString(val string) DriveMode {
|
||||||
|
switch val {
|
||||||
|
case "user":
|
||||||
|
return DriveModeUser
|
||||||
|
case "pilot":
|
||||||
|
return DriveModePilot
|
||||||
|
default:
|
||||||
|
log.Printf("invalid DriveMode: %v", val)
|
||||||
|
return DriveModeInvalid
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
40
mode/mode_test.go
Normal file
40
mode/mode_test.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package mode
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestToString(t *testing.T) {
|
||||||
|
cases := []struct{
|
||||||
|
value DriveMode
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{DriveModeUser, "user"},
|
||||||
|
{DriveModePilot, "pilot"},
|
||||||
|
{DriveModeInvalid, ""},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases{
|
||||||
|
val := ToString(c.value)
|
||||||
|
if val != c.expected{
|
||||||
|
t.Errorf("ToString(%v): %v, wants %v", c.value, val, c.expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseString(t *testing.T) {
|
||||||
|
cases := []struct{
|
||||||
|
value string
|
||||||
|
expected DriveMode
|
||||||
|
}{
|
||||||
|
{"user", DriveModeUser},
|
||||||
|
{"pilot",DriveModePilot},
|
||||||
|
{"", DriveModeInvalid},
|
||||||
|
{"invalid", DriveModeInvalid},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases{
|
||||||
|
val := ParseString(c.value)
|
||||||
|
if val != c.expected{
|
||||||
|
t.Errorf("ParseString(%v): %v, wants %v", c.value, val, c.expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
146
mqttdevice/mqttdevice.go
Normal file
146
mqttdevice/mqttdevice.go
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
package mqttdevice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/cyrilix/robocar-base/mode"
|
||||||
|
MQTT "github.com/eclipse/paho.mqtt.golang"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Publisher interface {
|
||||||
|
Publish(topic string, payload MqttValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Subscriber interface {
|
||||||
|
Subscribe(topic string, mh MQTT.MessageHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
type MQTTPubSub interface {
|
||||||
|
Publisher
|
||||||
|
Subscriber
|
||||||
|
io.Closer
|
||||||
|
}
|
||||||
|
|
||||||
|
type pahoMqttPubSub struct {
|
||||||
|
Uri string
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
ClientId string
|
||||||
|
Qos int
|
||||||
|
Retain bool
|
||||||
|
client MQTT.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPahoMqttPubSub(uri string, username string, password string, clientId string, qos int, retain bool) MQTTPubSub {
|
||||||
|
p := pahoMqttPubSub{Uri: uri, Username: username, Password: password, ClientId: clientId, Qos: qos, Retain: retain}
|
||||||
|
p.Connect()
|
||||||
|
return &p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Publish message to broker
|
||||||
|
func (p *pahoMqttPubSub) Publish(topic string, payload MqttValue) {
|
||||||
|
tokenResp := p.client.Publish(topic, byte(p.Qos), p.Retain, string(payload))
|
||||||
|
if tokenResp.Error() != nil {
|
||||||
|
log.Fatalf("%+v\n", tokenResp.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register func to execute on message
|
||||||
|
func (p *pahoMqttPubSub) Subscribe(topic string, callback MQTT.MessageHandler) {
|
||||||
|
tokenResp := p.client.Subscribe(topic, byte(p.Qos), callback)
|
||||||
|
if tokenResp.Error() != nil {
|
||||||
|
log.Fatalf("%+v\n", tokenResp.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close connection to broker
|
||||||
|
func (p *pahoMqttPubSub) Close() error {
|
||||||
|
p.client.Disconnect(500)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *pahoMqttPubSub) Connect() {
|
||||||
|
if p.client != nil && p.client.IsConnected() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//create a ClientOptions struct setting the broker address, clientid, turn
|
||||||
|
//off trace output and set the default message handler
|
||||||
|
opts := MQTT.NewClientOptions().AddBroker(p.Uri)
|
||||||
|
opts.SetUsername(p.Username)
|
||||||
|
opts.SetPassword(p.Password)
|
||||||
|
opts.SetClientID(p.ClientId)
|
||||||
|
opts.SetAutoReconnect(true)
|
||||||
|
opts.SetDefaultPublishHandler(
|
||||||
|
//define a function for the default message handler
|
||||||
|
func(client MQTT.Client, msg MQTT.Message) {
|
||||||
|
fmt.Printf("TOPIC: %s\n", msg.Topic())
|
||||||
|
fmt.Printf("MSG: %s\n", msg.Payload())
|
||||||
|
})
|
||||||
|
|
||||||
|
//create and start a client using the above ClientOptions
|
||||||
|
p.client = MQTT.NewClient(opts)
|
||||||
|
if token := p.client.Connect(); token.Wait() && token.Error() != nil {
|
||||||
|
panic(token.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type MqttValue []byte
|
||||||
|
|
||||||
|
func NewMqttValue(v interface{}) MqttValue {
|
||||||
|
switch val := v.(type) {
|
||||||
|
case string:
|
||||||
|
return MqttValue(val)
|
||||||
|
case float32, float64:
|
||||||
|
return MqttValue(fmt.Sprintf("%0.2f", val))
|
||||||
|
case int, int8, int16, int32, int64:
|
||||||
|
return MqttValue(fmt.Sprintf("%d", val))
|
||||||
|
case mode.DriveMode:
|
||||||
|
return MqttValue(mode.ToString(val))
|
||||||
|
case bool:
|
||||||
|
if val {
|
||||||
|
return []byte("ON")
|
||||||
|
} else {
|
||||||
|
return []byte("OFF")
|
||||||
|
}
|
||||||
|
case []byte:
|
||||||
|
return val
|
||||||
|
case MqttValue:
|
||||||
|
return val
|
||||||
|
default:
|
||||||
|
log.Printf("invalid mqtt value: %v", val)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MqttValue) IntValue() (int, error) {
|
||||||
|
return strconv.Atoi(string(*m))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MqttValue) Float32Value() (float32, error) {
|
||||||
|
val := string(*m)
|
||||||
|
r, err := strconv.ParseFloat(val, 32)
|
||||||
|
return float32(r), err
|
||||||
|
}
|
||||||
|
func (m *MqttValue) Float64Value() (float64, error) {
|
||||||
|
val := string(*m)
|
||||||
|
return strconv.ParseFloat(val, 64)
|
||||||
|
}
|
||||||
|
func (m *MqttValue) StringValue() (string, error) {
|
||||||
|
return string(*m), nil
|
||||||
|
}
|
||||||
|
func (m *MqttValue) ByteSliceValue() ([]byte, error) {
|
||||||
|
return *m, nil
|
||||||
|
}
|
||||||
|
func (m *MqttValue) BoolValue() (bool, error) {
|
||||||
|
val := string(*m)
|
||||||
|
switch val {
|
||||||
|
case "ON":
|
||||||
|
return true, nil
|
||||||
|
case "OFF":
|
||||||
|
return false, nil
|
||||||
|
default:
|
||||||
|
return false, fmt.Errorf("value %v can't be converted to bool", val)
|
||||||
|
}
|
||||||
|
}
|
189
mqttdevice/mqttdevice_test.go
Normal file
189
mqttdevice/mqttdevice_test.go
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
package mqttdevice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/cyrilix/robocar-base/testtools"
|
||||||
|
mqtt "github.com/eclipse/paho.mqtt.golang"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIntegration(t *testing.T) {
|
||||||
|
|
||||||
|
ctx, mqttC, mqttUri := testtools.MqttContainer(t)
|
||||||
|
defer mqttC.Terminate(ctx)
|
||||||
|
|
||||||
|
t.Run("ConnectAndClose", func(t *testing.T) {
|
||||||
|
t.Logf("Mqtt connection %s ready", mqttUri)
|
||||||
|
|
||||||
|
p := pahoMqttPubSub{Uri: mqttUri, ClientId: "TestMqtt", Username: "guest", Password: "guest"}
|
||||||
|
p.Connect()
|
||||||
|
p.Close()
|
||||||
|
})
|
||||||
|
t.Run("Publish", func(t *testing.T) {
|
||||||
|
options := mqtt.NewClientOptions().AddBroker(mqttUri)
|
||||||
|
options.SetUsername("guest")
|
||||||
|
options.SetPassword("guest")
|
||||||
|
|
||||||
|
client := mqtt.NewClient(options)
|
||||||
|
token := client.Connect()
|
||||||
|
defer client.Disconnect(100)
|
||||||
|
token.Wait()
|
||||||
|
if token.Error() != nil {
|
||||||
|
t.Fatalf("unable to connect to mqtt broker: %v\n", token.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
c := make(chan string)
|
||||||
|
defer close(c)
|
||||||
|
client.Subscribe("test/publish", 0, func(client mqtt.Client, message mqtt.Message) {
|
||||||
|
c <- string(message.Payload())
|
||||||
|
}).Wait()
|
||||||
|
|
||||||
|
p := pahoMqttPubSub{Uri: mqttUri, ClientId: "TestMqtt", Username: "guest", Password: "guest"}
|
||||||
|
p.Connect()
|
||||||
|
defer p.Close()
|
||||||
|
|
||||||
|
p.Publish("test/publish", []byte("Test1234"))
|
||||||
|
result := <-c
|
||||||
|
if result != "Test1234" {
|
||||||
|
t.Fatalf("bad message: %v\n", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewMqttValue(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
value interface{}
|
||||||
|
expected MqttValue
|
||||||
|
}{
|
||||||
|
{"text", []byte("text")},
|
||||||
|
{float32(2.0123), []byte("2.01")},
|
||||||
|
{3.12345, []byte("3.12")},
|
||||||
|
{12, []byte("12")},
|
||||||
|
{true, []byte("ON")},
|
||||||
|
{false, []byte("OFF")},
|
||||||
|
{MqttValue("13"), []byte("13")},
|
||||||
|
{[]byte("test bytes"), []byte("test bytes")},
|
||||||
|
|
||||||
|
{struct {
|
||||||
|
content string
|
||||||
|
}{"invalid"}, nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
val := NewMqttValue(c.value)
|
||||||
|
if string(val) != string(c.expected) {
|
||||||
|
t.Errorf("NewMqttValue(%v): %v, wants %v", c.value, val, c.expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMqttValue_BoolValue(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
value MqttValue
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{NewMqttValue("ON"), true},
|
||||||
|
{NewMqttValue("OFF"), false},
|
||||||
|
}
|
||||||
|
for _, c := range cases {
|
||||||
|
val, err := c.value.BoolValue()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected conversion error: %v", err)
|
||||||
|
}
|
||||||
|
if c.expected != val {
|
||||||
|
t.Errorf("MqttValue.BoolValue(): %v, wants %v", val, c.expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMqttValue_ByteSliceValue(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
value MqttValue
|
||||||
|
expected []byte
|
||||||
|
}{
|
||||||
|
{NewMqttValue([]byte("content")), []byte("content")},
|
||||||
|
}
|
||||||
|
for _, c := range cases{
|
||||||
|
val, err := c.value.ByteSliceValue()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected conversion error: %v", err)
|
||||||
|
}
|
||||||
|
if string(c.expected) != string(val){
|
||||||
|
t.Errorf("MqttValue.BoolValue(): %v, wants %v", val, c.expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMqttValue_Float32Value(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
value MqttValue
|
||||||
|
expected float32
|
||||||
|
}{
|
||||||
|
{NewMqttValue("32.0123"), float32(32.0123)},
|
||||||
|
{NewMqttValue("33"), float32(33.)},
|
||||||
|
}
|
||||||
|
for _, c := range cases{
|
||||||
|
val, err := c.value.Float32Value()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected conversion error: %v", err)
|
||||||
|
}
|
||||||
|
if c.expected != val{
|
||||||
|
t.Errorf("MqttValue.BoolValue(): %v, wants %v", val, c.expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMqttValue_Float64Value(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
value MqttValue
|
||||||
|
expected float64
|
||||||
|
}{
|
||||||
|
{NewMqttValue("32.0123"), 32.0123},
|
||||||
|
{NewMqttValue("33"), 33.},
|
||||||
|
}
|
||||||
|
for _, c := range cases{
|
||||||
|
val, err := c.value.Float64Value()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected conversion error: %v", err)
|
||||||
|
}
|
||||||
|
if c.expected != val{
|
||||||
|
t.Errorf("MqttValue.BoolValue(): %v, wants %v", val, c.expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func TestMqttValue_IntValue(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
value MqttValue
|
||||||
|
expected int
|
||||||
|
}{
|
||||||
|
{NewMqttValue("1"), 1},
|
||||||
|
{NewMqttValue("-10"), -10},
|
||||||
|
}
|
||||||
|
for _, c := range cases{
|
||||||
|
val, err := c.value.IntValue()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected conversion error: %v", err)
|
||||||
|
}
|
||||||
|
if c.expected != val{
|
||||||
|
t.Errorf("MqttValue.BoolValue(): %v, wants %v", val, c.expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func TestMqttValue_StringValue(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
value MqttValue
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{NewMqttValue("ON"), "ON"},
|
||||||
|
{NewMqttValue("OFF"), "OFF"},
|
||||||
|
}
|
||||||
|
for _, c := range cases{
|
||||||
|
val, err := c.value.StringValue()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected conversion error: %v", err)
|
||||||
|
}
|
||||||
|
if c.expected != val{
|
||||||
|
t.Errorf("MqttValue.BoolValue(): %v, wants %v", val, c.expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
37
testtools/testtools.go
Normal file
37
testtools/testtools.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package testtools
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/testcontainers/testcontainers-go"
|
||||||
|
"github.com/testcontainers/testcontainers-go/wait"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MqttContainer(t *testing.T) (context.Context, testcontainers.Container, string) {
|
||||||
|
ctx := context.Background()
|
||||||
|
req := testcontainers.ContainerRequest{
|
||||||
|
Image: "eclipse-mosquitto",
|
||||||
|
ExposedPorts: []string{"1883/tcp"},
|
||||||
|
WaitingFor: wait.ForLog("listen socket on port 1883."),
|
||||||
|
}
|
||||||
|
mqttC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
|
||||||
|
ContainerRequest: req,
|
||||||
|
Started: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ip, err := mqttC.Host(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
port, err := mqttC.MappedPort(ctx, "1883/tcp")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mqttUri := fmt.Sprintf("tcp://%s:%d", ip, port.Int())
|
||||||
|
return ctx, mqttC, mqttUri
|
||||||
|
}
|
1
vendor/github.com/Microsoft/go-winio/.gitignore
generated
vendored
Normal file
1
vendor/github.com/Microsoft/go-winio/.gitignore
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.exe
|
22
vendor/github.com/Microsoft/go-winio/LICENSE
generated
vendored
Normal file
22
vendor/github.com/Microsoft/go-winio/LICENSE
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015 Microsoft
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
22
vendor/github.com/Microsoft/go-winio/README.md
generated
vendored
Normal file
22
vendor/github.com/Microsoft/go-winio/README.md
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# go-winio
|
||||||
|
|
||||||
|
This repository contains utilities for efficiently performing Win32 IO operations in
|
||||||
|
Go. Currently, this is focused on accessing named pipes and other file handles, and
|
||||||
|
for using named pipes as a net transport.
|
||||||
|
|
||||||
|
This code relies on IO completion ports to avoid blocking IO on system threads, allowing Go
|
||||||
|
to reuse the thread to schedule another goroutine. This limits support to Windows Vista and
|
||||||
|
newer operating systems. This is similar to the implementation of network sockets in Go's net
|
||||||
|
package.
|
||||||
|
|
||||||
|
Please see the LICENSE file for licensing information.
|
||||||
|
|
||||||
|
This project has adopted the [Microsoft Open Source Code of
|
||||||
|
Conduct](https://opensource.microsoft.com/codeofconduct/). For more information
|
||||||
|
see the [Code of Conduct
|
||||||
|
FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact
|
||||||
|
[opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional
|
||||||
|
questions or comments.
|
||||||
|
|
||||||
|
Thanks to natefinch for the inspiration for this library. See https://github.com/natefinch/npipe
|
||||||
|
for another named pipe implementation.
|
280
vendor/github.com/Microsoft/go-winio/backup.go
generated
vendored
Normal file
280
vendor/github.com/Microsoft/go-winio/backup.go
generated
vendored
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"syscall"
|
||||||
|
"unicode/utf16"
|
||||||
|
)
|
||||||
|
|
||||||
|
//sys backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupRead
|
||||||
|
//sys backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupWrite
|
||||||
|
|
||||||
|
const (
|
||||||
|
BackupData = uint32(iota + 1)
|
||||||
|
BackupEaData
|
||||||
|
BackupSecurity
|
||||||
|
BackupAlternateData
|
||||||
|
BackupLink
|
||||||
|
BackupPropertyData
|
||||||
|
BackupObjectId
|
||||||
|
BackupReparseData
|
||||||
|
BackupSparseBlock
|
||||||
|
BackupTxfsData
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
StreamSparseAttributes = uint32(8)
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
WRITE_DAC = 0x40000
|
||||||
|
WRITE_OWNER = 0x80000
|
||||||
|
ACCESS_SYSTEM_SECURITY = 0x1000000
|
||||||
|
)
|
||||||
|
|
||||||
|
// BackupHeader represents a backup stream of a file.
|
||||||
|
type BackupHeader struct {
|
||||||
|
Id uint32 // The backup stream ID
|
||||||
|
Attributes uint32 // Stream attributes
|
||||||
|
Size int64 // The size of the stream in bytes
|
||||||
|
Name string // The name of the stream (for BackupAlternateData only).
|
||||||
|
Offset int64 // The offset of the stream in the file (for BackupSparseBlock only).
|
||||||
|
}
|
||||||
|
|
||||||
|
type win32StreamId struct {
|
||||||
|
StreamId uint32
|
||||||
|
Attributes uint32
|
||||||
|
Size uint64
|
||||||
|
NameSize uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// BackupStreamReader reads from a stream produced by the BackupRead Win32 API and produces a series
|
||||||
|
// of BackupHeader values.
|
||||||
|
type BackupStreamReader struct {
|
||||||
|
r io.Reader
|
||||||
|
bytesLeft int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBackupStreamReader produces a BackupStreamReader from any io.Reader.
|
||||||
|
func NewBackupStreamReader(r io.Reader) *BackupStreamReader {
|
||||||
|
return &BackupStreamReader{r, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next returns the next backup stream and prepares for calls to Read(). It skips the remainder of the current stream if
|
||||||
|
// it was not completely read.
|
||||||
|
func (r *BackupStreamReader) Next() (*BackupHeader, error) {
|
||||||
|
if r.bytesLeft > 0 {
|
||||||
|
if s, ok := r.r.(io.Seeker); ok {
|
||||||
|
// Make sure Seek on io.SeekCurrent sometimes succeeds
|
||||||
|
// before trying the actual seek.
|
||||||
|
if _, err := s.Seek(0, io.SeekCurrent); err == nil {
|
||||||
|
if _, err = s.Seek(r.bytesLeft, io.SeekCurrent); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r.bytesLeft = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, err := io.Copy(ioutil.Discard, r); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var wsi win32StreamId
|
||||||
|
if err := binary.Read(r.r, binary.LittleEndian, &wsi); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
hdr := &BackupHeader{
|
||||||
|
Id: wsi.StreamId,
|
||||||
|
Attributes: wsi.Attributes,
|
||||||
|
Size: int64(wsi.Size),
|
||||||
|
}
|
||||||
|
if wsi.NameSize != 0 {
|
||||||
|
name := make([]uint16, int(wsi.NameSize/2))
|
||||||
|
if err := binary.Read(r.r, binary.LittleEndian, name); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
hdr.Name = syscall.UTF16ToString(name)
|
||||||
|
}
|
||||||
|
if wsi.StreamId == BackupSparseBlock {
|
||||||
|
if err := binary.Read(r.r, binary.LittleEndian, &hdr.Offset); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
hdr.Size -= 8
|
||||||
|
}
|
||||||
|
r.bytesLeft = hdr.Size
|
||||||
|
return hdr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads from the current backup stream.
|
||||||
|
func (r *BackupStreamReader) Read(b []byte) (int, error) {
|
||||||
|
if r.bytesLeft == 0 {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
if int64(len(b)) > r.bytesLeft {
|
||||||
|
b = b[:r.bytesLeft]
|
||||||
|
}
|
||||||
|
n, err := r.r.Read(b)
|
||||||
|
r.bytesLeft -= int64(n)
|
||||||
|
if err == io.EOF {
|
||||||
|
err = io.ErrUnexpectedEOF
|
||||||
|
} else if r.bytesLeft == 0 && err == nil {
|
||||||
|
err = io.EOF
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// BackupStreamWriter writes a stream compatible with the BackupWrite Win32 API.
|
||||||
|
type BackupStreamWriter struct {
|
||||||
|
w io.Writer
|
||||||
|
bytesLeft int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBackupStreamWriter produces a BackupStreamWriter on top of an io.Writer.
|
||||||
|
func NewBackupStreamWriter(w io.Writer) *BackupStreamWriter {
|
||||||
|
return &BackupStreamWriter{w, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteHeader writes the next backup stream header and prepares for calls to Write().
|
||||||
|
func (w *BackupStreamWriter) WriteHeader(hdr *BackupHeader) error {
|
||||||
|
if w.bytesLeft != 0 {
|
||||||
|
return fmt.Errorf("missing %d bytes", w.bytesLeft)
|
||||||
|
}
|
||||||
|
name := utf16.Encode([]rune(hdr.Name))
|
||||||
|
wsi := win32StreamId{
|
||||||
|
StreamId: hdr.Id,
|
||||||
|
Attributes: hdr.Attributes,
|
||||||
|
Size: uint64(hdr.Size),
|
||||||
|
NameSize: uint32(len(name) * 2),
|
||||||
|
}
|
||||||
|
if hdr.Id == BackupSparseBlock {
|
||||||
|
// Include space for the int64 block offset
|
||||||
|
wsi.Size += 8
|
||||||
|
}
|
||||||
|
if err := binary.Write(w.w, binary.LittleEndian, &wsi); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(name) != 0 {
|
||||||
|
if err := binary.Write(w.w, binary.LittleEndian, name); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hdr.Id == BackupSparseBlock {
|
||||||
|
if err := binary.Write(w.w, binary.LittleEndian, hdr.Offset); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.bytesLeft = hdr.Size
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes to the current backup stream.
|
||||||
|
func (w *BackupStreamWriter) Write(b []byte) (int, error) {
|
||||||
|
if w.bytesLeft < int64(len(b)) {
|
||||||
|
return 0, fmt.Errorf("too many bytes by %d", int64(len(b))-w.bytesLeft)
|
||||||
|
}
|
||||||
|
n, err := w.w.Write(b)
|
||||||
|
w.bytesLeft -= int64(n)
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// BackupFileReader provides an io.ReadCloser interface on top of the BackupRead Win32 API.
|
||||||
|
type BackupFileReader struct {
|
||||||
|
f *os.File
|
||||||
|
includeSecurity bool
|
||||||
|
ctx uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBackupFileReader returns a new BackupFileReader from a file handle. If includeSecurity is true,
|
||||||
|
// Read will attempt to read the security descriptor of the file.
|
||||||
|
func NewBackupFileReader(f *os.File, includeSecurity bool) *BackupFileReader {
|
||||||
|
r := &BackupFileReader{f, includeSecurity, 0}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads a backup stream from the file by calling the Win32 API BackupRead().
|
||||||
|
func (r *BackupFileReader) Read(b []byte) (int, error) {
|
||||||
|
var bytesRead uint32
|
||||||
|
err := backupRead(syscall.Handle(r.f.Fd()), b, &bytesRead, false, r.includeSecurity, &r.ctx)
|
||||||
|
if err != nil {
|
||||||
|
return 0, &os.PathError{"BackupRead", r.f.Name(), err}
|
||||||
|
}
|
||||||
|
runtime.KeepAlive(r.f)
|
||||||
|
if bytesRead == 0 {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
return int(bytesRead), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close frees Win32 resources associated with the BackupFileReader. It does not close
|
||||||
|
// the underlying file.
|
||||||
|
func (r *BackupFileReader) Close() error {
|
||||||
|
if r.ctx != 0 {
|
||||||
|
backupRead(syscall.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx)
|
||||||
|
runtime.KeepAlive(r.f)
|
||||||
|
r.ctx = 0
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BackupFileWriter provides an io.WriteCloser interface on top of the BackupWrite Win32 API.
|
||||||
|
type BackupFileWriter struct {
|
||||||
|
f *os.File
|
||||||
|
includeSecurity bool
|
||||||
|
ctx uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBackupFileWriter returns a new BackupFileWriter from a file handle. If includeSecurity is true,
|
||||||
|
// Write() will attempt to restore the security descriptor from the stream.
|
||||||
|
func NewBackupFileWriter(f *os.File, includeSecurity bool) *BackupFileWriter {
|
||||||
|
w := &BackupFileWriter{f, includeSecurity, 0}
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write restores a portion of the file using the provided backup stream.
|
||||||
|
func (w *BackupFileWriter) Write(b []byte) (int, error) {
|
||||||
|
var bytesWritten uint32
|
||||||
|
err := backupWrite(syscall.Handle(w.f.Fd()), b, &bytesWritten, false, w.includeSecurity, &w.ctx)
|
||||||
|
if err != nil {
|
||||||
|
return 0, &os.PathError{"BackupWrite", w.f.Name(), err}
|
||||||
|
}
|
||||||
|
runtime.KeepAlive(w.f)
|
||||||
|
if int(bytesWritten) != len(b) {
|
||||||
|
return int(bytesWritten), errors.New("not all bytes could be written")
|
||||||
|
}
|
||||||
|
return len(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close frees Win32 resources associated with the BackupFileWriter. It does not
|
||||||
|
// close the underlying file.
|
||||||
|
func (w *BackupFileWriter) Close() error {
|
||||||
|
if w.ctx != 0 {
|
||||||
|
backupWrite(syscall.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx)
|
||||||
|
runtime.KeepAlive(w.f)
|
||||||
|
w.ctx = 0
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenForBackup opens a file or directory, potentially skipping access checks if the backup
|
||||||
|
// or restore privileges have been acquired.
|
||||||
|
//
|
||||||
|
// If the file opened was a directory, it cannot be used with Readdir().
|
||||||
|
func OpenForBackup(path string, access uint32, share uint32, createmode uint32) (*os.File, error) {
|
||||||
|
winPath, err := syscall.UTF16FromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
h, err := syscall.CreateFile(&winPath[0], access, share, nil, createmode, syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OPEN_REPARSE_POINT, 0)
|
||||||
|
if err != nil {
|
||||||
|
err = &os.PathError{Op: "open", Path: path, Err: err}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return os.NewFile(uintptr(h), path), nil
|
||||||
|
}
|
137
vendor/github.com/Microsoft/go-winio/ea.go
generated
vendored
Normal file
137
vendor/github.com/Microsoft/go-winio/ea.go
generated
vendored
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fileFullEaInformation struct {
|
||||||
|
NextEntryOffset uint32
|
||||||
|
Flags uint8
|
||||||
|
NameLength uint8
|
||||||
|
ValueLength uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
fileFullEaInformationSize = binary.Size(&fileFullEaInformation{})
|
||||||
|
|
||||||
|
errInvalidEaBuffer = errors.New("invalid extended attribute buffer")
|
||||||
|
errEaNameTooLarge = errors.New("extended attribute name too large")
|
||||||
|
errEaValueTooLarge = errors.New("extended attribute value too large")
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExtendedAttribute represents a single Windows EA.
|
||||||
|
type ExtendedAttribute struct {
|
||||||
|
Name string
|
||||||
|
Value []byte
|
||||||
|
Flags uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) {
|
||||||
|
var info fileFullEaInformation
|
||||||
|
err = binary.Read(bytes.NewReader(b), binary.LittleEndian, &info)
|
||||||
|
if err != nil {
|
||||||
|
err = errInvalidEaBuffer
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
nameOffset := fileFullEaInformationSize
|
||||||
|
nameLen := int(info.NameLength)
|
||||||
|
valueOffset := nameOffset + int(info.NameLength) + 1
|
||||||
|
valueLen := int(info.ValueLength)
|
||||||
|
nextOffset := int(info.NextEntryOffset)
|
||||||
|
if valueLen+valueOffset > len(b) || nextOffset < 0 || nextOffset > len(b) {
|
||||||
|
err = errInvalidEaBuffer
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ea.Name = string(b[nameOffset : nameOffset+nameLen])
|
||||||
|
ea.Value = b[valueOffset : valueOffset+valueLen]
|
||||||
|
ea.Flags = info.Flags
|
||||||
|
if info.NextEntryOffset != 0 {
|
||||||
|
nb = b[info.NextEntryOffset:]
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeExtendedAttributes decodes a list of EAs from a FILE_FULL_EA_INFORMATION
|
||||||
|
// buffer retrieved from BackupRead, ZwQueryEaFile, etc.
|
||||||
|
func DecodeExtendedAttributes(b []byte) (eas []ExtendedAttribute, err error) {
|
||||||
|
for len(b) != 0 {
|
||||||
|
ea, nb, err := parseEa(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
eas = append(eas, ea)
|
||||||
|
b = nb
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeEa(buf *bytes.Buffer, ea *ExtendedAttribute, last bool) error {
|
||||||
|
if int(uint8(len(ea.Name))) != len(ea.Name) {
|
||||||
|
return errEaNameTooLarge
|
||||||
|
}
|
||||||
|
if int(uint16(len(ea.Value))) != len(ea.Value) {
|
||||||
|
return errEaValueTooLarge
|
||||||
|
}
|
||||||
|
entrySize := uint32(fileFullEaInformationSize + len(ea.Name) + 1 + len(ea.Value))
|
||||||
|
withPadding := (entrySize + 3) &^ 3
|
||||||
|
nextOffset := uint32(0)
|
||||||
|
if !last {
|
||||||
|
nextOffset = withPadding
|
||||||
|
}
|
||||||
|
info := fileFullEaInformation{
|
||||||
|
NextEntryOffset: nextOffset,
|
||||||
|
Flags: ea.Flags,
|
||||||
|
NameLength: uint8(len(ea.Name)),
|
||||||
|
ValueLength: uint16(len(ea.Value)),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := binary.Write(buf, binary.LittleEndian, &info)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = buf.Write([]byte(ea.Name))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = buf.WriteByte(0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = buf.Write(ea.Value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = buf.Write([]byte{0, 0, 0}[0 : withPadding-entrySize])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeExtendedAttributes encodes a list of EAs into a FILE_FULL_EA_INFORMATION
|
||||||
|
// buffer for use with BackupWrite, ZwSetEaFile, etc.
|
||||||
|
func EncodeExtendedAttributes(eas []ExtendedAttribute) ([]byte, error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for i := range eas {
|
||||||
|
last := false
|
||||||
|
if i == len(eas)-1 {
|
||||||
|
last = true
|
||||||
|
}
|
||||||
|
|
||||||
|
err := writeEa(&buf, &eas[i], last)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
307
vendor/github.com/Microsoft/go-winio/file.go
generated
vendored
Normal file
307
vendor/github.com/Microsoft/go-winio/file.go
generated
vendored
Normal file
@ -0,0 +1,307 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
//sys cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) = CancelIoEx
|
||||||
|
//sys createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) = CreateIoCompletionPort
|
||||||
|
//sys getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) = GetQueuedCompletionStatus
|
||||||
|
//sys setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes
|
||||||
|
|
||||||
|
type atomicBool int32
|
||||||
|
|
||||||
|
func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 }
|
||||||
|
func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) }
|
||||||
|
func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) }
|
||||||
|
func (b *atomicBool) swap(new bool) bool {
|
||||||
|
var newInt int32
|
||||||
|
if new {
|
||||||
|
newInt = 1
|
||||||
|
}
|
||||||
|
return atomic.SwapInt32((*int32)(b), newInt) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 1
|
||||||
|
cFILE_SKIP_SET_EVENT_ON_HANDLE = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrFileClosed = errors.New("file has already been closed")
|
||||||
|
ErrTimeout = &timeoutError{}
|
||||||
|
)
|
||||||
|
|
||||||
|
type timeoutError struct{}
|
||||||
|
|
||||||
|
func (e *timeoutError) Error() string { return "i/o timeout" }
|
||||||
|
func (e *timeoutError) Timeout() bool { return true }
|
||||||
|
func (e *timeoutError) Temporary() bool { return true }
|
||||||
|
|
||||||
|
type timeoutChan chan struct{}
|
||||||
|
|
||||||
|
var ioInitOnce sync.Once
|
||||||
|
var ioCompletionPort syscall.Handle
|
||||||
|
|
||||||
|
// ioResult contains the result of an asynchronous IO operation
|
||||||
|
type ioResult struct {
|
||||||
|
bytes uint32
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ioOperation represents an outstanding asynchronous Win32 IO
|
||||||
|
type ioOperation struct {
|
||||||
|
o syscall.Overlapped
|
||||||
|
ch chan ioResult
|
||||||
|
}
|
||||||
|
|
||||||
|
func initIo() {
|
||||||
|
h, err := createIoCompletionPort(syscall.InvalidHandle, 0, 0, 0xffffffff)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
ioCompletionPort = h
|
||||||
|
go ioCompletionProcessor(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// win32File implements Reader, Writer, and Closer on a Win32 handle without blocking in a syscall.
|
||||||
|
// It takes ownership of this handle and will close it if it is garbage collected.
|
||||||
|
type win32File struct {
|
||||||
|
handle syscall.Handle
|
||||||
|
wg sync.WaitGroup
|
||||||
|
wgLock sync.RWMutex
|
||||||
|
closing atomicBool
|
||||||
|
readDeadline deadlineHandler
|
||||||
|
writeDeadline deadlineHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
type deadlineHandler struct {
|
||||||
|
setLock sync.Mutex
|
||||||
|
channel timeoutChan
|
||||||
|
channelLock sync.RWMutex
|
||||||
|
timer *time.Timer
|
||||||
|
timedout atomicBool
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeWin32File makes a new win32File from an existing file handle
|
||||||
|
func makeWin32File(h syscall.Handle) (*win32File, error) {
|
||||||
|
f := &win32File{handle: h}
|
||||||
|
ioInitOnce.Do(initIo)
|
||||||
|
_, err := createIoCompletionPort(h, ioCompletionPort, 0, 0xffffffff)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = setFileCompletionNotificationModes(h, cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS|cFILE_SKIP_SET_EVENT_ON_HANDLE)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
f.readDeadline.channel = make(timeoutChan)
|
||||||
|
f.writeDeadline.channel = make(timeoutChan)
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
|
||||||
|
return makeWin32File(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// closeHandle closes the resources associated with a Win32 handle
|
||||||
|
func (f *win32File) closeHandle() {
|
||||||
|
f.wgLock.Lock()
|
||||||
|
// Atomically set that we are closing, releasing the resources only once.
|
||||||
|
if !f.closing.swap(true) {
|
||||||
|
f.wgLock.Unlock()
|
||||||
|
// cancel all IO and wait for it to complete
|
||||||
|
cancelIoEx(f.handle, nil)
|
||||||
|
f.wg.Wait()
|
||||||
|
// at this point, no new IO can start
|
||||||
|
syscall.Close(f.handle)
|
||||||
|
f.handle = 0
|
||||||
|
} else {
|
||||||
|
f.wgLock.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes a win32File.
|
||||||
|
func (f *win32File) Close() error {
|
||||||
|
f.closeHandle()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepareIo prepares for a new IO operation.
|
||||||
|
// The caller must call f.wg.Done() when the IO is finished, prior to Close() returning.
|
||||||
|
func (f *win32File) prepareIo() (*ioOperation, error) {
|
||||||
|
f.wgLock.RLock()
|
||||||
|
if f.closing.isSet() {
|
||||||
|
f.wgLock.RUnlock()
|
||||||
|
return nil, ErrFileClosed
|
||||||
|
}
|
||||||
|
f.wg.Add(1)
|
||||||
|
f.wgLock.RUnlock()
|
||||||
|
c := &ioOperation{}
|
||||||
|
c.ch = make(chan ioResult)
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ioCompletionProcessor processes completed async IOs forever
|
||||||
|
func ioCompletionProcessor(h syscall.Handle) {
|
||||||
|
for {
|
||||||
|
var bytes uint32
|
||||||
|
var key uintptr
|
||||||
|
var op *ioOperation
|
||||||
|
err := getQueuedCompletionStatus(h, &bytes, &key, &op, syscall.INFINITE)
|
||||||
|
if op == nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
op.ch <- ioResult{bytes, err}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// asyncIo processes the return value from ReadFile or WriteFile, blocking until
|
||||||
|
// the operation has actually completed.
|
||||||
|
func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) {
|
||||||
|
if err != syscall.ERROR_IO_PENDING {
|
||||||
|
return int(bytes), err
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.closing.isSet() {
|
||||||
|
cancelIoEx(f.handle, &c.o)
|
||||||
|
}
|
||||||
|
|
||||||
|
var timeout timeoutChan
|
||||||
|
if d != nil {
|
||||||
|
d.channelLock.Lock()
|
||||||
|
timeout = d.channel
|
||||||
|
d.channelLock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
var r ioResult
|
||||||
|
select {
|
||||||
|
case r = <-c.ch:
|
||||||
|
err = r.err
|
||||||
|
if err == syscall.ERROR_OPERATION_ABORTED {
|
||||||
|
if f.closing.isSet() {
|
||||||
|
err = ErrFileClosed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case <-timeout:
|
||||||
|
cancelIoEx(f.handle, &c.o)
|
||||||
|
r = <-c.ch
|
||||||
|
err = r.err
|
||||||
|
if err == syscall.ERROR_OPERATION_ABORTED {
|
||||||
|
err = ErrTimeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// runtime.KeepAlive is needed, as c is passed via native
|
||||||
|
// code to ioCompletionProcessor, c must remain alive
|
||||||
|
// until the channel read is complete.
|
||||||
|
runtime.KeepAlive(c)
|
||||||
|
return int(r.bytes), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads from a file handle.
|
||||||
|
func (f *win32File) Read(b []byte) (int, error) {
|
||||||
|
c, err := f.prepareIo()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer f.wg.Done()
|
||||||
|
|
||||||
|
if f.readDeadline.timedout.isSet() {
|
||||||
|
return 0, ErrTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
var bytes uint32
|
||||||
|
err = syscall.ReadFile(f.handle, b, &bytes, &c.o)
|
||||||
|
n, err := f.asyncIo(c, &f.readDeadline, bytes, err)
|
||||||
|
runtime.KeepAlive(b)
|
||||||
|
|
||||||
|
// Handle EOF conditions.
|
||||||
|
if err == nil && n == 0 && len(b) != 0 {
|
||||||
|
return 0, io.EOF
|
||||||
|
} else if err == syscall.ERROR_BROKEN_PIPE {
|
||||||
|
return 0, io.EOF
|
||||||
|
} else {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes to a file handle.
|
||||||
|
func (f *win32File) Write(b []byte) (int, error) {
|
||||||
|
c, err := f.prepareIo()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer f.wg.Done()
|
||||||
|
|
||||||
|
if f.writeDeadline.timedout.isSet() {
|
||||||
|
return 0, ErrTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
var bytes uint32
|
||||||
|
err = syscall.WriteFile(f.handle, b, &bytes, &c.o)
|
||||||
|
n, err := f.asyncIo(c, &f.writeDeadline, bytes, err)
|
||||||
|
runtime.KeepAlive(b)
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *win32File) SetReadDeadline(deadline time.Time) error {
|
||||||
|
return f.readDeadline.set(deadline)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *win32File) SetWriteDeadline(deadline time.Time) error {
|
||||||
|
return f.writeDeadline.set(deadline)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *win32File) Flush() error {
|
||||||
|
return syscall.FlushFileBuffers(f.handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *deadlineHandler) set(deadline time.Time) error {
|
||||||
|
d.setLock.Lock()
|
||||||
|
defer d.setLock.Unlock()
|
||||||
|
|
||||||
|
if d.timer != nil {
|
||||||
|
if !d.timer.Stop() {
|
||||||
|
<-d.channel
|
||||||
|
}
|
||||||
|
d.timer = nil
|
||||||
|
}
|
||||||
|
d.timedout.setFalse()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-d.channel:
|
||||||
|
d.channelLock.Lock()
|
||||||
|
d.channel = make(chan struct{})
|
||||||
|
d.channelLock.Unlock()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
if deadline.IsZero() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
timeoutIO := func() {
|
||||||
|
d.timedout.setTrue()
|
||||||
|
close(d.channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
duration := deadline.Sub(now)
|
||||||
|
if deadline.After(now) {
|
||||||
|
// Deadline is in the future, set a timer to wait
|
||||||
|
d.timer = time.AfterFunc(duration, timeoutIO)
|
||||||
|
} else {
|
||||||
|
// Deadline is in the past. Cancel all pending IO now.
|
||||||
|
timeoutIO()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
61
vendor/github.com/Microsoft/go-winio/fileinfo.go
generated
vendored
Normal file
61
vendor/github.com/Microsoft/go-winio/fileinfo.go
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
//sys getFileInformationByHandleEx(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) = GetFileInformationByHandleEx
|
||||||
|
//sys setFileInformationByHandle(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) = SetFileInformationByHandle
|
||||||
|
|
||||||
|
const (
|
||||||
|
fileBasicInfo = 0
|
||||||
|
fileIDInfo = 0x12
|
||||||
|
)
|
||||||
|
|
||||||
|
// FileBasicInfo contains file access time and file attributes information.
|
||||||
|
type FileBasicInfo struct {
|
||||||
|
CreationTime, LastAccessTime, LastWriteTime, ChangeTime syscall.Filetime
|
||||||
|
FileAttributes uint32
|
||||||
|
pad uint32 // padding
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFileBasicInfo retrieves times and attributes for a file.
|
||||||
|
func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) {
|
||||||
|
bi := &FileBasicInfo{}
|
||||||
|
if err := getFileInformationByHandleEx(syscall.Handle(f.Fd()), fileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil {
|
||||||
|
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
|
||||||
|
}
|
||||||
|
runtime.KeepAlive(f)
|
||||||
|
return bi, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetFileBasicInfo sets times and attributes for a file.
|
||||||
|
func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error {
|
||||||
|
if err := setFileInformationByHandle(syscall.Handle(f.Fd()), fileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil {
|
||||||
|
return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err}
|
||||||
|
}
|
||||||
|
runtime.KeepAlive(f)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileIDInfo contains the volume serial number and file ID for a file. This pair should be
|
||||||
|
// unique on a system.
|
||||||
|
type FileIDInfo struct {
|
||||||
|
VolumeSerialNumber uint64
|
||||||
|
FileID [16]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFileID retrieves the unique (volume, file ID) pair for a file.
|
||||||
|
func GetFileID(f *os.File) (*FileIDInfo, error) {
|
||||||
|
fileID := &FileIDInfo{}
|
||||||
|
if err := getFileInformationByHandleEx(syscall.Handle(f.Fd()), fileIDInfo, (*byte)(unsafe.Pointer(fileID)), uint32(unsafe.Sizeof(*fileID))); err != nil {
|
||||||
|
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
|
||||||
|
}
|
||||||
|
runtime.KeepAlive(f)
|
||||||
|
return fileID, nil
|
||||||
|
}
|
421
vendor/github.com/Microsoft/go-winio/pipe.go
generated
vendored
Normal file
421
vendor/github.com/Microsoft/go-winio/pipe.go
generated
vendored
Normal file
@ -0,0 +1,421 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
//sys connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) = ConnectNamedPipe
|
||||||
|
//sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateNamedPipeW
|
||||||
|
//sys createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateFileW
|
||||||
|
//sys getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo
|
||||||
|
//sys getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW
|
||||||
|
//sys localAlloc(uFlags uint32, length uint32) (ptr uintptr) = LocalAlloc
|
||||||
|
|
||||||
|
const (
|
||||||
|
cERROR_PIPE_BUSY = syscall.Errno(231)
|
||||||
|
cERROR_NO_DATA = syscall.Errno(232)
|
||||||
|
cERROR_PIPE_CONNECTED = syscall.Errno(535)
|
||||||
|
cERROR_SEM_TIMEOUT = syscall.Errno(121)
|
||||||
|
|
||||||
|
cPIPE_ACCESS_DUPLEX = 0x3
|
||||||
|
cFILE_FLAG_FIRST_PIPE_INSTANCE = 0x80000
|
||||||
|
cSECURITY_SQOS_PRESENT = 0x100000
|
||||||
|
cSECURITY_ANONYMOUS = 0
|
||||||
|
|
||||||
|
cPIPE_REJECT_REMOTE_CLIENTS = 0x8
|
||||||
|
|
||||||
|
cPIPE_UNLIMITED_INSTANCES = 255
|
||||||
|
|
||||||
|
cNMPWAIT_USE_DEFAULT_WAIT = 0
|
||||||
|
cNMPWAIT_NOWAIT = 1
|
||||||
|
|
||||||
|
cPIPE_TYPE_MESSAGE = 4
|
||||||
|
|
||||||
|
cPIPE_READMODE_MESSAGE = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrPipeListenerClosed is returned for pipe operations on listeners that have been closed.
|
||||||
|
// This error should match net.errClosing since docker takes a dependency on its text.
|
||||||
|
ErrPipeListenerClosed = errors.New("use of closed network connection")
|
||||||
|
|
||||||
|
errPipeWriteClosed = errors.New("pipe has been closed for write")
|
||||||
|
)
|
||||||
|
|
||||||
|
type win32Pipe struct {
|
||||||
|
*win32File
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
type win32MessageBytePipe struct {
|
||||||
|
win32Pipe
|
||||||
|
writeClosed bool
|
||||||
|
readEOF bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type pipeAddress string
|
||||||
|
|
||||||
|
func (f *win32Pipe) LocalAddr() net.Addr {
|
||||||
|
return pipeAddress(f.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *win32Pipe) RemoteAddr() net.Addr {
|
||||||
|
return pipeAddress(f.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *win32Pipe) SetDeadline(t time.Time) error {
|
||||||
|
f.SetReadDeadline(t)
|
||||||
|
f.SetWriteDeadline(t)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloseWrite closes the write side of a message pipe in byte mode.
|
||||||
|
func (f *win32MessageBytePipe) CloseWrite() error {
|
||||||
|
if f.writeClosed {
|
||||||
|
return errPipeWriteClosed
|
||||||
|
}
|
||||||
|
err := f.win32File.Flush()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = f.win32File.Write(nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f.writeClosed = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes bytes to a message pipe in byte mode. Zero-byte writes are ignored, since
|
||||||
|
// they are used to implement CloseWrite().
|
||||||
|
func (f *win32MessageBytePipe) Write(b []byte) (int, error) {
|
||||||
|
if f.writeClosed {
|
||||||
|
return 0, errPipeWriteClosed
|
||||||
|
}
|
||||||
|
if len(b) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
return f.win32File.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads bytes from a message pipe in byte mode. A read of a zero-byte message on a message
|
||||||
|
// mode pipe will return io.EOF, as will all subsequent reads.
|
||||||
|
func (f *win32MessageBytePipe) Read(b []byte) (int, error) {
|
||||||
|
if f.readEOF {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
n, err := f.win32File.Read(b)
|
||||||
|
if err == io.EOF {
|
||||||
|
// If this was the result of a zero-byte read, then
|
||||||
|
// it is possible that the read was due to a zero-size
|
||||||
|
// message. Since we are simulating CloseWrite with a
|
||||||
|
// zero-byte message, ensure that all future Read() calls
|
||||||
|
// also return EOF.
|
||||||
|
f.readEOF = true
|
||||||
|
} else if err == syscall.ERROR_MORE_DATA {
|
||||||
|
// ERROR_MORE_DATA indicates that the pipe's read mode is message mode
|
||||||
|
// and the message still has more bytes. Treat this as a success, since
|
||||||
|
// this package presents all named pipes as byte streams.
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s pipeAddress) Network() string {
|
||||||
|
return "pipe"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s pipeAddress) String() string {
|
||||||
|
return string(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialPipe connects to a named pipe by path, timing out if the connection
|
||||||
|
// takes longer than the specified duration. If timeout is nil, then we use
|
||||||
|
// a default timeout of 5 seconds. (We do not use WaitNamedPipe.)
|
||||||
|
func DialPipe(path string, timeout *time.Duration) (net.Conn, error) {
|
||||||
|
var absTimeout time.Time
|
||||||
|
if timeout != nil {
|
||||||
|
absTimeout = time.Now().Add(*timeout)
|
||||||
|
} else {
|
||||||
|
absTimeout = time.Now().Add(time.Second * 2)
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
var h syscall.Handle
|
||||||
|
for {
|
||||||
|
h, err = createFile(path, syscall.GENERIC_READ|syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED|cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0)
|
||||||
|
if err != cERROR_PIPE_BUSY {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if time.Now().After(absTimeout) {
|
||||||
|
return nil, ErrTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait 10 msec and try again. This is a rather simplistic
|
||||||
|
// view, as we always try each 10 milliseconds.
|
||||||
|
time.Sleep(time.Millisecond * 10)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, &os.PathError{Op: "open", Path: path, Err: err}
|
||||||
|
}
|
||||||
|
|
||||||
|
var flags uint32
|
||||||
|
err = getNamedPipeInfo(h, &flags, nil, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := makeWin32File(h)
|
||||||
|
if err != nil {
|
||||||
|
syscall.Close(h)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the pipe is in message mode, return a message byte pipe, which
|
||||||
|
// supports CloseWrite().
|
||||||
|
if flags&cPIPE_TYPE_MESSAGE != 0 {
|
||||||
|
return &win32MessageBytePipe{
|
||||||
|
win32Pipe: win32Pipe{win32File: f, path: path},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return &win32Pipe{win32File: f, path: path}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type acceptResponse struct {
|
||||||
|
f *win32File
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
type win32PipeListener struct {
|
||||||
|
firstHandle syscall.Handle
|
||||||
|
path string
|
||||||
|
securityDescriptor []byte
|
||||||
|
config PipeConfig
|
||||||
|
acceptCh chan (chan acceptResponse)
|
||||||
|
closeCh chan int
|
||||||
|
doneCh chan int
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeServerPipeHandle(path string, securityDescriptor []byte, c *PipeConfig, first bool) (syscall.Handle, error) {
|
||||||
|
var flags uint32 = cPIPE_ACCESS_DUPLEX | syscall.FILE_FLAG_OVERLAPPED
|
||||||
|
if first {
|
||||||
|
flags |= cFILE_FLAG_FIRST_PIPE_INSTANCE
|
||||||
|
}
|
||||||
|
|
||||||
|
var mode uint32 = cPIPE_REJECT_REMOTE_CLIENTS
|
||||||
|
if c.MessageMode {
|
||||||
|
mode |= cPIPE_TYPE_MESSAGE
|
||||||
|
}
|
||||||
|
|
||||||
|
sa := &syscall.SecurityAttributes{}
|
||||||
|
sa.Length = uint32(unsafe.Sizeof(*sa))
|
||||||
|
if securityDescriptor != nil {
|
||||||
|
len := uint32(len(securityDescriptor))
|
||||||
|
sa.SecurityDescriptor = localAlloc(0, len)
|
||||||
|
defer localFree(sa.SecurityDescriptor)
|
||||||
|
copy((*[0xffff]byte)(unsafe.Pointer(sa.SecurityDescriptor))[:], securityDescriptor)
|
||||||
|
}
|
||||||
|
h, err := createNamedPipe(path, flags, mode, cPIPE_UNLIMITED_INSTANCES, uint32(c.OutputBufferSize), uint32(c.InputBufferSize), 0, sa)
|
||||||
|
if err != nil {
|
||||||
|
return 0, &os.PathError{Op: "open", Path: path, Err: err}
|
||||||
|
}
|
||||||
|
return h, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *win32PipeListener) makeServerPipe() (*win32File, error) {
|
||||||
|
h, err := makeServerPipeHandle(l.path, l.securityDescriptor, &l.config, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
f, err := makeWin32File(h)
|
||||||
|
if err != nil {
|
||||||
|
syscall.Close(h)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *win32PipeListener) makeConnectedServerPipe() (*win32File, error) {
|
||||||
|
p, err := l.makeServerPipe()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the client to connect.
|
||||||
|
ch := make(chan error)
|
||||||
|
go func(p *win32File) {
|
||||||
|
ch <- connectPipe(p)
|
||||||
|
}(p)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err = <-ch:
|
||||||
|
if err != nil {
|
||||||
|
p.Close()
|
||||||
|
p = nil
|
||||||
|
}
|
||||||
|
case <-l.closeCh:
|
||||||
|
// Abort the connect request by closing the handle.
|
||||||
|
p.Close()
|
||||||
|
p = nil
|
||||||
|
err = <-ch
|
||||||
|
if err == nil || err == ErrFileClosed {
|
||||||
|
err = ErrPipeListenerClosed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *win32PipeListener) listenerRoutine() {
|
||||||
|
closed := false
|
||||||
|
for !closed {
|
||||||
|
select {
|
||||||
|
case <-l.closeCh:
|
||||||
|
closed = true
|
||||||
|
case responseCh := <-l.acceptCh:
|
||||||
|
var (
|
||||||
|
p *win32File
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
for {
|
||||||
|
p, err = l.makeConnectedServerPipe()
|
||||||
|
// If the connection was immediately closed by the client, try
|
||||||
|
// again.
|
||||||
|
if err != cERROR_NO_DATA {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
responseCh <- acceptResponse{p, err}
|
||||||
|
closed = err == ErrPipeListenerClosed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
syscall.Close(l.firstHandle)
|
||||||
|
l.firstHandle = 0
|
||||||
|
// Notify Close() and Accept() callers that the handle has been closed.
|
||||||
|
close(l.doneCh)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PipeConfig contain configuration for the pipe listener.
|
||||||
|
type PipeConfig struct {
|
||||||
|
// SecurityDescriptor contains a Windows security descriptor in SDDL format.
|
||||||
|
SecurityDescriptor string
|
||||||
|
|
||||||
|
// MessageMode determines whether the pipe is in byte or message mode. In either
|
||||||
|
// case the pipe is read in byte mode by default. The only practical difference in
|
||||||
|
// this implementation is that CloseWrite() is only supported for message mode pipes;
|
||||||
|
// CloseWrite() is implemented as a zero-byte write, but zero-byte writes are only
|
||||||
|
// transferred to the reader (and returned as io.EOF in this implementation)
|
||||||
|
// when the pipe is in message mode.
|
||||||
|
MessageMode bool
|
||||||
|
|
||||||
|
// InputBufferSize specifies the size the input buffer, in bytes.
|
||||||
|
InputBufferSize int32
|
||||||
|
|
||||||
|
// OutputBufferSize specifies the size the input buffer, in bytes.
|
||||||
|
OutputBufferSize int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListenPipe creates a listener on a Windows named pipe path, e.g. \\.\pipe\mypipe.
|
||||||
|
// The pipe must not already exist.
|
||||||
|
func ListenPipe(path string, c *PipeConfig) (net.Listener, error) {
|
||||||
|
var (
|
||||||
|
sd []byte
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if c == nil {
|
||||||
|
c = &PipeConfig{}
|
||||||
|
}
|
||||||
|
if c.SecurityDescriptor != "" {
|
||||||
|
sd, err = SddlToSecurityDescriptor(c.SecurityDescriptor)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h, err := makeServerPipeHandle(path, sd, c, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Create a client handle and connect it. This results in the pipe
|
||||||
|
// instance always existing, so that clients see ERROR_PIPE_BUSY
|
||||||
|
// rather than ERROR_FILE_NOT_FOUND. This ties the first instance
|
||||||
|
// up so that no other instances can be used. This would have been
|
||||||
|
// cleaner if the Win32 API matched CreateFile with ConnectNamedPipe
|
||||||
|
// instead of CreateNamedPipe. (Apparently created named pipes are
|
||||||
|
// considered to be in listening state regardless of whether any
|
||||||
|
// active calls to ConnectNamedPipe are outstanding.)
|
||||||
|
h2, err := createFile(path, 0, 0, nil, syscall.OPEN_EXISTING, cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0)
|
||||||
|
if err != nil {
|
||||||
|
syscall.Close(h)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Close the client handle. The server side of the instance will
|
||||||
|
// still be busy, leading to ERROR_PIPE_BUSY instead of
|
||||||
|
// ERROR_NOT_FOUND, as long as we don't close the server handle,
|
||||||
|
// or disconnect the client with DisconnectNamedPipe.
|
||||||
|
syscall.Close(h2)
|
||||||
|
l := &win32PipeListener{
|
||||||
|
firstHandle: h,
|
||||||
|
path: path,
|
||||||
|
securityDescriptor: sd,
|
||||||
|
config: *c,
|
||||||
|
acceptCh: make(chan (chan acceptResponse)),
|
||||||
|
closeCh: make(chan int),
|
||||||
|
doneCh: make(chan int),
|
||||||
|
}
|
||||||
|
go l.listenerRoutine()
|
||||||
|
return l, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func connectPipe(p *win32File) error {
|
||||||
|
c, err := p.prepareIo()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer p.wg.Done()
|
||||||
|
|
||||||
|
err = connectNamedPipe(p.handle, &c.o)
|
||||||
|
_, err = p.asyncIo(c, nil, 0, err)
|
||||||
|
if err != nil && err != cERROR_PIPE_CONNECTED {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *win32PipeListener) Accept() (net.Conn, error) {
|
||||||
|
ch := make(chan acceptResponse)
|
||||||
|
select {
|
||||||
|
case l.acceptCh <- ch:
|
||||||
|
response := <-ch
|
||||||
|
err := response.err
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if l.config.MessageMode {
|
||||||
|
return &win32MessageBytePipe{
|
||||||
|
win32Pipe: win32Pipe{win32File: response.f, path: l.path},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return &win32Pipe{win32File: response.f, path: l.path}, nil
|
||||||
|
case <-l.doneCh:
|
||||||
|
return nil, ErrPipeListenerClosed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *win32PipeListener) Close() error {
|
||||||
|
select {
|
||||||
|
case l.closeCh <- 1:
|
||||||
|
<-l.doneCh
|
||||||
|
case <-l.doneCh:
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *win32PipeListener) Addr() net.Addr {
|
||||||
|
return pipeAddress(l.path)
|
||||||
|
}
|
202
vendor/github.com/Microsoft/go-winio/privilege.go
generated
vendored
Normal file
202
vendor/github.com/Microsoft/go-winio/privilege.go
generated
vendored
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"unicode/utf16"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
//sys adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) [true] = advapi32.AdjustTokenPrivileges
|
||||||
|
//sys impersonateSelf(level uint32) (err error) = advapi32.ImpersonateSelf
|
||||||
|
//sys revertToSelf() (err error) = advapi32.RevertToSelf
|
||||||
|
//sys openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) = advapi32.OpenThreadToken
|
||||||
|
//sys getCurrentThread() (h syscall.Handle) = GetCurrentThread
|
||||||
|
//sys lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) = advapi32.LookupPrivilegeValueW
|
||||||
|
//sys lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) = advapi32.LookupPrivilegeNameW
|
||||||
|
//sys lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) = advapi32.LookupPrivilegeDisplayNameW
|
||||||
|
|
||||||
|
const (
|
||||||
|
SE_PRIVILEGE_ENABLED = 2
|
||||||
|
|
||||||
|
ERROR_NOT_ALL_ASSIGNED syscall.Errno = 1300
|
||||||
|
|
||||||
|
SeBackupPrivilege = "SeBackupPrivilege"
|
||||||
|
SeRestorePrivilege = "SeRestorePrivilege"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
securityAnonymous = iota
|
||||||
|
securityIdentification
|
||||||
|
securityImpersonation
|
||||||
|
securityDelegation
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
privNames = make(map[string]uint64)
|
||||||
|
privNameMutex sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
|
// PrivilegeError represents an error enabling privileges.
|
||||||
|
type PrivilegeError struct {
|
||||||
|
privileges []uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PrivilegeError) Error() string {
|
||||||
|
s := ""
|
||||||
|
if len(e.privileges) > 1 {
|
||||||
|
s = "Could not enable privileges "
|
||||||
|
} else {
|
||||||
|
s = "Could not enable privilege "
|
||||||
|
}
|
||||||
|
for i, p := range e.privileges {
|
||||||
|
if i != 0 {
|
||||||
|
s += ", "
|
||||||
|
}
|
||||||
|
s += `"`
|
||||||
|
s += getPrivilegeName(p)
|
||||||
|
s += `"`
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunWithPrivilege enables a single privilege for a function call.
|
||||||
|
func RunWithPrivilege(name string, fn func() error) error {
|
||||||
|
return RunWithPrivileges([]string{name}, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunWithPrivileges enables privileges for a function call.
|
||||||
|
func RunWithPrivileges(names []string, fn func() error) error {
|
||||||
|
privileges, err := mapPrivileges(names)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
token, err := newThreadToken()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer releaseThreadToken(token)
|
||||||
|
err = adjustPrivileges(token, privileges, SE_PRIVILEGE_ENABLED)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return fn()
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapPrivileges(names []string) ([]uint64, error) {
|
||||||
|
var privileges []uint64
|
||||||
|
privNameMutex.Lock()
|
||||||
|
defer privNameMutex.Unlock()
|
||||||
|
for _, name := range names {
|
||||||
|
p, ok := privNames[name]
|
||||||
|
if !ok {
|
||||||
|
err := lookupPrivilegeValue("", name, &p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
privNames[name] = p
|
||||||
|
}
|
||||||
|
privileges = append(privileges, p)
|
||||||
|
}
|
||||||
|
return privileges, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableProcessPrivileges enables privileges globally for the process.
|
||||||
|
func EnableProcessPrivileges(names []string) error {
|
||||||
|
return enableDisableProcessPrivilege(names, SE_PRIVILEGE_ENABLED)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableProcessPrivileges disables privileges globally for the process.
|
||||||
|
func DisableProcessPrivileges(names []string) error {
|
||||||
|
return enableDisableProcessPrivilege(names, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func enableDisableProcessPrivilege(names []string, action uint32) error {
|
||||||
|
privileges, err := mapPrivileges(names)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
p, _ := windows.GetCurrentProcess()
|
||||||
|
var token windows.Token
|
||||||
|
err = windows.OpenProcessToken(p, windows.TOKEN_ADJUST_PRIVILEGES|windows.TOKEN_QUERY, &token)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer token.Close()
|
||||||
|
return adjustPrivileges(token, privileges, action)
|
||||||
|
}
|
||||||
|
|
||||||
|
func adjustPrivileges(token windows.Token, privileges []uint64, action uint32) error {
|
||||||
|
var b bytes.Buffer
|
||||||
|
binary.Write(&b, binary.LittleEndian, uint32(len(privileges)))
|
||||||
|
for _, p := range privileges {
|
||||||
|
binary.Write(&b, binary.LittleEndian, p)
|
||||||
|
binary.Write(&b, binary.LittleEndian, action)
|
||||||
|
}
|
||||||
|
prevState := make([]byte, b.Len())
|
||||||
|
reqSize := uint32(0)
|
||||||
|
success, err := adjustTokenPrivileges(token, false, &b.Bytes()[0], uint32(len(prevState)), &prevState[0], &reqSize)
|
||||||
|
if !success {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err == ERROR_NOT_ALL_ASSIGNED {
|
||||||
|
return &PrivilegeError{privileges}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPrivilegeName(luid uint64) string {
|
||||||
|
var nameBuffer [256]uint16
|
||||||
|
bufSize := uint32(len(nameBuffer))
|
||||||
|
err := lookupPrivilegeName("", &luid, &nameBuffer[0], &bufSize)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Sprintf("<unknown privilege %d>", luid)
|
||||||
|
}
|
||||||
|
|
||||||
|
var displayNameBuffer [256]uint16
|
||||||
|
displayBufSize := uint32(len(displayNameBuffer))
|
||||||
|
var langID uint32
|
||||||
|
err = lookupPrivilegeDisplayName("", &nameBuffer[0], &displayNameBuffer[0], &displayBufSize, &langID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Sprintf("<unknown privilege %s>", string(utf16.Decode(nameBuffer[:bufSize])))
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(utf16.Decode(displayNameBuffer[:displayBufSize]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func newThreadToken() (windows.Token, error) {
|
||||||
|
err := impersonateSelf(securityImpersonation)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var token windows.Token
|
||||||
|
err = openThreadToken(getCurrentThread(), syscall.TOKEN_ADJUST_PRIVILEGES|syscall.TOKEN_QUERY, false, &token)
|
||||||
|
if err != nil {
|
||||||
|
rerr := revertToSelf()
|
||||||
|
if rerr != nil {
|
||||||
|
panic(rerr)
|
||||||
|
}
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return token, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func releaseThreadToken(h windows.Token) {
|
||||||
|
err := revertToSelf()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
h.Close()
|
||||||
|
}
|
128
vendor/github.com/Microsoft/go-winio/reparse.go
generated
vendored
Normal file
128
vendor/github.com/Microsoft/go-winio/reparse.go
generated
vendored
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf16"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
reparseTagMountPoint = 0xA0000003
|
||||||
|
reparseTagSymlink = 0xA000000C
|
||||||
|
)
|
||||||
|
|
||||||
|
type reparseDataBuffer struct {
|
||||||
|
ReparseTag uint32
|
||||||
|
ReparseDataLength uint16
|
||||||
|
Reserved uint16
|
||||||
|
SubstituteNameOffset uint16
|
||||||
|
SubstituteNameLength uint16
|
||||||
|
PrintNameOffset uint16
|
||||||
|
PrintNameLength uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReparsePoint describes a Win32 symlink or mount point.
|
||||||
|
type ReparsePoint struct {
|
||||||
|
Target string
|
||||||
|
IsMountPoint bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnsupportedReparsePointError is returned when trying to decode a non-symlink or
|
||||||
|
// mount point reparse point.
|
||||||
|
type UnsupportedReparsePointError struct {
|
||||||
|
Tag uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *UnsupportedReparsePointError) Error() string {
|
||||||
|
return fmt.Sprintf("unsupported reparse point %x", e.Tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeReparsePoint decodes a Win32 REPARSE_DATA_BUFFER structure containing either a symlink
|
||||||
|
// or a mount point.
|
||||||
|
func DecodeReparsePoint(b []byte) (*ReparsePoint, error) {
|
||||||
|
tag := binary.LittleEndian.Uint32(b[0:4])
|
||||||
|
return DecodeReparsePointData(tag, b[8:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func DecodeReparsePointData(tag uint32, b []byte) (*ReparsePoint, error) {
|
||||||
|
isMountPoint := false
|
||||||
|
switch tag {
|
||||||
|
case reparseTagMountPoint:
|
||||||
|
isMountPoint = true
|
||||||
|
case reparseTagSymlink:
|
||||||
|
default:
|
||||||
|
return nil, &UnsupportedReparsePointError{tag}
|
||||||
|
}
|
||||||
|
nameOffset := 8 + binary.LittleEndian.Uint16(b[4:6])
|
||||||
|
if !isMountPoint {
|
||||||
|
nameOffset += 4
|
||||||
|
}
|
||||||
|
nameLength := binary.LittleEndian.Uint16(b[6:8])
|
||||||
|
name := make([]uint16, nameLength/2)
|
||||||
|
err := binary.Read(bytes.NewReader(b[nameOffset:nameOffset+nameLength]), binary.LittleEndian, &name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &ReparsePoint{string(utf16.Decode(name)), isMountPoint}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isDriveLetter(c byte) bool {
|
||||||
|
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeReparsePoint encodes a Win32 REPARSE_DATA_BUFFER structure describing a symlink or
|
||||||
|
// mount point.
|
||||||
|
func EncodeReparsePoint(rp *ReparsePoint) []byte {
|
||||||
|
// Generate an NT path and determine if this is a relative path.
|
||||||
|
var ntTarget string
|
||||||
|
relative := false
|
||||||
|
if strings.HasPrefix(rp.Target, `\\?\`) {
|
||||||
|
ntTarget = `\??\` + rp.Target[4:]
|
||||||
|
} else if strings.HasPrefix(rp.Target, `\\`) {
|
||||||
|
ntTarget = `\??\UNC\` + rp.Target[2:]
|
||||||
|
} else if len(rp.Target) >= 2 && isDriveLetter(rp.Target[0]) && rp.Target[1] == ':' {
|
||||||
|
ntTarget = `\??\` + rp.Target
|
||||||
|
} else {
|
||||||
|
ntTarget = rp.Target
|
||||||
|
relative = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// The paths must be NUL-terminated even though they are counted strings.
|
||||||
|
target16 := utf16.Encode([]rune(rp.Target + "\x00"))
|
||||||
|
ntTarget16 := utf16.Encode([]rune(ntTarget + "\x00"))
|
||||||
|
|
||||||
|
size := int(unsafe.Sizeof(reparseDataBuffer{})) - 8
|
||||||
|
size += len(ntTarget16)*2 + len(target16)*2
|
||||||
|
|
||||||
|
tag := uint32(reparseTagMountPoint)
|
||||||
|
if !rp.IsMountPoint {
|
||||||
|
tag = reparseTagSymlink
|
||||||
|
size += 4 // Add room for symlink flags
|
||||||
|
}
|
||||||
|
|
||||||
|
data := reparseDataBuffer{
|
||||||
|
ReparseTag: tag,
|
||||||
|
ReparseDataLength: uint16(size),
|
||||||
|
SubstituteNameOffset: 0,
|
||||||
|
SubstituteNameLength: uint16((len(ntTarget16) - 1) * 2),
|
||||||
|
PrintNameOffset: uint16(len(ntTarget16) * 2),
|
||||||
|
PrintNameLength: uint16((len(target16) - 1) * 2),
|
||||||
|
}
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
binary.Write(&b, binary.LittleEndian, &data)
|
||||||
|
if !rp.IsMountPoint {
|
||||||
|
flags := uint32(0)
|
||||||
|
if relative {
|
||||||
|
flags |= 1
|
||||||
|
}
|
||||||
|
binary.Write(&b, binary.LittleEndian, flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
binary.Write(&b, binary.LittleEndian, ntTarget16)
|
||||||
|
binary.Write(&b, binary.LittleEndian, target16)
|
||||||
|
return b.Bytes()
|
||||||
|
}
|
98
vendor/github.com/Microsoft/go-winio/sd.go
generated
vendored
Normal file
98
vendor/github.com/Microsoft/go-winio/sd.go
generated
vendored
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
//sys lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountNameW
|
||||||
|
//sys convertSidToStringSid(sid *byte, str **uint16) (err error) = advapi32.ConvertSidToStringSidW
|
||||||
|
//sys convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) = advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW
|
||||||
|
//sys convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) = advapi32.ConvertSecurityDescriptorToStringSecurityDescriptorW
|
||||||
|
//sys localFree(mem uintptr) = LocalFree
|
||||||
|
//sys getSecurityDescriptorLength(sd uintptr) (len uint32) = advapi32.GetSecurityDescriptorLength
|
||||||
|
|
||||||
|
const (
|
||||||
|
cERROR_NONE_MAPPED = syscall.Errno(1332)
|
||||||
|
)
|
||||||
|
|
||||||
|
type AccountLookupError struct {
|
||||||
|
Name string
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *AccountLookupError) Error() string {
|
||||||
|
if e.Name == "" {
|
||||||
|
return "lookup account: empty account name specified"
|
||||||
|
}
|
||||||
|
var s string
|
||||||
|
switch e.Err {
|
||||||
|
case cERROR_NONE_MAPPED:
|
||||||
|
s = "not found"
|
||||||
|
default:
|
||||||
|
s = e.Err.Error()
|
||||||
|
}
|
||||||
|
return "lookup account " + e.Name + ": " + s
|
||||||
|
}
|
||||||
|
|
||||||
|
type SddlConversionError struct {
|
||||||
|
Sddl string
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SddlConversionError) Error() string {
|
||||||
|
return "convert " + e.Sddl + ": " + e.Err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupSidByName looks up the SID of an account by name
|
||||||
|
func LookupSidByName(name string) (sid string, err error) {
|
||||||
|
if name == "" {
|
||||||
|
return "", &AccountLookupError{name, cERROR_NONE_MAPPED}
|
||||||
|
}
|
||||||
|
|
||||||
|
var sidSize, sidNameUse, refDomainSize uint32
|
||||||
|
err = lookupAccountName(nil, name, nil, &sidSize, nil, &refDomainSize, &sidNameUse)
|
||||||
|
if err != nil && err != syscall.ERROR_INSUFFICIENT_BUFFER {
|
||||||
|
return "", &AccountLookupError{name, err}
|
||||||
|
}
|
||||||
|
sidBuffer := make([]byte, sidSize)
|
||||||
|
refDomainBuffer := make([]uint16, refDomainSize)
|
||||||
|
err = lookupAccountName(nil, name, &sidBuffer[0], &sidSize, &refDomainBuffer[0], &refDomainSize, &sidNameUse)
|
||||||
|
if err != nil {
|
||||||
|
return "", &AccountLookupError{name, err}
|
||||||
|
}
|
||||||
|
var strBuffer *uint16
|
||||||
|
err = convertSidToStringSid(&sidBuffer[0], &strBuffer)
|
||||||
|
if err != nil {
|
||||||
|
return "", &AccountLookupError{name, err}
|
||||||
|
}
|
||||||
|
sid = syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(strBuffer))[:])
|
||||||
|
localFree(uintptr(unsafe.Pointer(strBuffer)))
|
||||||
|
return sid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SddlToSecurityDescriptor(sddl string) ([]byte, error) {
|
||||||
|
var sdBuffer uintptr
|
||||||
|
err := convertStringSecurityDescriptorToSecurityDescriptor(sddl, 1, &sdBuffer, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &SddlConversionError{sddl, err}
|
||||||
|
}
|
||||||
|
defer localFree(sdBuffer)
|
||||||
|
sd := make([]byte, getSecurityDescriptorLength(sdBuffer))
|
||||||
|
copy(sd, (*[0xffff]byte)(unsafe.Pointer(sdBuffer))[:len(sd)])
|
||||||
|
return sd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SecurityDescriptorToSddl(sd []byte) (string, error) {
|
||||||
|
var sddl *uint16
|
||||||
|
// The returned string length seems to including an aribtrary number of terminating NULs.
|
||||||
|
// Don't use it.
|
||||||
|
err := convertSecurityDescriptorToStringSecurityDescriptor(&sd[0], 1, 0xff, &sddl, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer localFree(uintptr(unsafe.Pointer(sddl)))
|
||||||
|
return syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(sddl))[:]), nil
|
||||||
|
}
|
3
vendor/github.com/Microsoft/go-winio/syscall.go
generated
vendored
Normal file
3
vendor/github.com/Microsoft/go-winio/syscall.go
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
package winio
|
||||||
|
|
||||||
|
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go file.go pipe.go sd.go fileinfo.go privilege.go backup.go
|
520
vendor/github.com/Microsoft/go-winio/zsyscall_windows.go
generated
vendored
Normal file
520
vendor/github.com/Microsoft/go-winio/zsyscall_windows.go
generated
vendored
Normal file
@ -0,0 +1,520 @@
|
|||||||
|
// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT
|
||||||
|
|
||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ unsafe.Pointer
|
||||||
|
|
||||||
|
// Do the interface allocations only once for common
|
||||||
|
// Errno values.
|
||||||
|
const (
|
||||||
|
errnoERROR_IO_PENDING = 997
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||||
|
)
|
||||||
|
|
||||||
|
// errnoErr returns common boxed Errno values, to prevent
|
||||||
|
// allocations at runtime.
|
||||||
|
func errnoErr(e syscall.Errno) error {
|
||||||
|
switch e {
|
||||||
|
case 0:
|
||||||
|
return nil
|
||||||
|
case errnoERROR_IO_PENDING:
|
||||||
|
return errERROR_IO_PENDING
|
||||||
|
}
|
||||||
|
// TODO: add more here, after collecting data on the common
|
||||||
|
// error values see on Windows. (perhaps when running
|
||||||
|
// all.bat?)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||||
|
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
|
||||||
|
|
||||||
|
procCancelIoEx = modkernel32.NewProc("CancelIoEx")
|
||||||
|
procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
|
||||||
|
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
|
||||||
|
procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes")
|
||||||
|
procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe")
|
||||||
|
procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW")
|
||||||
|
procCreateFileW = modkernel32.NewProc("CreateFileW")
|
||||||
|
procWaitNamedPipeW = modkernel32.NewProc("WaitNamedPipeW")
|
||||||
|
procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo")
|
||||||
|
procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW")
|
||||||
|
procLocalAlloc = modkernel32.NewProc("LocalAlloc")
|
||||||
|
procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW")
|
||||||
|
procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW")
|
||||||
|
procConvertStringSecurityDescriptorToSecurityDescriptorW = modadvapi32.NewProc("ConvertStringSecurityDescriptorToSecurityDescriptorW")
|
||||||
|
procConvertSecurityDescriptorToStringSecurityDescriptorW = modadvapi32.NewProc("ConvertSecurityDescriptorToStringSecurityDescriptorW")
|
||||||
|
procLocalFree = modkernel32.NewProc("LocalFree")
|
||||||
|
procGetSecurityDescriptorLength = modadvapi32.NewProc("GetSecurityDescriptorLength")
|
||||||
|
procGetFileInformationByHandleEx = modkernel32.NewProc("GetFileInformationByHandleEx")
|
||||||
|
procSetFileInformationByHandle = modkernel32.NewProc("SetFileInformationByHandle")
|
||||||
|
procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges")
|
||||||
|
procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf")
|
||||||
|
procRevertToSelf = modadvapi32.NewProc("RevertToSelf")
|
||||||
|
procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken")
|
||||||
|
procGetCurrentThread = modkernel32.NewProc("GetCurrentThread")
|
||||||
|
procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW")
|
||||||
|
procLookupPrivilegeNameW = modadvapi32.NewProc("LookupPrivilegeNameW")
|
||||||
|
procLookupPrivilegeDisplayNameW = modadvapi32.NewProc("LookupPrivilegeDisplayNameW")
|
||||||
|
procBackupRead = modkernel32.NewProc("BackupRead")
|
||||||
|
procBackupWrite = modkernel32.NewProc("BackupWrite")
|
||||||
|
)
|
||||||
|
|
||||||
|
func cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(file), uintptr(unsafe.Pointer(o)), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) {
|
||||||
|
r0, _, e1 := syscall.Syscall6(procCreateIoCompletionPort.Addr(), 4, uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount), 0, 0)
|
||||||
|
newport = syscall.Handle(r0)
|
||||||
|
if newport == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(port), uintptr(unsafe.Pointer(bytes)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(o)), uintptr(timeout), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procSetFileCompletionNotificationModes.Addr(), 2, uintptr(h), uintptr(flags), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(o)), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, err = syscall.UTF16PtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _createNamedPipe(_p0, flags, pipeMode, maxInstances, outSize, inSize, defaultTimeout, sa)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) {
|
||||||
|
r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(pipeMode), uintptr(maxInstances), uintptr(outSize), uintptr(inSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0)
|
||||||
|
handle = syscall.Handle(r0)
|
||||||
|
if handle == syscall.InvalidHandle {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, err = syscall.UTF16PtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _createFile(_p0, access, mode, sa, createmode, attrs, templatefile)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _createFile(name *uint16, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) {
|
||||||
|
r0, _, e1 := syscall.Syscall9(procCreateFileW.Addr(), 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0)
|
||||||
|
handle = syscall.Handle(r0)
|
||||||
|
if handle == syscall.InvalidHandle {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitNamedPipe(name string, timeout uint32) (err error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, err = syscall.UTF16PtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _waitNamedPipe(_p0, timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _waitNamedPipe(name *uint16, timeout uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procWaitNamedPipeW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(timeout), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procGetNamedPipeInfo.Addr(), 5, uintptr(pipe), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(inSize)), uintptr(unsafe.Pointer(maxInstances)), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall9(procGetNamedPipeHandleStateW.Addr(), 7, uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func localAlloc(uFlags uint32, length uint32) (ptr uintptr) {
|
||||||
|
r0, _, _ := syscall.Syscall(procLocalAlloc.Addr(), 2, uintptr(uFlags), uintptr(length), 0)
|
||||||
|
ptr = uintptr(r0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, err = syscall.UTF16PtrFromString(accountName)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _lookupAccountName(systemName, _p0, sid, sidSize, refDomain, refDomainSize, sidNameUse)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall9(procLookupAccountNameW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertSidToStringSid(sid *byte, str **uint16) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procConvertSidToStringSidW.Addr(), 2, uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(str)), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, err = syscall.UTF16PtrFromString(str)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _convertStringSecurityDescriptorToSecurityDescriptor(_p0, revision, sd, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _convertStringSecurityDescriptorToSecurityDescriptor(str *uint16, revision uint32, sd *uintptr, size *uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procConvertStringSecurityDescriptorToSecurityDescriptorW.Addr(), 4, uintptr(unsafe.Pointer(str)), uintptr(revision), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(size)), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procConvertSecurityDescriptorToStringSecurityDescriptorW.Addr(), 5, uintptr(unsafe.Pointer(sd)), uintptr(revision), uintptr(secInfo), uintptr(unsafe.Pointer(sddl)), uintptr(unsafe.Pointer(sddlSize)), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func localFree(mem uintptr) {
|
||||||
|
syscall.Syscall(procLocalFree.Addr(), 1, uintptr(mem), 0, 0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSecurityDescriptorLength(sd uintptr) (len uint32) {
|
||||||
|
r0, _, _ := syscall.Syscall(procGetSecurityDescriptorLength.Addr(), 1, uintptr(sd), 0, 0)
|
||||||
|
len = uint32(r0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFileInformationByHandleEx(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(), 4, uintptr(h), uintptr(class), uintptr(unsafe.Pointer(buffer)), uintptr(size), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func setFileInformationByHandle(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procSetFileInformationByHandle.Addr(), 4, uintptr(h), uintptr(class), uintptr(unsafe.Pointer(buffer)), uintptr(size), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) {
|
||||||
|
var _p0 uint32
|
||||||
|
if releaseAll {
|
||||||
|
_p0 = 1
|
||||||
|
} else {
|
||||||
|
_p0 = 0
|
||||||
|
}
|
||||||
|
r0, _, e1 := syscall.Syscall6(procAdjustTokenPrivileges.Addr(), 6, uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(input)), uintptr(outputSize), uintptr(unsafe.Pointer(output)), uintptr(unsafe.Pointer(requiredSize)))
|
||||||
|
success = r0 != 0
|
||||||
|
if true {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func impersonateSelf(level uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procImpersonateSelf.Addr(), 1, uintptr(level), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func revertToSelf() (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procRevertToSelf.Addr(), 0, 0, 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) {
|
||||||
|
var _p0 uint32
|
||||||
|
if openAsSelf {
|
||||||
|
_p0 = 1
|
||||||
|
} else {
|
||||||
|
_p0 = 0
|
||||||
|
}
|
||||||
|
r1, _, e1 := syscall.Syscall6(procOpenThreadToken.Addr(), 4, uintptr(thread), uintptr(accessMask), uintptr(_p0), uintptr(unsafe.Pointer(token)), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCurrentThread() (h syscall.Handle) {
|
||||||
|
r0, _, _ := syscall.Syscall(procGetCurrentThread.Addr(), 0, 0, 0, 0)
|
||||||
|
h = syscall.Handle(r0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, err = syscall.UTF16PtrFromString(systemName)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p1 *uint16
|
||||||
|
_p1, err = syscall.UTF16PtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _lookupPrivilegeValue(_p0, _p1, luid)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _lookupPrivilegeValue(systemName *uint16, name *uint16, luid *uint64) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procLookupPrivilegeValueW.Addr(), 3, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid)))
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, err = syscall.UTF16PtrFromString(systemName)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _lookupPrivilegeName(_p0, luid, buffer, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _lookupPrivilegeName(systemName *uint16, luid *uint64, buffer *uint16, size *uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeNameW.Addr(), 4, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(luid)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, err = syscall.UTF16PtrFromString(systemName)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _lookupPrivilegeDisplayName(_p0, name, buffer, size, languageId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _lookupPrivilegeDisplayName(systemName *uint16, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeDisplayNameW.Addr(), 5, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), uintptr(unsafe.Pointer(languageId)), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
|
||||||
|
var _p0 *byte
|
||||||
|
if len(b) > 0 {
|
||||||
|
_p0 = &b[0]
|
||||||
|
}
|
||||||
|
var _p1 uint32
|
||||||
|
if abort {
|
||||||
|
_p1 = 1
|
||||||
|
} else {
|
||||||
|
_p1 = 0
|
||||||
|
}
|
||||||
|
var _p2 uint32
|
||||||
|
if processSecurity {
|
||||||
|
_p2 = 1
|
||||||
|
} else {
|
||||||
|
_p2 = 0
|
||||||
|
}
|
||||||
|
r1, _, e1 := syscall.Syscall9(procBackupRead.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesRead)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
|
||||||
|
var _p0 *byte
|
||||||
|
if len(b) > 0 {
|
||||||
|
_p0 = &b[0]
|
||||||
|
}
|
||||||
|
var _p1 uint32
|
||||||
|
if abort {
|
||||||
|
_p1 = 1
|
||||||
|
} else {
|
||||||
|
_p1 = 0
|
||||||
|
}
|
||||||
|
var _p2 uint32
|
||||||
|
if processSecurity {
|
||||||
|
_p2 = 1
|
||||||
|
} else {
|
||||||
|
_p2 = 0
|
||||||
|
}
|
||||||
|
r1, _, e1 := syscall.Syscall9(procBackupWrite.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesWritten)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
21
vendor/github.com/Microsoft/hcsshim/LICENSE
generated
vendored
Normal file
21
vendor/github.com/Microsoft/hcsshim/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015 Microsoft
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
51
vendor/github.com/Microsoft/hcsshim/osversion/osversion.go
generated
vendored
Normal file
51
vendor/github.com/Microsoft/hcsshim/osversion/osversion.go
generated
vendored
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package osversion
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
// OSVersion is a wrapper for Windows version information
|
||||||
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724439(v=vs.85).aspx
|
||||||
|
type OSVersion struct {
|
||||||
|
Version uint32
|
||||||
|
MajorVersion uint8
|
||||||
|
MinorVersion uint8
|
||||||
|
Build uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724833(v=vs.85).aspx
|
||||||
|
type osVersionInfoEx struct {
|
||||||
|
OSVersionInfoSize uint32
|
||||||
|
MajorVersion uint32
|
||||||
|
MinorVersion uint32
|
||||||
|
BuildNumber uint32
|
||||||
|
PlatformID uint32
|
||||||
|
CSDVersion [128]uint16
|
||||||
|
ServicePackMajor uint16
|
||||||
|
ServicePackMinor uint16
|
||||||
|
SuiteMask uint16
|
||||||
|
ProductType byte
|
||||||
|
Reserve byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get gets the operating system version on Windows.
|
||||||
|
// The calling application must be manifested to get the correct version information.
|
||||||
|
func Get() OSVersion {
|
||||||
|
var err error
|
||||||
|
osv := OSVersion{}
|
||||||
|
osv.Version, err = windows.GetVersion()
|
||||||
|
if err != nil {
|
||||||
|
// GetVersion never fails.
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
osv.MajorVersion = uint8(osv.Version & 0xFF)
|
||||||
|
osv.MinorVersion = uint8(osv.Version >> 8 & 0xFF)
|
||||||
|
osv.Build = uint16(osv.Version >> 16)
|
||||||
|
return osv
|
||||||
|
}
|
||||||
|
|
||||||
|
func (osv OSVersion) ToString() string {
|
||||||
|
return fmt.Sprintf("%d.%d.%d", osv.MajorVersion, osv.MinorVersion, osv.Build)
|
||||||
|
}
|
10
vendor/github.com/Microsoft/hcsshim/osversion/windowsbuilds.go
generated
vendored
Normal file
10
vendor/github.com/Microsoft/hcsshim/osversion/windowsbuilds.go
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package osversion
|
||||||
|
|
||||||
|
const (
|
||||||
|
|
||||||
|
// RS2 was a client-only release in case you're asking why it's not in the list.
|
||||||
|
RS1 = 14393
|
||||||
|
RS3 = 16299
|
||||||
|
RS4 = 17134
|
||||||
|
RS5 = 17763
|
||||||
|
)
|
22
vendor/github.com/cenkalti/backoff/.gitignore
generated
vendored
Normal file
22
vendor/github.com/cenkalti/backoff/.gitignore
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Folders
|
||||||
|
_obj
|
||||||
|
_test
|
||||||
|
|
||||||
|
# Architecture specific extensions/prefixes
|
||||||
|
*.[568vq]
|
||||||
|
[568vq].out
|
||||||
|
|
||||||
|
*.cgo1.go
|
||||||
|
*.cgo2.c
|
||||||
|
_cgo_defun.c
|
||||||
|
_cgo_gotypes.go
|
||||||
|
_cgo_export.*
|
||||||
|
|
||||||
|
_testmain.go
|
||||||
|
|
||||||
|
*.exe
|
10
vendor/github.com/cenkalti/backoff/.travis.yml
generated
vendored
Normal file
10
vendor/github.com/cenkalti/backoff/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- 1.7
|
||||||
|
- 1.x
|
||||||
|
- tip
|
||||||
|
before_install:
|
||||||
|
- go get github.com/mattn/goveralls
|
||||||
|
- go get golang.org/x/tools/cmd/cover
|
||||||
|
script:
|
||||||
|
- $HOME/gopath/bin/goveralls -service=travis-ci
|
20
vendor/github.com/cenkalti/backoff/LICENSE
generated
vendored
Normal file
20
vendor/github.com/cenkalti/backoff/LICENSE
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 Cenk Altı
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
30
vendor/github.com/cenkalti/backoff/README.md
generated
vendored
Normal file
30
vendor/github.com/cenkalti/backoff/README.md
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# Exponential Backoff [![GoDoc][godoc image]][godoc] [![Build Status][travis image]][travis] [![Coverage Status][coveralls image]][coveralls]
|
||||||
|
|
||||||
|
This is a Go port of the exponential backoff algorithm from [Google's HTTP Client Library for Java][google-http-java-client].
|
||||||
|
|
||||||
|
[Exponential backoff][exponential backoff wiki]
|
||||||
|
is an algorithm that uses feedback to multiplicatively decrease the rate of some process,
|
||||||
|
in order to gradually find an acceptable rate.
|
||||||
|
The retries exponentially increase and stop increasing when a certain threshold is met.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
See https://godoc.org/github.com/cenkalti/backoff#pkg-examples
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
* I would like to keep this library as small as possible.
|
||||||
|
* Please don't send a PR without opening an issue and discussing it first.
|
||||||
|
* If proposed change is not a common use case, I will probably not accept it.
|
||||||
|
|
||||||
|
[godoc]: https://godoc.org/github.com/cenkalti/backoff
|
||||||
|
[godoc image]: https://godoc.org/github.com/cenkalti/backoff?status.png
|
||||||
|
[travis]: https://travis-ci.org/cenkalti/backoff
|
||||||
|
[travis image]: https://travis-ci.org/cenkalti/backoff.png?branch=master
|
||||||
|
[coveralls]: https://coveralls.io/github/cenkalti/backoff?branch=master
|
||||||
|
[coveralls image]: https://coveralls.io/repos/github/cenkalti/backoff/badge.svg?branch=master
|
||||||
|
|
||||||
|
[google-http-java-client]: https://github.com/google/google-http-java-client/blob/da1aa993e90285ec18579f1553339b00e19b3ab5/google-http-client/src/main/java/com/google/api/client/util/ExponentialBackOff.java
|
||||||
|
[exponential backoff wiki]: http://en.wikipedia.org/wiki/Exponential_backoff
|
||||||
|
|
||||||
|
[advanced example]: https://godoc.org/github.com/cenkalti/backoff#example_
|
66
vendor/github.com/cenkalti/backoff/backoff.go
generated
vendored
Normal file
66
vendor/github.com/cenkalti/backoff/backoff.go
generated
vendored
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
// Package backoff implements backoff algorithms for retrying operations.
|
||||||
|
//
|
||||||
|
// Use Retry function for retrying operations that may fail.
|
||||||
|
// If Retry does not meet your needs,
|
||||||
|
// copy/paste the function into your project and modify as you wish.
|
||||||
|
//
|
||||||
|
// There is also Ticker type similar to time.Ticker.
|
||||||
|
// You can use it if you need to work with channels.
|
||||||
|
//
|
||||||
|
// See Examples section below for usage examples.
|
||||||
|
package backoff
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// BackOff is a backoff policy for retrying an operation.
|
||||||
|
type BackOff interface {
|
||||||
|
// NextBackOff returns the duration to wait before retrying the operation,
|
||||||
|
// or backoff. Stop to indicate that no more retries should be made.
|
||||||
|
//
|
||||||
|
// Example usage:
|
||||||
|
//
|
||||||
|
// duration := backoff.NextBackOff();
|
||||||
|
// if (duration == backoff.Stop) {
|
||||||
|
// // Do not retry operation.
|
||||||
|
// } else {
|
||||||
|
// // Sleep for duration and retry operation.
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
NextBackOff() time.Duration
|
||||||
|
|
||||||
|
// Reset to initial state.
|
||||||
|
Reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop indicates that no more retries should be made for use in NextBackOff().
|
||||||
|
const Stop time.Duration = -1
|
||||||
|
|
||||||
|
// ZeroBackOff is a fixed backoff policy whose backoff time is always zero,
|
||||||
|
// meaning that the operation is retried immediately without waiting, indefinitely.
|
||||||
|
type ZeroBackOff struct{}
|
||||||
|
|
||||||
|
func (b *ZeroBackOff) Reset() {}
|
||||||
|
|
||||||
|
func (b *ZeroBackOff) NextBackOff() time.Duration { return 0 }
|
||||||
|
|
||||||
|
// StopBackOff is a fixed backoff policy that always returns backoff.Stop for
|
||||||
|
// NextBackOff(), meaning that the operation should never be retried.
|
||||||
|
type StopBackOff struct{}
|
||||||
|
|
||||||
|
func (b *StopBackOff) Reset() {}
|
||||||
|
|
||||||
|
func (b *StopBackOff) NextBackOff() time.Duration { return Stop }
|
||||||
|
|
||||||
|
// ConstantBackOff is a backoff policy that always returns the same backoff delay.
|
||||||
|
// This is in contrast to an exponential backoff policy,
|
||||||
|
// which returns a delay that grows longer as you call NextBackOff() over and over again.
|
||||||
|
type ConstantBackOff struct {
|
||||||
|
Interval time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *ConstantBackOff) Reset() {}
|
||||||
|
func (b *ConstantBackOff) NextBackOff() time.Duration { return b.Interval }
|
||||||
|
|
||||||
|
func NewConstantBackOff(d time.Duration) *ConstantBackOff {
|
||||||
|
return &ConstantBackOff{Interval: d}
|
||||||
|
}
|
63
vendor/github.com/cenkalti/backoff/context.go
generated
vendored
Normal file
63
vendor/github.com/cenkalti/backoff/context.go
generated
vendored
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package backoff
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BackOffContext is a backoff policy that stops retrying after the context
|
||||||
|
// is canceled.
|
||||||
|
type BackOffContext interface {
|
||||||
|
BackOff
|
||||||
|
Context() context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
type backOffContext struct {
|
||||||
|
BackOff
|
||||||
|
ctx context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithContext returns a BackOffContext with context ctx
|
||||||
|
//
|
||||||
|
// ctx must not be nil
|
||||||
|
func WithContext(b BackOff, ctx context.Context) BackOffContext {
|
||||||
|
if ctx == nil {
|
||||||
|
panic("nil context")
|
||||||
|
}
|
||||||
|
|
||||||
|
if b, ok := b.(*backOffContext); ok {
|
||||||
|
return &backOffContext{
|
||||||
|
BackOff: b.BackOff,
|
||||||
|
ctx: ctx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &backOffContext{
|
||||||
|
BackOff: b,
|
||||||
|
ctx: ctx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ensureContext(b BackOff) BackOffContext {
|
||||||
|
if cb, ok := b.(BackOffContext); ok {
|
||||||
|
return cb
|
||||||
|
}
|
||||||
|
return WithContext(b, context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backOffContext) Context() context.Context {
|
||||||
|
return b.ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backOffContext) NextBackOff() time.Duration {
|
||||||
|
select {
|
||||||
|
case <-b.ctx.Done():
|
||||||
|
return Stop
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
next := b.BackOff.NextBackOff()
|
||||||
|
if deadline, ok := b.ctx.Deadline(); ok && deadline.Sub(time.Now()) < next {
|
||||||
|
return Stop
|
||||||
|
}
|
||||||
|
return next
|
||||||
|
}
|
153
vendor/github.com/cenkalti/backoff/exponential.go
generated
vendored
Normal file
153
vendor/github.com/cenkalti/backoff/exponential.go
generated
vendored
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
package backoff
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
ExponentialBackOff is a backoff implementation that increases the backoff
|
||||||
|
period for each retry attempt using a randomization function that grows exponentially.
|
||||||
|
|
||||||
|
NextBackOff() is calculated using the following formula:
|
||||||
|
|
||||||
|
randomized interval =
|
||||||
|
RetryInterval * (random value in range [1 - RandomizationFactor, 1 + RandomizationFactor])
|
||||||
|
|
||||||
|
In other words NextBackOff() will range between the randomization factor
|
||||||
|
percentage below and above the retry interval.
|
||||||
|
|
||||||
|
For example, given the following parameters:
|
||||||
|
|
||||||
|
RetryInterval = 2
|
||||||
|
RandomizationFactor = 0.5
|
||||||
|
Multiplier = 2
|
||||||
|
|
||||||
|
the actual backoff period used in the next retry attempt will range between 1 and 3 seconds,
|
||||||
|
multiplied by the exponential, that is, between 2 and 6 seconds.
|
||||||
|
|
||||||
|
Note: MaxInterval caps the RetryInterval and not the randomized interval.
|
||||||
|
|
||||||
|
If the time elapsed since an ExponentialBackOff instance is created goes past the
|
||||||
|
MaxElapsedTime, then the method NextBackOff() starts returning backoff.Stop.
|
||||||
|
|
||||||
|
The elapsed time can be reset by calling Reset().
|
||||||
|
|
||||||
|
Example: Given the following default arguments, for 10 tries the sequence will be,
|
||||||
|
and assuming we go over the MaxElapsedTime on the 10th try:
|
||||||
|
|
||||||
|
Request # RetryInterval (seconds) Randomized Interval (seconds)
|
||||||
|
|
||||||
|
1 0.5 [0.25, 0.75]
|
||||||
|
2 0.75 [0.375, 1.125]
|
||||||
|
3 1.125 [0.562, 1.687]
|
||||||
|
4 1.687 [0.8435, 2.53]
|
||||||
|
5 2.53 [1.265, 3.795]
|
||||||
|
6 3.795 [1.897, 5.692]
|
||||||
|
7 5.692 [2.846, 8.538]
|
||||||
|
8 8.538 [4.269, 12.807]
|
||||||
|
9 12.807 [6.403, 19.210]
|
||||||
|
10 19.210 backoff.Stop
|
||||||
|
|
||||||
|
Note: Implementation is not thread-safe.
|
||||||
|
*/
|
||||||
|
type ExponentialBackOff struct {
|
||||||
|
InitialInterval time.Duration
|
||||||
|
RandomizationFactor float64
|
||||||
|
Multiplier float64
|
||||||
|
MaxInterval time.Duration
|
||||||
|
// After MaxElapsedTime the ExponentialBackOff stops.
|
||||||
|
// It never stops if MaxElapsedTime == 0.
|
||||||
|
MaxElapsedTime time.Duration
|
||||||
|
Clock Clock
|
||||||
|
|
||||||
|
currentInterval time.Duration
|
||||||
|
startTime time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clock is an interface that returns current time for BackOff.
|
||||||
|
type Clock interface {
|
||||||
|
Now() time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default values for ExponentialBackOff.
|
||||||
|
const (
|
||||||
|
DefaultInitialInterval = 500 * time.Millisecond
|
||||||
|
DefaultRandomizationFactor = 0.5
|
||||||
|
DefaultMultiplier = 1.5
|
||||||
|
DefaultMaxInterval = 60 * time.Second
|
||||||
|
DefaultMaxElapsedTime = 15 * time.Minute
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewExponentialBackOff creates an instance of ExponentialBackOff using default values.
|
||||||
|
func NewExponentialBackOff() *ExponentialBackOff {
|
||||||
|
b := &ExponentialBackOff{
|
||||||
|
InitialInterval: DefaultInitialInterval,
|
||||||
|
RandomizationFactor: DefaultRandomizationFactor,
|
||||||
|
Multiplier: DefaultMultiplier,
|
||||||
|
MaxInterval: DefaultMaxInterval,
|
||||||
|
MaxElapsedTime: DefaultMaxElapsedTime,
|
||||||
|
Clock: SystemClock,
|
||||||
|
}
|
||||||
|
b.Reset()
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
type systemClock struct{}
|
||||||
|
|
||||||
|
func (t systemClock) Now() time.Time {
|
||||||
|
return time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SystemClock implements Clock interface that uses time.Now().
|
||||||
|
var SystemClock = systemClock{}
|
||||||
|
|
||||||
|
// Reset the interval back to the initial retry interval and restarts the timer.
|
||||||
|
func (b *ExponentialBackOff) Reset() {
|
||||||
|
b.currentInterval = b.InitialInterval
|
||||||
|
b.startTime = b.Clock.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextBackOff calculates the next backoff interval using the formula:
|
||||||
|
// Randomized interval = RetryInterval +/- (RandomizationFactor * RetryInterval)
|
||||||
|
func (b *ExponentialBackOff) NextBackOff() time.Duration {
|
||||||
|
// Make sure we have not gone over the maximum elapsed time.
|
||||||
|
if b.MaxElapsedTime != 0 && b.GetElapsedTime() > b.MaxElapsedTime {
|
||||||
|
return Stop
|
||||||
|
}
|
||||||
|
defer b.incrementCurrentInterval()
|
||||||
|
return getRandomValueFromInterval(b.RandomizationFactor, rand.Float64(), b.currentInterval)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetElapsedTime returns the elapsed time since an ExponentialBackOff instance
|
||||||
|
// is created and is reset when Reset() is called.
|
||||||
|
//
|
||||||
|
// The elapsed time is computed using time.Now().UnixNano(). It is
|
||||||
|
// safe to call even while the backoff policy is used by a running
|
||||||
|
// ticker.
|
||||||
|
func (b *ExponentialBackOff) GetElapsedTime() time.Duration {
|
||||||
|
return b.Clock.Now().Sub(b.startTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increments the current interval by multiplying it with the multiplier.
|
||||||
|
func (b *ExponentialBackOff) incrementCurrentInterval() {
|
||||||
|
// Check for overflow, if overflow is detected set the current interval to the max interval.
|
||||||
|
if float64(b.currentInterval) >= float64(b.MaxInterval)/b.Multiplier {
|
||||||
|
b.currentInterval = b.MaxInterval
|
||||||
|
} else {
|
||||||
|
b.currentInterval = time.Duration(float64(b.currentInterval) * b.Multiplier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a random value from the following interval:
|
||||||
|
// [randomizationFactor * currentInterval, randomizationFactor * currentInterval].
|
||||||
|
func getRandomValueFromInterval(randomizationFactor, random float64, currentInterval time.Duration) time.Duration {
|
||||||
|
var delta = randomizationFactor * float64(currentInterval)
|
||||||
|
var minInterval = float64(currentInterval) - delta
|
||||||
|
var maxInterval = float64(currentInterval) + delta
|
||||||
|
|
||||||
|
// Get a random value from the range [minInterval, maxInterval].
|
||||||
|
// The formula used below has a +1 because if the minInterval is 1 and the maxInterval is 3 then
|
||||||
|
// we want a 33% chance for selecting either 1, 2 or 3.
|
||||||
|
return time.Duration(minInterval + (random * (maxInterval - minInterval + 1)))
|
||||||
|
}
|
82
vendor/github.com/cenkalti/backoff/retry.go
generated
vendored
Normal file
82
vendor/github.com/cenkalti/backoff/retry.go
generated
vendored
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
package backoff
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// An Operation is executing by Retry() or RetryNotify().
|
||||||
|
// The operation will be retried using a backoff policy if it returns an error.
|
||||||
|
type Operation func() error
|
||||||
|
|
||||||
|
// Notify is a notify-on-error function. It receives an operation error and
|
||||||
|
// backoff delay if the operation failed (with an error).
|
||||||
|
//
|
||||||
|
// NOTE that if the backoff policy stated to stop retrying,
|
||||||
|
// the notify function isn't called.
|
||||||
|
type Notify func(error, time.Duration)
|
||||||
|
|
||||||
|
// Retry the operation o until it does not return error or BackOff stops.
|
||||||
|
// o is guaranteed to be run at least once.
|
||||||
|
//
|
||||||
|
// If o returns a *PermanentError, the operation is not retried, and the
|
||||||
|
// wrapped error is returned.
|
||||||
|
//
|
||||||
|
// Retry sleeps the goroutine for the duration returned by BackOff after a
|
||||||
|
// failed operation returns.
|
||||||
|
func Retry(o Operation, b BackOff) error { return RetryNotify(o, b, nil) }
|
||||||
|
|
||||||
|
// RetryNotify calls notify function with the error and wait duration
|
||||||
|
// for each failed attempt before sleep.
|
||||||
|
func RetryNotify(operation Operation, b BackOff, notify Notify) error {
|
||||||
|
var err error
|
||||||
|
var next time.Duration
|
||||||
|
var t *time.Timer
|
||||||
|
|
||||||
|
cb := ensureContext(b)
|
||||||
|
|
||||||
|
b.Reset()
|
||||||
|
for {
|
||||||
|
if err = operation(); err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if permanent, ok := err.(*PermanentError); ok {
|
||||||
|
return permanent.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
if next = cb.NextBackOff(); next == Stop {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if notify != nil {
|
||||||
|
notify(err, next)
|
||||||
|
}
|
||||||
|
|
||||||
|
if t == nil {
|
||||||
|
t = time.NewTimer(next)
|
||||||
|
defer t.Stop()
|
||||||
|
} else {
|
||||||
|
t.Reset(next)
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-cb.Context().Done():
|
||||||
|
return err
|
||||||
|
case <-t.C:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PermanentError signals that the operation should not be retried.
|
||||||
|
type PermanentError struct {
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PermanentError) Error() string {
|
||||||
|
return e.Err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Permanent wraps the given err in a *PermanentError.
|
||||||
|
func Permanent(err error) *PermanentError {
|
||||||
|
return &PermanentError{
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
82
vendor/github.com/cenkalti/backoff/ticker.go
generated
vendored
Normal file
82
vendor/github.com/cenkalti/backoff/ticker.go
generated
vendored
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
package backoff
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ticker holds a channel that delivers `ticks' of a clock at times reported by a BackOff.
|
||||||
|
//
|
||||||
|
// Ticks will continue to arrive when the previous operation is still running,
|
||||||
|
// so operations that take a while to fail could run in quick succession.
|
||||||
|
type Ticker struct {
|
||||||
|
C <-chan time.Time
|
||||||
|
c chan time.Time
|
||||||
|
b BackOffContext
|
||||||
|
stop chan struct{}
|
||||||
|
stopOnce sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTicker returns a new Ticker containing a channel that will send
|
||||||
|
// the time at times specified by the BackOff argument. Ticker is
|
||||||
|
// guaranteed to tick at least once. The channel is closed when Stop
|
||||||
|
// method is called or BackOff stops. It is not safe to manipulate the
|
||||||
|
// provided backoff policy (notably calling NextBackOff or Reset)
|
||||||
|
// while the ticker is running.
|
||||||
|
func NewTicker(b BackOff) *Ticker {
|
||||||
|
c := make(chan time.Time)
|
||||||
|
t := &Ticker{
|
||||||
|
C: c,
|
||||||
|
c: c,
|
||||||
|
b: ensureContext(b),
|
||||||
|
stop: make(chan struct{}),
|
||||||
|
}
|
||||||
|
t.b.Reset()
|
||||||
|
go t.run()
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop turns off a ticker. After Stop, no more ticks will be sent.
|
||||||
|
func (t *Ticker) Stop() {
|
||||||
|
t.stopOnce.Do(func() { close(t.stop) })
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Ticker) run() {
|
||||||
|
c := t.c
|
||||||
|
defer close(c)
|
||||||
|
|
||||||
|
// Ticker is guaranteed to tick at least once.
|
||||||
|
afterC := t.send(time.Now())
|
||||||
|
|
||||||
|
for {
|
||||||
|
if afterC == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case tick := <-afterC:
|
||||||
|
afterC = t.send(tick)
|
||||||
|
case <-t.stop:
|
||||||
|
t.c = nil // Prevent future ticks from being sent to the channel.
|
||||||
|
return
|
||||||
|
case <-t.b.Context().Done():
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Ticker) send(tick time.Time) <-chan time.Time {
|
||||||
|
select {
|
||||||
|
case t.c <- tick:
|
||||||
|
case <-t.stop:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
next := t.b.NextBackOff()
|
||||||
|
if next == Stop {
|
||||||
|
t.Stop()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return time.After(next)
|
||||||
|
}
|
35
vendor/github.com/cenkalti/backoff/tries.go
generated
vendored
Normal file
35
vendor/github.com/cenkalti/backoff/tries.go
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package backoff
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
/*
|
||||||
|
WithMaxRetries creates a wrapper around another BackOff, which will
|
||||||
|
return Stop if NextBackOff() has been called too many times since
|
||||||
|
the last time Reset() was called
|
||||||
|
|
||||||
|
Note: Implementation is not thread-safe.
|
||||||
|
*/
|
||||||
|
func WithMaxRetries(b BackOff, max uint64) BackOff {
|
||||||
|
return &backOffTries{delegate: b, maxTries: max}
|
||||||
|
}
|
||||||
|
|
||||||
|
type backOffTries struct {
|
||||||
|
delegate BackOff
|
||||||
|
maxTries uint64
|
||||||
|
numTries uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backOffTries) NextBackOff() time.Duration {
|
||||||
|
if b.maxTries > 0 {
|
||||||
|
if b.maxTries <= b.numTries {
|
||||||
|
return Stop
|
||||||
|
}
|
||||||
|
b.numTries++
|
||||||
|
}
|
||||||
|
return b.delegate.NextBackOff()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backOffTries) Reset() {
|
||||||
|
b.numTries = 0
|
||||||
|
b.delegate.Reset()
|
||||||
|
}
|
16
vendor/github.com/containerd/continuity/AUTHORS
generated
vendored
Normal file
16
vendor/github.com/containerd/continuity/AUTHORS
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
Aaron Lehmann <aaron.lehmann@docker.com>
|
||||||
|
Akash Gupta <akagup@microsoft.com>
|
||||||
|
Akihiro Suda <suda.akihiro@lab.ntt.co.jp>
|
||||||
|
Andrew Pennebaker <apennebaker@datapipe.com>
|
||||||
|
Brandon Philips <brandon.philips@coreos.com>
|
||||||
|
Christopher Jones <tophj@linux.vnet.ibm.com>
|
||||||
|
Daniel, Dao Quang Minh <dqminh89@gmail.com>
|
||||||
|
Derek McGowan <derek@mcgstyle.net>
|
||||||
|
Edward Pilatowicz <edward.pilatowicz@oracle.com>
|
||||||
|
Ian Campbell <ijc@docker.com>
|
||||||
|
Justin Cormack <justin.cormack@docker.com>
|
||||||
|
Justin Cummins <sul3n3t@gmail.com>
|
||||||
|
Phil Estes <estesp@gmail.com>
|
||||||
|
Stephen J Day <stephen.day@docker.com>
|
||||||
|
Tobias Klauser <tklauser@distanz.ch>
|
||||||
|
Tonis Tiigi <tonistiigi@gmail.com>
|
191
vendor/github.com/containerd/continuity/LICENSE
generated
vendored
Normal file
191
vendor/github.com/containerd/continuity/LICENSE
generated
vendored
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
https://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
Copyright The containerd Authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
172
vendor/github.com/containerd/continuity/fs/copy.go
generated
vendored
Normal file
172
vendor/github.com/containerd/continuity/fs/copy.go
generated
vendored
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var bufferPool = &sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
buffer := make([]byte, 32*1024)
|
||||||
|
return &buffer
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// XAttrErrorHandlers transform a non-nil xattr error.
|
||||||
|
// Return nil to ignore an error.
|
||||||
|
// xattrKey can be empty for listxattr operation.
|
||||||
|
type XAttrErrorHandler func(dst, src, xattrKey string, err error) error
|
||||||
|
|
||||||
|
type copyDirOpts struct {
|
||||||
|
xeh XAttrErrorHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
type CopyDirOpt func(*copyDirOpts) error
|
||||||
|
|
||||||
|
// WithXAttrErrorHandler allows specifying XAttrErrorHandler
|
||||||
|
// If nil XAttrErrorHandler is specified (default), CopyDir stops
|
||||||
|
// on a non-nil xattr error.
|
||||||
|
func WithXAttrErrorHandler(xeh XAttrErrorHandler) CopyDirOpt {
|
||||||
|
return func(o *copyDirOpts) error {
|
||||||
|
o.xeh = xeh
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithAllowXAttrErrors allows ignoring xattr errors.
|
||||||
|
func WithAllowXAttrErrors() CopyDirOpt {
|
||||||
|
xeh := func(dst, src, xattrKey string, err error) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return WithXAttrErrorHandler(xeh)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CopyDir copies the directory from src to dst.
|
||||||
|
// Most efficient copy of files is attempted.
|
||||||
|
func CopyDir(dst, src string, opts ...CopyDirOpt) error {
|
||||||
|
var o copyDirOpts
|
||||||
|
for _, opt := range opts {
|
||||||
|
if err := opt(&o); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inodes := map[uint64]string{}
|
||||||
|
return copyDirectory(dst, src, inodes, &o)
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyDirectory(dst, src string, inodes map[uint64]string, o *copyDirOpts) error {
|
||||||
|
stat, err := os.Stat(src)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to stat %s", src)
|
||||||
|
}
|
||||||
|
if !stat.IsDir() {
|
||||||
|
return errors.Errorf("source is not directory")
|
||||||
|
}
|
||||||
|
|
||||||
|
if st, err := os.Stat(dst); err != nil {
|
||||||
|
if err := os.Mkdir(dst, stat.Mode()); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to mkdir %s", dst)
|
||||||
|
}
|
||||||
|
} else if !st.IsDir() {
|
||||||
|
return errors.Errorf("cannot copy to non-directory: %s", dst)
|
||||||
|
} else {
|
||||||
|
if err := os.Chmod(dst, stat.Mode()); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to chmod on %s", dst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fis, err := ioutil.ReadDir(src)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to read %s", src)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := copyFileInfo(stat, dst); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to copy file info for %s", dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, fi := range fis {
|
||||||
|
source := filepath.Join(src, fi.Name())
|
||||||
|
target := filepath.Join(dst, fi.Name())
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case fi.IsDir():
|
||||||
|
if err := copyDirectory(target, source, inodes, o); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
case (fi.Mode() & os.ModeType) == 0:
|
||||||
|
link, err := getLinkSource(target, fi, inodes)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to get hardlink")
|
||||||
|
}
|
||||||
|
if link != "" {
|
||||||
|
if err := os.Link(link, target); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to create hard link")
|
||||||
|
}
|
||||||
|
} else if err := CopyFile(target, source); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to copy files")
|
||||||
|
}
|
||||||
|
case (fi.Mode() & os.ModeSymlink) == os.ModeSymlink:
|
||||||
|
link, err := os.Readlink(source)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to read link: %s", source)
|
||||||
|
}
|
||||||
|
if err := os.Symlink(link, target); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to create symlink: %s", target)
|
||||||
|
}
|
||||||
|
case (fi.Mode() & os.ModeDevice) == os.ModeDevice:
|
||||||
|
if err := copyDevice(target, fi); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to create device")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// TODO: Support pipes and sockets
|
||||||
|
return errors.Wrapf(err, "unsupported mode %s", fi.Mode())
|
||||||
|
}
|
||||||
|
if err := copyFileInfo(fi, target); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to copy file info")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := copyXAttrs(target, source, o.xeh); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to copy xattrs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CopyFile copies the source file to the target.
|
||||||
|
// The most efficient means of copying is used for the platform.
|
||||||
|
func CopyFile(target, source string) error {
|
||||||
|
src, err := os.Open(source)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to open source %s", source)
|
||||||
|
}
|
||||||
|
defer src.Close()
|
||||||
|
tgt, err := os.Create(target)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to open target %s", target)
|
||||||
|
}
|
||||||
|
defer tgt.Close()
|
||||||
|
|
||||||
|
return copyFileContent(tgt, src)
|
||||||
|
}
|
144
vendor/github.com/containerd/continuity/fs/copy_linux.go
generated
vendored
Normal file
144
vendor/github.com/containerd/continuity/fs/copy_linux.go
generated
vendored
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/containerd/continuity/sysx"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
func copyFileInfo(fi os.FileInfo, name string) error {
|
||||||
|
st := fi.Sys().(*syscall.Stat_t)
|
||||||
|
if err := os.Lchown(name, int(st.Uid), int(st.Gid)); err != nil {
|
||||||
|
if os.IsPermission(err) {
|
||||||
|
// Normally if uid/gid are the same this would be a no-op, but some
|
||||||
|
// filesystems may still return EPERM... for instance NFS does this.
|
||||||
|
// In such a case, this is not an error.
|
||||||
|
if dstStat, err2 := os.Lstat(name); err2 == nil {
|
||||||
|
st2 := dstStat.Sys().(*syscall.Stat_t)
|
||||||
|
if st.Uid == st2.Uid && st.Gid == st2.Gid {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to chown %s", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fi.Mode() & os.ModeSymlink) != os.ModeSymlink {
|
||||||
|
if err := os.Chmod(name, fi.Mode()); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to chmod %s", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
timespec := []unix.Timespec{unix.Timespec(StatAtime(st)), unix.Timespec(StatMtime(st))}
|
||||||
|
if err := unix.UtimesNanoAt(unix.AT_FDCWD, name, timespec, unix.AT_SYMLINK_NOFOLLOW); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to utime %s", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxSSizeT = int64(^uint(0) >> 1)
|
||||||
|
|
||||||
|
func copyFileContent(dst, src *os.File) error {
|
||||||
|
st, err := src.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "unable to stat source")
|
||||||
|
}
|
||||||
|
|
||||||
|
size := st.Size()
|
||||||
|
first := true
|
||||||
|
srcFd := int(src.Fd())
|
||||||
|
dstFd := int(dst.Fd())
|
||||||
|
|
||||||
|
for size > 0 {
|
||||||
|
// Ensure that we are never trying to copy more than SSIZE_MAX at a
|
||||||
|
// time and at the same time avoids overflows when the file is larger
|
||||||
|
// than 4GB on 32-bit systems.
|
||||||
|
var copySize int
|
||||||
|
if size > maxSSizeT {
|
||||||
|
copySize = int(maxSSizeT)
|
||||||
|
} else {
|
||||||
|
copySize = int(size)
|
||||||
|
}
|
||||||
|
n, err := unix.CopyFileRange(srcFd, nil, dstFd, nil, copySize, 0)
|
||||||
|
if err != nil {
|
||||||
|
if (err != unix.ENOSYS && err != unix.EXDEV) || !first {
|
||||||
|
return errors.Wrap(err, "copy file range failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := bufferPool.Get().(*[]byte)
|
||||||
|
_, err = io.CopyBuffer(dst, src, *buf)
|
||||||
|
bufferPool.Put(buf)
|
||||||
|
return errors.Wrap(err, "userspace copy failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
first = false
|
||||||
|
size -= int64(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyXAttrs(dst, src string, xeh XAttrErrorHandler) error {
|
||||||
|
xattrKeys, err := sysx.LListxattr(src)
|
||||||
|
if err != nil {
|
||||||
|
e := errors.Wrapf(err, "failed to list xattrs on %s", src)
|
||||||
|
if xeh != nil {
|
||||||
|
e = xeh(dst, src, "", e)
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
for _, xattr := range xattrKeys {
|
||||||
|
data, err := sysx.LGetxattr(src, xattr)
|
||||||
|
if err != nil {
|
||||||
|
e := errors.Wrapf(err, "failed to get xattr %q on %s", xattr, src)
|
||||||
|
if xeh != nil {
|
||||||
|
if e = xeh(dst, src, xattr, e); e == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
if err := sysx.LSetxattr(dst, xattr, data, 0); err != nil {
|
||||||
|
e := errors.Wrapf(err, "failed to set xattr %q on %s", xattr, dst)
|
||||||
|
if xeh != nil {
|
||||||
|
if e = xeh(dst, src, xattr, e); e == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyDevice(dst string, fi os.FileInfo) error {
|
||||||
|
st, ok := fi.Sys().(*syscall.Stat_t)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("unsupported stat type")
|
||||||
|
}
|
||||||
|
return unix.Mknod(dst, uint32(fi.Mode()), int(st.Rdev))
|
||||||
|
}
|
112
vendor/github.com/containerd/continuity/fs/copy_unix.go
generated
vendored
Normal file
112
vendor/github.com/containerd/continuity/fs/copy_unix.go
generated
vendored
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
// +build solaris darwin freebsd
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/containerd/continuity/sysx"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
func copyFileInfo(fi os.FileInfo, name string) error {
|
||||||
|
st := fi.Sys().(*syscall.Stat_t)
|
||||||
|
if err := os.Lchown(name, int(st.Uid), int(st.Gid)); err != nil {
|
||||||
|
if os.IsPermission(err) {
|
||||||
|
// Normally if uid/gid are the same this would be a no-op, but some
|
||||||
|
// filesystems may still return EPERM... for instance NFS does this.
|
||||||
|
// In such a case, this is not an error.
|
||||||
|
if dstStat, err2 := os.Lstat(name); err2 == nil {
|
||||||
|
st2 := dstStat.Sys().(*syscall.Stat_t)
|
||||||
|
if st.Uid == st2.Uid && st.Gid == st2.Gid {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to chown %s", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fi.Mode() & os.ModeSymlink) != os.ModeSymlink {
|
||||||
|
if err := os.Chmod(name, fi.Mode()); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to chmod %s", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
timespec := []syscall.Timespec{StatAtime(st), StatMtime(st)}
|
||||||
|
if err := syscall.UtimesNano(name, timespec); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to utime %s", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyFileContent(dst, src *os.File) error {
|
||||||
|
buf := bufferPool.Get().(*[]byte)
|
||||||
|
_, err := io.CopyBuffer(dst, src, *buf)
|
||||||
|
bufferPool.Put(buf)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyXAttrs(dst, src string, xeh XAttrErrorHandler) error {
|
||||||
|
xattrKeys, err := sysx.LListxattr(src)
|
||||||
|
if err != nil {
|
||||||
|
e := errors.Wrapf(err, "failed to list xattrs on %s", src)
|
||||||
|
if xeh != nil {
|
||||||
|
e = xeh(dst, src, "", e)
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
for _, xattr := range xattrKeys {
|
||||||
|
data, err := sysx.LGetxattr(src, xattr)
|
||||||
|
if err != nil {
|
||||||
|
e := errors.Wrapf(err, "failed to get xattr %q on %s", xattr, src)
|
||||||
|
if xeh != nil {
|
||||||
|
if e = xeh(dst, src, xattr, e); e == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
if err := sysx.LSetxattr(dst, xattr, data, 0); err != nil {
|
||||||
|
e := errors.Wrapf(err, "failed to set xattr %q on %s", xattr, dst)
|
||||||
|
if xeh != nil {
|
||||||
|
if e = xeh(dst, src, xattr, e); e == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyDevice(dst string, fi os.FileInfo) error {
|
||||||
|
st, ok := fi.Sys().(*syscall.Stat_t)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("unsupported stat type")
|
||||||
|
}
|
||||||
|
return unix.Mknod(dst, uint32(fi.Mode()), int(st.Rdev))
|
||||||
|
}
|
49
vendor/github.com/containerd/continuity/fs/copy_windows.go
generated
vendored
Normal file
49
vendor/github.com/containerd/continuity/fs/copy_windows.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func copyFileInfo(fi os.FileInfo, name string) error {
|
||||||
|
if err := os.Chmod(name, fi.Mode()); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to chmod %s", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: copy windows specific metadata
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyFileContent(dst, src *os.File) error {
|
||||||
|
buf := bufferPool.Get().(*[]byte)
|
||||||
|
_, err := io.CopyBuffer(dst, src, *buf)
|
||||||
|
bufferPool.Put(buf)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyXAttrs(dst, src string, xeh XAttrErrorHandler) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyDevice(dst string, fi os.FileInfo) error {
|
||||||
|
return errors.New("device copy not supported")
|
||||||
|
}
|
326
vendor/github.com/containerd/continuity/fs/diff.go
generated
vendored
Normal file
326
vendor/github.com/containerd/continuity/fs/diff.go
generated
vendored
Normal file
@ -0,0 +1,326 @@
|
|||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ChangeKind is the type of modification that
|
||||||
|
// a change is making.
|
||||||
|
type ChangeKind int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ChangeKindUnmodified represents an unmodified
|
||||||
|
// file
|
||||||
|
ChangeKindUnmodified = iota
|
||||||
|
|
||||||
|
// ChangeKindAdd represents an addition of
|
||||||
|
// a file
|
||||||
|
ChangeKindAdd
|
||||||
|
|
||||||
|
// ChangeKindModify represents a change to
|
||||||
|
// an existing file
|
||||||
|
ChangeKindModify
|
||||||
|
|
||||||
|
// ChangeKindDelete represents a delete of
|
||||||
|
// a file
|
||||||
|
ChangeKindDelete
|
||||||
|
)
|
||||||
|
|
||||||
|
func (k ChangeKind) String() string {
|
||||||
|
switch k {
|
||||||
|
case ChangeKindUnmodified:
|
||||||
|
return "unmodified"
|
||||||
|
case ChangeKindAdd:
|
||||||
|
return "add"
|
||||||
|
case ChangeKindModify:
|
||||||
|
return "modify"
|
||||||
|
case ChangeKindDelete:
|
||||||
|
return "delete"
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change represents single change between a diff and its parent.
|
||||||
|
type Change struct {
|
||||||
|
Kind ChangeKind
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChangeFunc is the type of function called for each change
|
||||||
|
// computed during a directory changes calculation.
|
||||||
|
type ChangeFunc func(ChangeKind, string, os.FileInfo, error) error
|
||||||
|
|
||||||
|
// Changes computes changes between two directories calling the
|
||||||
|
// given change function for each computed change. The first
|
||||||
|
// directory is intended to the base directory and second
|
||||||
|
// directory the changed directory.
|
||||||
|
//
|
||||||
|
// The change callback is called by the order of path names and
|
||||||
|
// should be appliable in that order.
|
||||||
|
// Due to this apply ordering, the following is true
|
||||||
|
// - Removed directory trees only create a single change for the root
|
||||||
|
// directory removed. Remaining changes are implied.
|
||||||
|
// - A directory which is modified to become a file will not have
|
||||||
|
// delete entries for sub-path items, their removal is implied
|
||||||
|
// by the removal of the parent directory.
|
||||||
|
//
|
||||||
|
// Opaque directories will not be treated specially and each file
|
||||||
|
// removed from the base directory will show up as a removal.
|
||||||
|
//
|
||||||
|
// File content comparisons will be done on files which have timestamps
|
||||||
|
// which may have been truncated. If either of the files being compared
|
||||||
|
// has a zero value nanosecond value, each byte will be compared for
|
||||||
|
// differences. If 2 files have the same seconds value but different
|
||||||
|
// nanosecond values where one of those values is zero, the files will
|
||||||
|
// be considered unchanged if the content is the same. This behavior
|
||||||
|
// is to account for timestamp truncation during archiving.
|
||||||
|
func Changes(ctx context.Context, a, b string, changeFn ChangeFunc) error {
|
||||||
|
if a == "" {
|
||||||
|
logrus.Debugf("Using single walk diff for %s", b)
|
||||||
|
return addDirChanges(ctx, changeFn, b)
|
||||||
|
} else if diffOptions := detectDirDiff(b, a); diffOptions != nil {
|
||||||
|
logrus.Debugf("Using single walk diff for %s from %s", diffOptions.diffDir, a)
|
||||||
|
return diffDirChanges(ctx, changeFn, a, diffOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf("Using double walk diff for %s from %s", b, a)
|
||||||
|
return doubleWalkDiff(ctx, changeFn, a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func addDirChanges(ctx context.Context, changeFn ChangeFunc, root string) error {
|
||||||
|
return filepath.Walk(root, func(path string, f os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rebase path
|
||||||
|
path, err = filepath.Rel(root, path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
path = filepath.Join(string(os.PathSeparator), path)
|
||||||
|
|
||||||
|
// Skip root
|
||||||
|
if path == string(os.PathSeparator) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return changeFn(ChangeKindAdd, path, f, nil)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// diffDirOptions is used when the diff can be directly calculated from
|
||||||
|
// a diff directory to its base, without walking both trees.
|
||||||
|
type diffDirOptions struct {
|
||||||
|
diffDir string
|
||||||
|
skipChange func(string) (bool, error)
|
||||||
|
deleteChange func(string, string, os.FileInfo) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// diffDirChanges walks the diff directory and compares changes against the base.
|
||||||
|
func diffDirChanges(ctx context.Context, changeFn ChangeFunc, base string, o *diffDirOptions) error {
|
||||||
|
changedDirs := make(map[string]struct{})
|
||||||
|
return filepath.Walk(o.diffDir, func(path string, f os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rebase path
|
||||||
|
path, err = filepath.Rel(o.diffDir, path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
path = filepath.Join(string(os.PathSeparator), path)
|
||||||
|
|
||||||
|
// Skip root
|
||||||
|
if path == string(os.PathSeparator) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: handle opaqueness, start new double walker at this
|
||||||
|
// location to get deletes, and skip tree in single walker
|
||||||
|
|
||||||
|
if o.skipChange != nil {
|
||||||
|
if skip, err := o.skipChange(path); skip {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var kind ChangeKind
|
||||||
|
|
||||||
|
deletedFile, err := o.deleteChange(o.diffDir, path, f)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find out what kind of modification happened
|
||||||
|
if deletedFile != "" {
|
||||||
|
path = deletedFile
|
||||||
|
kind = ChangeKindDelete
|
||||||
|
f = nil
|
||||||
|
} else {
|
||||||
|
// Otherwise, the file was added
|
||||||
|
kind = ChangeKindAdd
|
||||||
|
|
||||||
|
// ...Unless it already existed in a base, in which case, it's a modification
|
||||||
|
stat, err := os.Stat(filepath.Join(base, path))
|
||||||
|
if err != nil && !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
// The file existed in the base, so that's a modification
|
||||||
|
|
||||||
|
// However, if it's a directory, maybe it wasn't actually modified.
|
||||||
|
// If you modify /foo/bar/baz, then /foo will be part of the changed files only because it's the parent of bar
|
||||||
|
if stat.IsDir() && f.IsDir() {
|
||||||
|
if f.Size() == stat.Size() && f.Mode() == stat.Mode() && sameFsTime(f.ModTime(), stat.ModTime()) {
|
||||||
|
// Both directories are the same, don't record the change
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kind = ChangeKindModify
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If /foo/bar/file.txt is modified, then /foo/bar must be part of the changed files.
|
||||||
|
// This block is here to ensure the change is recorded even if the
|
||||||
|
// modify time, mode and size of the parent directory in the rw and ro layers are all equal.
|
||||||
|
// Check https://github.com/docker/docker/pull/13590 for details.
|
||||||
|
if f.IsDir() {
|
||||||
|
changedDirs[path] = struct{}{}
|
||||||
|
}
|
||||||
|
if kind == ChangeKindAdd || kind == ChangeKindDelete {
|
||||||
|
parent := filepath.Dir(path)
|
||||||
|
if _, ok := changedDirs[parent]; !ok && parent != "/" {
|
||||||
|
pi, err := os.Stat(filepath.Join(o.diffDir, parent))
|
||||||
|
if err := changeFn(ChangeKindModify, parent, pi, err); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
changedDirs[parent] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return changeFn(kind, path, f, nil)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// doubleWalkDiff walks both directories to create a diff
|
||||||
|
func doubleWalkDiff(ctx context.Context, changeFn ChangeFunc, a, b string) (err error) {
|
||||||
|
g, ctx := errgroup.WithContext(ctx)
|
||||||
|
|
||||||
|
var (
|
||||||
|
c1 = make(chan *currentPath)
|
||||||
|
c2 = make(chan *currentPath)
|
||||||
|
|
||||||
|
f1, f2 *currentPath
|
||||||
|
rmdir string
|
||||||
|
)
|
||||||
|
g.Go(func() error {
|
||||||
|
defer close(c1)
|
||||||
|
return pathWalk(ctx, a, c1)
|
||||||
|
})
|
||||||
|
g.Go(func() error {
|
||||||
|
defer close(c2)
|
||||||
|
return pathWalk(ctx, b, c2)
|
||||||
|
})
|
||||||
|
g.Go(func() error {
|
||||||
|
for c1 != nil || c2 != nil {
|
||||||
|
if f1 == nil && c1 != nil {
|
||||||
|
f1, err = nextPath(ctx, c1)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if f1 == nil {
|
||||||
|
c1 = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if f2 == nil && c2 != nil {
|
||||||
|
f2, err = nextPath(ctx, c2)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if f2 == nil {
|
||||||
|
c2 = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if f1 == nil && f2 == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var f os.FileInfo
|
||||||
|
k, p := pathChange(f1, f2)
|
||||||
|
switch k {
|
||||||
|
case ChangeKindAdd:
|
||||||
|
if rmdir != "" {
|
||||||
|
rmdir = ""
|
||||||
|
}
|
||||||
|
f = f2.f
|
||||||
|
f2 = nil
|
||||||
|
case ChangeKindDelete:
|
||||||
|
// Check if this file is already removed by being
|
||||||
|
// under of a removed directory
|
||||||
|
if rmdir != "" && strings.HasPrefix(f1.path, rmdir) {
|
||||||
|
f1 = nil
|
||||||
|
continue
|
||||||
|
} else if f1.f.IsDir() {
|
||||||
|
rmdir = f1.path + string(os.PathSeparator)
|
||||||
|
} else if rmdir != "" {
|
||||||
|
rmdir = ""
|
||||||
|
}
|
||||||
|
f1 = nil
|
||||||
|
case ChangeKindModify:
|
||||||
|
same, err := sameFile(f1, f2)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if f1.f.IsDir() && !f2.f.IsDir() {
|
||||||
|
rmdir = f1.path + string(os.PathSeparator)
|
||||||
|
} else if rmdir != "" {
|
||||||
|
rmdir = ""
|
||||||
|
}
|
||||||
|
f = f2.f
|
||||||
|
f1 = nil
|
||||||
|
f2 = nil
|
||||||
|
if same {
|
||||||
|
if !isLinked(f) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
k = ChangeKindUnmodified
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := changeFn(k, p, f, nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return g.Wait()
|
||||||
|
}
|
74
vendor/github.com/containerd/continuity/fs/diff_unix.go
generated
vendored
Normal file
74
vendor/github.com/containerd/continuity/fs/diff_unix.go
generated
vendored
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// +build !windows
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/containerd/continuity/sysx"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// detectDirDiff returns diff dir options if a directory could
|
||||||
|
// be found in the mount info for upper which is the direct
|
||||||
|
// diff with the provided lower directory
|
||||||
|
func detectDirDiff(upper, lower string) *diffDirOptions {
|
||||||
|
// TODO: get mount options for upper
|
||||||
|
// TODO: detect AUFS
|
||||||
|
// TODO: detect overlay
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// compareSysStat returns whether the stats are equivalent,
|
||||||
|
// whether the files are considered the same file, and
|
||||||
|
// an error
|
||||||
|
func compareSysStat(s1, s2 interface{}) (bool, error) {
|
||||||
|
ls1, ok := s1.(*syscall.Stat_t)
|
||||||
|
if !ok {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
ls2, ok := s2.(*syscall.Stat_t)
|
||||||
|
if !ok {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return ls1.Mode == ls2.Mode && ls1.Uid == ls2.Uid && ls1.Gid == ls2.Gid && ls1.Rdev == ls2.Rdev, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func compareCapabilities(p1, p2 string) (bool, error) {
|
||||||
|
c1, err := sysx.LGetxattr(p1, "security.capability")
|
||||||
|
if err != nil && err != sysx.ENODATA {
|
||||||
|
return false, errors.Wrapf(err, "failed to get xattr for %s", p1)
|
||||||
|
}
|
||||||
|
c2, err := sysx.LGetxattr(p2, "security.capability")
|
||||||
|
if err != nil && err != sysx.ENODATA {
|
||||||
|
return false, errors.Wrapf(err, "failed to get xattr for %s", p2)
|
||||||
|
}
|
||||||
|
return bytes.Equal(c1, c2), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isLinked(f os.FileInfo) bool {
|
||||||
|
s, ok := f.Sys().(*syscall.Stat_t)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return !f.IsDir() && s.Nlink > 1
|
||||||
|
}
|
48
vendor/github.com/containerd/continuity/fs/diff_windows.go
generated
vendored
Normal file
48
vendor/github.com/containerd/continuity/fs/diff_windows.go
generated
vendored
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
func detectDirDiff(upper, lower string) *diffDirOptions {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func compareSysStat(s1, s2 interface{}) (bool, error) {
|
||||||
|
f1, ok := s1.(windows.Win32FileAttributeData)
|
||||||
|
if !ok {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
f2, ok := s2.(windows.Win32FileAttributeData)
|
||||||
|
if !ok {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return f1.FileAttributes == f2.FileAttributes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func compareCapabilities(p1, p2 string) (bool, error) {
|
||||||
|
// TODO: Use windows equivalent
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isLinked(os.FileInfo) bool {
|
||||||
|
return false
|
||||||
|
}
|
103
vendor/github.com/containerd/continuity/fs/dtype_linux.go
generated
vendored
Normal file
103
vendor/github.com/containerd/continuity/fs/dtype_linux.go
generated
vendored
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func locateDummyIfEmpty(path string) (string, error) {
|
||||||
|
children, err := ioutil.ReadDir(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if len(children) != 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
dummyFile, err := ioutil.TempFile(path, "fsutils-dummy")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
name := dummyFile.Name()
|
||||||
|
err = dummyFile.Close()
|
||||||
|
return name, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SupportsDType returns whether the filesystem mounted on path supports d_type
|
||||||
|
func SupportsDType(path string) (bool, error) {
|
||||||
|
// locate dummy so that we have at least one dirent
|
||||||
|
dummy, err := locateDummyIfEmpty(path)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if dummy != "" {
|
||||||
|
defer os.Remove(dummy)
|
||||||
|
}
|
||||||
|
|
||||||
|
visited := 0
|
||||||
|
supportsDType := true
|
||||||
|
fn := func(ent *syscall.Dirent) bool {
|
||||||
|
visited++
|
||||||
|
if ent.Type == syscall.DT_UNKNOWN {
|
||||||
|
supportsDType = false
|
||||||
|
// stop iteration
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// continue iteration
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if err = iterateReadDir(path, fn); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if visited == 0 {
|
||||||
|
return false, fmt.Errorf("did not hit any dirent during iteration %s", path)
|
||||||
|
}
|
||||||
|
return supportsDType, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func iterateReadDir(path string, fn func(*syscall.Dirent) bool) error {
|
||||||
|
d, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer d.Close()
|
||||||
|
fd := int(d.Fd())
|
||||||
|
buf := make([]byte, 4096)
|
||||||
|
for {
|
||||||
|
nbytes, err := syscall.ReadDirent(fd, buf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if nbytes == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
for off := 0; off < nbytes; {
|
||||||
|
ent := (*syscall.Dirent)(unsafe.Pointer(&buf[off]))
|
||||||
|
if stop := fn(ent); stop {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
off += int(ent.Reclen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
38
vendor/github.com/containerd/continuity/fs/du.go
generated
vendored
Normal file
38
vendor/github.com/containerd/continuity/fs/du.go
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package fs
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
// Usage of disk information
|
||||||
|
type Usage struct {
|
||||||
|
Inodes int64
|
||||||
|
Size int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiskUsage counts the number of inodes and disk usage for the resources under
|
||||||
|
// path.
|
||||||
|
func DiskUsage(ctx context.Context, roots ...string) (Usage, error) {
|
||||||
|
return diskUsage(ctx, roots...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiffUsage counts the numbers of inodes and disk usage in the
|
||||||
|
// diff between the 2 directories. The first path is intended
|
||||||
|
// as the base directory and the second as the changed directory.
|
||||||
|
func DiffUsage(ctx context.Context, a, b string) (Usage, error) {
|
||||||
|
return diffUsage(ctx, a, b)
|
||||||
|
}
|
110
vendor/github.com/containerd/continuity/fs/du_unix.go
generated
vendored
Normal file
110
vendor/github.com/containerd/continuity/fs/du_unix.go
generated
vendored
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
// +build !windows
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
type inode struct {
|
||||||
|
// TODO(stevvooe): Can probably reduce memory usage by not tracking
|
||||||
|
// device, but we can leave this right for now.
|
||||||
|
dev, ino uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func newInode(stat *syscall.Stat_t) inode {
|
||||||
|
return inode{
|
||||||
|
// Dev is uint32 on darwin/bsd, uint64 on linux/solaris
|
||||||
|
dev: uint64(stat.Dev), // nolint: unconvert
|
||||||
|
// Ino is uint32 on bsd, uint64 on darwin/linux/solaris
|
||||||
|
ino: uint64(stat.Ino), // nolint: unconvert
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func diskUsage(ctx context.Context, roots ...string) (Usage, error) {
|
||||||
|
|
||||||
|
var (
|
||||||
|
size int64
|
||||||
|
inodes = map[inode]struct{}{} // expensive!
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, root := range roots {
|
||||||
|
if err := filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
inoKey := newInode(fi.Sys().(*syscall.Stat_t))
|
||||||
|
if _, ok := inodes[inoKey]; !ok {
|
||||||
|
inodes[inoKey] = struct{}{}
|
||||||
|
size += fi.Size()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return Usage{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Usage{
|
||||||
|
Inodes: int64(len(inodes)),
|
||||||
|
Size: size,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func diffUsage(ctx context.Context, a, b string) (Usage, error) {
|
||||||
|
var (
|
||||||
|
size int64
|
||||||
|
inodes = map[inode]struct{}{} // expensive!
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := Changes(ctx, a, b, func(kind ChangeKind, _ string, fi os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if kind == ChangeKindAdd || kind == ChangeKindModify {
|
||||||
|
inoKey := newInode(fi.Sys().(*syscall.Stat_t))
|
||||||
|
if _, ok := inodes[inoKey]; !ok {
|
||||||
|
inodes[inoKey] = struct{}{}
|
||||||
|
size += fi.Size()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return Usage{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return Usage{
|
||||||
|
Inodes: int64(len(inodes)),
|
||||||
|
Size: size,
|
||||||
|
}, nil
|
||||||
|
}
|
82
vendor/github.com/containerd/continuity/fs/du_windows.go
generated
vendored
Normal file
82
vendor/github.com/containerd/continuity/fs/du_windows.go
generated
vendored
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
func diskUsage(ctx context.Context, roots ...string) (Usage, error) {
|
||||||
|
var (
|
||||||
|
size int64
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO(stevvooe): Support inodes (or equivalent) for windows.
|
||||||
|
|
||||||
|
for _, root := range roots {
|
||||||
|
if err := filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
size += fi.Size()
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return Usage{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Usage{
|
||||||
|
Size: size,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func diffUsage(ctx context.Context, a, b string) (Usage, error) {
|
||||||
|
var (
|
||||||
|
size int64
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := Changes(ctx, a, b, func(kind ChangeKind, _ string, fi os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if kind == ChangeKindAdd || kind == ChangeKindModify {
|
||||||
|
size += fi.Size()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return Usage{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return Usage{
|
||||||
|
Size: size,
|
||||||
|
}, nil
|
||||||
|
}
|
43
vendor/github.com/containerd/continuity/fs/hardlink.go
generated
vendored
Normal file
43
vendor/github.com/containerd/continuity/fs/hardlink.go
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package fs
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
// GetLinkInfo returns an identifier representing the node a hardlink is pointing
|
||||||
|
// to. If the file is not hard linked then 0 will be returned.
|
||||||
|
func GetLinkInfo(fi os.FileInfo) (uint64, bool) {
|
||||||
|
return getLinkInfo(fi)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getLinkSource returns a path for the given name and
|
||||||
|
// file info to its link source in the provided inode
|
||||||
|
// map. If the given file name is not in the map and
|
||||||
|
// has other links, it is added to the inode map
|
||||||
|
// to be a source for other link locations.
|
||||||
|
func getLinkSource(name string, fi os.FileInfo, inodes map[uint64]string) (string, error) {
|
||||||
|
inode, isHardlink := getLinkInfo(fi)
|
||||||
|
if !isHardlink {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
path, ok := inodes[inode]
|
||||||
|
if !ok {
|
||||||
|
inodes[inode] = name
|
||||||
|
}
|
||||||
|
return path, nil
|
||||||
|
}
|
34
vendor/github.com/containerd/continuity/fs/hardlink_unix.go
generated
vendored
Normal file
34
vendor/github.com/containerd/continuity/fs/hardlink_unix.go
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// +build !windows
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getLinkInfo(fi os.FileInfo) (uint64, bool) {
|
||||||
|
s, ok := fi.Sys().(*syscall.Stat_t)
|
||||||
|
if !ok {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ino is uint32 on bsd, uint64 on darwin/linux/solaris
|
||||||
|
return uint64(s.Ino), !fi.IsDir() && s.Nlink > 1 // nolint: unconvert
|
||||||
|
}
|
23
vendor/github.com/containerd/continuity/fs/hardlink_windows.go
generated
vendored
Normal file
23
vendor/github.com/containerd/continuity/fs/hardlink_windows.go
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package fs
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
func getLinkInfo(fi os.FileInfo) (uint64, bool) {
|
||||||
|
return 0, false
|
||||||
|
}
|
313
vendor/github.com/containerd/continuity/fs/path.go
generated
vendored
Normal file
313
vendor/github.com/containerd/continuity/fs/path.go
generated
vendored
Normal file
@ -0,0 +1,313 @@
|
|||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errTooManyLinks = errors.New("too many links")
|
||||||
|
)
|
||||||
|
|
||||||
|
type currentPath struct {
|
||||||
|
path string
|
||||||
|
f os.FileInfo
|
||||||
|
fullPath string
|
||||||
|
}
|
||||||
|
|
||||||
|
func pathChange(lower, upper *currentPath) (ChangeKind, string) {
|
||||||
|
if lower == nil {
|
||||||
|
if upper == nil {
|
||||||
|
panic("cannot compare nil paths")
|
||||||
|
}
|
||||||
|
return ChangeKindAdd, upper.path
|
||||||
|
}
|
||||||
|
if upper == nil {
|
||||||
|
return ChangeKindDelete, lower.path
|
||||||
|
}
|
||||||
|
|
||||||
|
switch i := directoryCompare(lower.path, upper.path); {
|
||||||
|
case i < 0:
|
||||||
|
// File in lower that is not in upper
|
||||||
|
return ChangeKindDelete, lower.path
|
||||||
|
case i > 0:
|
||||||
|
// File in upper that is not in lower
|
||||||
|
return ChangeKindAdd, upper.path
|
||||||
|
default:
|
||||||
|
return ChangeKindModify, upper.path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func directoryCompare(a, b string) int {
|
||||||
|
l := len(a)
|
||||||
|
if len(b) < l {
|
||||||
|
l = len(b)
|
||||||
|
}
|
||||||
|
for i := 0; i < l; i++ {
|
||||||
|
c1, c2 := a[i], b[i]
|
||||||
|
if c1 == filepath.Separator {
|
||||||
|
c1 = byte(0)
|
||||||
|
}
|
||||||
|
if c2 == filepath.Separator {
|
||||||
|
c2 = byte(0)
|
||||||
|
}
|
||||||
|
if c1 < c2 {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
if c1 > c2 {
|
||||||
|
return +1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(a) < len(b) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
if len(a) > len(b) {
|
||||||
|
return +1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func sameFile(f1, f2 *currentPath) (bool, error) {
|
||||||
|
if os.SameFile(f1.f, f2.f) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
equalStat, err := compareSysStat(f1.f.Sys(), f2.f.Sys())
|
||||||
|
if err != nil || !equalStat {
|
||||||
|
return equalStat, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if eq, err := compareCapabilities(f1.fullPath, f2.fullPath); err != nil || !eq {
|
||||||
|
return eq, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not a directory also check size, modtime, and content
|
||||||
|
if !f1.f.IsDir() {
|
||||||
|
if f1.f.Size() != f2.f.Size() {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
t1 := f1.f.ModTime()
|
||||||
|
t2 := f2.f.ModTime()
|
||||||
|
|
||||||
|
if t1.Unix() != t2.Unix() {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the timestamp may have been truncated in both of the
|
||||||
|
// files, check content of file to determine difference
|
||||||
|
if t1.Nanosecond() == 0 && t2.Nanosecond() == 0 {
|
||||||
|
var eq bool
|
||||||
|
if (f1.f.Mode() & os.ModeSymlink) == os.ModeSymlink {
|
||||||
|
eq, err = compareSymlinkTarget(f1.fullPath, f2.fullPath)
|
||||||
|
} else if f1.f.Size() > 0 {
|
||||||
|
eq, err = compareFileContent(f1.fullPath, f2.fullPath)
|
||||||
|
}
|
||||||
|
if err != nil || !eq {
|
||||||
|
return eq, err
|
||||||
|
}
|
||||||
|
} else if t1.Nanosecond() != t2.Nanosecond() {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func compareSymlinkTarget(p1, p2 string) (bool, error) {
|
||||||
|
t1, err := os.Readlink(p1)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
t2, err := os.Readlink(p2)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return t1 == t2, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const compareChuckSize = 32 * 1024
|
||||||
|
|
||||||
|
// compareFileContent compares the content of 2 same sized files
|
||||||
|
// by comparing each byte.
|
||||||
|
func compareFileContent(p1, p2 string) (bool, error) {
|
||||||
|
f1, err := os.Open(p1)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
defer f1.Close()
|
||||||
|
f2, err := os.Open(p2)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
defer f2.Close()
|
||||||
|
|
||||||
|
b1 := make([]byte, compareChuckSize)
|
||||||
|
b2 := make([]byte, compareChuckSize)
|
||||||
|
for {
|
||||||
|
n1, err1 := f1.Read(b1)
|
||||||
|
if err1 != nil && err1 != io.EOF {
|
||||||
|
return false, err1
|
||||||
|
}
|
||||||
|
n2, err2 := f2.Read(b2)
|
||||||
|
if err2 != nil && err2 != io.EOF {
|
||||||
|
return false, err2
|
||||||
|
}
|
||||||
|
if n1 != n2 || !bytes.Equal(b1[:n1], b2[:n2]) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
if err1 == io.EOF && err2 == io.EOF {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func pathWalk(ctx context.Context, root string, pathC chan<- *currentPath) error {
|
||||||
|
return filepath.Walk(root, func(path string, f os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rebase path
|
||||||
|
path, err = filepath.Rel(root, path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
path = filepath.Join(string(os.PathSeparator), path)
|
||||||
|
|
||||||
|
// Skip root
|
||||||
|
if path == string(os.PathSeparator) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
p := ¤tPath{
|
||||||
|
path: path,
|
||||||
|
f: f,
|
||||||
|
fullPath: filepath.Join(root, path),
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
case pathC <- p:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func nextPath(ctx context.Context, pathC <-chan *currentPath) (*currentPath, error) {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
case p := <-pathC:
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RootPath joins a path with a root, evaluating and bounding any
|
||||||
|
// symlink to the root directory.
|
||||||
|
func RootPath(root, path string) (string, error) {
|
||||||
|
if path == "" {
|
||||||
|
return root, nil
|
||||||
|
}
|
||||||
|
var linksWalked int // to protect against cycles
|
||||||
|
for {
|
||||||
|
i := linksWalked
|
||||||
|
newpath, err := walkLinks(root, path, &linksWalked)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
path = newpath
|
||||||
|
if i == linksWalked {
|
||||||
|
newpath = filepath.Join("/", newpath)
|
||||||
|
if path == newpath {
|
||||||
|
return filepath.Join(root, newpath), nil
|
||||||
|
}
|
||||||
|
path = newpath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func walkLink(root, path string, linksWalked *int) (newpath string, islink bool, err error) {
|
||||||
|
if *linksWalked > 255 {
|
||||||
|
return "", false, errTooManyLinks
|
||||||
|
}
|
||||||
|
|
||||||
|
path = filepath.Join("/", path)
|
||||||
|
if path == "/" {
|
||||||
|
return path, false, nil
|
||||||
|
}
|
||||||
|
realPath := filepath.Join(root, path)
|
||||||
|
|
||||||
|
fi, err := os.Lstat(realPath)
|
||||||
|
if err != nil {
|
||||||
|
// If path does not yet exist, treat as non-symlink
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return path, false, nil
|
||||||
|
}
|
||||||
|
return "", false, err
|
||||||
|
}
|
||||||
|
if fi.Mode()&os.ModeSymlink == 0 {
|
||||||
|
return path, false, nil
|
||||||
|
}
|
||||||
|
newpath, err = os.Readlink(realPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", false, err
|
||||||
|
}
|
||||||
|
*linksWalked++
|
||||||
|
return newpath, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func walkLinks(root, path string, linksWalked *int) (string, error) {
|
||||||
|
switch dir, file := filepath.Split(path); {
|
||||||
|
case dir == "":
|
||||||
|
newpath, _, err := walkLink(root, file, linksWalked)
|
||||||
|
return newpath, err
|
||||||
|
case file == "":
|
||||||
|
if os.IsPathSeparator(dir[len(dir)-1]) {
|
||||||
|
if dir == "/" {
|
||||||
|
return dir, nil
|
||||||
|
}
|
||||||
|
return walkLinks(root, dir[:len(dir)-1], linksWalked)
|
||||||
|
}
|
||||||
|
newpath, _, err := walkLink(root, dir, linksWalked)
|
||||||
|
return newpath, err
|
||||||
|
default:
|
||||||
|
newdir, err := walkLinks(root, dir, linksWalked)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
newpath, islink, err := walkLink(root, filepath.Join(newdir, file), linksWalked)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if !islink {
|
||||||
|
return newpath, nil
|
||||||
|
}
|
||||||
|
if filepath.IsAbs(newpath) {
|
||||||
|
return newpath, nil
|
||||||
|
}
|
||||||
|
return filepath.Join(newdir, newpath), nil
|
||||||
|
}
|
||||||
|
}
|
44
vendor/github.com/containerd/continuity/fs/stat_bsd.go
generated
vendored
Normal file
44
vendor/github.com/containerd/continuity/fs/stat_bsd.go
generated
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// +build darwin freebsd
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StatAtime returns the access time from a stat struct
|
||||||
|
func StatAtime(st *syscall.Stat_t) syscall.Timespec {
|
||||||
|
return st.Atimespec
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatCtime returns the created time from a stat struct
|
||||||
|
func StatCtime(st *syscall.Stat_t) syscall.Timespec {
|
||||||
|
return st.Ctimespec
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatMtime returns the modified time from a stat struct
|
||||||
|
func StatMtime(st *syscall.Stat_t) syscall.Timespec {
|
||||||
|
return st.Mtimespec
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatATimeAsTime returns the access time as a time.Time
|
||||||
|
func StatATimeAsTime(st *syscall.Stat_t) time.Time {
|
||||||
|
return time.Unix(int64(st.Atimespec.Sec), int64(st.Atimespec.Nsec)) // nolint: unconvert
|
||||||
|
}
|
43
vendor/github.com/containerd/continuity/fs/stat_linux.go
generated
vendored
Normal file
43
vendor/github.com/containerd/continuity/fs/stat_linux.go
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StatAtime returns the Atim
|
||||||
|
func StatAtime(st *syscall.Stat_t) syscall.Timespec {
|
||||||
|
return st.Atim
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatCtime returns the Ctim
|
||||||
|
func StatCtime(st *syscall.Stat_t) syscall.Timespec {
|
||||||
|
return st.Ctim
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatMtime returns the Mtim
|
||||||
|
func StatMtime(st *syscall.Stat_t) syscall.Timespec {
|
||||||
|
return st.Mtim
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatATimeAsTime returns st.Atim as a time.Time
|
||||||
|
func StatATimeAsTime(st *syscall.Stat_t) time.Time {
|
||||||
|
// The int64 conversions ensure the line compiles for 32-bit systems as well.
|
||||||
|
return time.Unix(int64(st.Atim.Sec), int64(st.Atim.Nsec)) // nolint: unconvert
|
||||||
|
}
|
29
vendor/github.com/containerd/continuity/fs/time.go
generated
vendored
Normal file
29
vendor/github.com/containerd/continuity/fs/time.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package fs
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// Gnu tar and the go tar writer don't have sub-second mtime
|
||||||
|
// precision, which is problematic when we apply changes via tar
|
||||||
|
// files, we handle this by comparing for exact times, *or* same
|
||||||
|
// second count and either a or b having exactly 0 nanoseconds
|
||||||
|
func sameFsTime(a, b time.Time) bool {
|
||||||
|
return a == b ||
|
||||||
|
(a.Unix() == b.Unix() &&
|
||||||
|
(a.Nanosecond() == 0 || b.Nanosecond() == 0))
|
||||||
|
}
|
101
vendor/github.com/containerd/continuity/pathdriver/path_driver.go
generated
vendored
Normal file
101
vendor/github.com/containerd/continuity/pathdriver/path_driver.go
generated
vendored
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pathdriver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PathDriver provides all of the path manipulation functions in a common
|
||||||
|
// interface. The context should call these and never use the `filepath`
|
||||||
|
// package or any other package to manipulate paths.
|
||||||
|
type PathDriver interface {
|
||||||
|
Join(paths ...string) string
|
||||||
|
IsAbs(path string) bool
|
||||||
|
Rel(base, target string) (string, error)
|
||||||
|
Base(path string) string
|
||||||
|
Dir(path string) string
|
||||||
|
Clean(path string) string
|
||||||
|
Split(path string) (dir, file string)
|
||||||
|
Separator() byte
|
||||||
|
Abs(path string) (string, error)
|
||||||
|
Walk(string, filepath.WalkFunc) error
|
||||||
|
FromSlash(path string) string
|
||||||
|
ToSlash(path string) string
|
||||||
|
Match(pattern, name string) (matched bool, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// pathDriver is a simple default implementation calls the filepath package.
|
||||||
|
type pathDriver struct{}
|
||||||
|
|
||||||
|
// LocalPathDriver is the exported pathDriver struct for convenience.
|
||||||
|
var LocalPathDriver PathDriver = &pathDriver{}
|
||||||
|
|
||||||
|
func (*pathDriver) Join(paths ...string) string {
|
||||||
|
return filepath.Join(paths...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*pathDriver) IsAbs(path string) bool {
|
||||||
|
return filepath.IsAbs(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*pathDriver) Rel(base, target string) (string, error) {
|
||||||
|
return filepath.Rel(base, target)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*pathDriver) Base(path string) string {
|
||||||
|
return filepath.Base(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*pathDriver) Dir(path string) string {
|
||||||
|
return filepath.Dir(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*pathDriver) Clean(path string) string {
|
||||||
|
return filepath.Clean(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*pathDriver) Split(path string) (dir, file string) {
|
||||||
|
return filepath.Split(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*pathDriver) Separator() byte {
|
||||||
|
return filepath.Separator
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*pathDriver) Abs(path string) (string, error) {
|
||||||
|
return filepath.Abs(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note that filepath.Walk calls os.Stat, so if the context wants to
|
||||||
|
// to call Driver.Stat() for Walk, they need to create a new struct that
|
||||||
|
// overrides this method.
|
||||||
|
func (*pathDriver) Walk(root string, walkFn filepath.WalkFunc) error {
|
||||||
|
return filepath.Walk(root, walkFn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*pathDriver) FromSlash(path string) string {
|
||||||
|
return filepath.FromSlash(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*pathDriver) ToSlash(path string) string {
|
||||||
|
return filepath.ToSlash(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*pathDriver) Match(pattern, name string) (bool, error) {
|
||||||
|
return filepath.Match(pattern, name)
|
||||||
|
}
|
26
vendor/github.com/containerd/continuity/syscallx/syscall_unix.go
generated
vendored
Normal file
26
vendor/github.com/containerd/continuity/syscallx/syscall_unix.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// +build !windows
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package syscallx
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
// Readlink returns the destination of the named symbolic link.
|
||||||
|
func Readlink(path string, buf []byte) (n int, err error) {
|
||||||
|
return syscall.Readlink(path, buf)
|
||||||
|
}
|
112
vendor/github.com/containerd/continuity/syscallx/syscall_windows.go
generated
vendored
Normal file
112
vendor/github.com/containerd/continuity/syscallx/syscall_windows.go
generated
vendored
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package syscallx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
type reparseDataBuffer struct {
|
||||||
|
ReparseTag uint32
|
||||||
|
ReparseDataLength uint16
|
||||||
|
Reserved uint16
|
||||||
|
|
||||||
|
// GenericReparseBuffer
|
||||||
|
reparseBuffer byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type mountPointReparseBuffer struct {
|
||||||
|
SubstituteNameOffset uint16
|
||||||
|
SubstituteNameLength uint16
|
||||||
|
PrintNameOffset uint16
|
||||||
|
PrintNameLength uint16
|
||||||
|
PathBuffer [1]uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type symbolicLinkReparseBuffer struct {
|
||||||
|
SubstituteNameOffset uint16
|
||||||
|
SubstituteNameLength uint16
|
||||||
|
PrintNameOffset uint16
|
||||||
|
PrintNameLength uint16
|
||||||
|
Flags uint32
|
||||||
|
PathBuffer [1]uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
_IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003
|
||||||
|
_SYMLINK_FLAG_RELATIVE = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
// Readlink returns the destination of the named symbolic link.
|
||||||
|
func Readlink(path string, buf []byte) (n int, err error) {
|
||||||
|
fd, err := syscall.CreateFile(syscall.StringToUTF16Ptr(path), syscall.GENERIC_READ, 0, nil, syscall.OPEN_EXISTING,
|
||||||
|
syscall.FILE_FLAG_OPEN_REPARSE_POINT|syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
defer syscall.CloseHandle(fd)
|
||||||
|
|
||||||
|
rdbbuf := make([]byte, syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE)
|
||||||
|
var bytesReturned uint32
|
||||||
|
err = syscall.DeviceIoControl(fd, syscall.FSCTL_GET_REPARSE_POINT, nil, 0, &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rdb := (*reparseDataBuffer)(unsafe.Pointer(&rdbbuf[0]))
|
||||||
|
var s string
|
||||||
|
switch rdb.ReparseTag {
|
||||||
|
case syscall.IO_REPARSE_TAG_SYMLINK:
|
||||||
|
data := (*symbolicLinkReparseBuffer)(unsafe.Pointer(&rdb.reparseBuffer))
|
||||||
|
p := (*[0xffff]uint16)(unsafe.Pointer(&data.PathBuffer[0]))
|
||||||
|
s = syscall.UTF16ToString(p[data.SubstituteNameOffset/2 : (data.SubstituteNameOffset+data.SubstituteNameLength)/2])
|
||||||
|
if data.Flags&_SYMLINK_FLAG_RELATIVE == 0 {
|
||||||
|
if len(s) >= 4 && s[:4] == `\??\` {
|
||||||
|
s = s[4:]
|
||||||
|
switch {
|
||||||
|
case len(s) >= 2 && s[1] == ':': // \??\C:\foo\bar
|
||||||
|
// do nothing
|
||||||
|
case len(s) >= 4 && s[:4] == `UNC\`: // \??\UNC\foo\bar
|
||||||
|
s = `\\` + s[4:]
|
||||||
|
default:
|
||||||
|
// unexpected; do nothing
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// unexpected; do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case _IO_REPARSE_TAG_MOUNT_POINT:
|
||||||
|
data := (*mountPointReparseBuffer)(unsafe.Pointer(&rdb.reparseBuffer))
|
||||||
|
p := (*[0xffff]uint16)(unsafe.Pointer(&data.PathBuffer[0]))
|
||||||
|
s = syscall.UTF16ToString(p[data.SubstituteNameOffset/2 : (data.SubstituteNameOffset+data.SubstituteNameLength)/2])
|
||||||
|
if len(s) >= 4 && s[:4] == `\??\` { // \??\C:\foo\bar
|
||||||
|
if len(s) < 48 || s[:11] != `\??\Volume{` {
|
||||||
|
s = s[4:]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// unexpected; do nothing
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// the path is not a symlink or junction but another type of reparse
|
||||||
|
// point
|
||||||
|
return -1, syscall.ENOENT
|
||||||
|
}
|
||||||
|
n = copy(buf, []byte(s))
|
||||||
|
|
||||||
|
return n, nil
|
||||||
|
}
|
3
vendor/github.com/containerd/continuity/sysx/README.md
generated
vendored
Normal file
3
vendor/github.com/containerd/continuity/sysx/README.md
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
This package is for internal use only. It is intended to only have
|
||||||
|
temporary changes before they are upstreamed to golang.org/x/sys/
|
||||||
|
(a.k.a. https://github.com/golang/sys).
|
128
vendor/github.com/containerd/continuity/sysx/file_posix.go
generated
vendored
Normal file
128
vendor/github.com/containerd/continuity/sysx/file_posix.go
generated
vendored
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sysx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/containerd/continuity/syscallx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Readlink returns the destination of the named symbolic link.
|
||||||
|
// If there is an error, it will be of type *PathError.
|
||||||
|
func Readlink(name string) (string, error) {
|
||||||
|
for len := 128; ; len *= 2 {
|
||||||
|
b := make([]byte, len)
|
||||||
|
n, e := fixCount(syscallx.Readlink(fixLongPath(name), b))
|
||||||
|
if e != nil {
|
||||||
|
return "", &os.PathError{Op: "readlink", Path: name, Err: e}
|
||||||
|
}
|
||||||
|
if n < len {
|
||||||
|
return string(b[0:n]), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Many functions in package syscall return a count of -1 instead of 0.
|
||||||
|
// Using fixCount(call()) instead of call() corrects the count.
|
||||||
|
func fixCount(n int, err error) (int, error) {
|
||||||
|
if n < 0 {
|
||||||
|
n = 0
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// fixLongPath returns the extended-length (\\?\-prefixed) form of
|
||||||
|
// path when needed, in order to avoid the default 260 character file
|
||||||
|
// path limit imposed by Windows. If path is not easily converted to
|
||||||
|
// the extended-length form (for example, if path is a relative path
|
||||||
|
// or contains .. elements), or is short enough, fixLongPath returns
|
||||||
|
// path unmodified.
|
||||||
|
//
|
||||||
|
// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath
|
||||||
|
func fixLongPath(path string) string {
|
||||||
|
// Do nothing (and don't allocate) if the path is "short".
|
||||||
|
// Empirically (at least on the Windows Server 2013 builder),
|
||||||
|
// the kernel is arbitrarily okay with < 248 bytes. That
|
||||||
|
// matches what the docs above say:
|
||||||
|
// "When using an API to create a directory, the specified
|
||||||
|
// path cannot be so long that you cannot append an 8.3 file
|
||||||
|
// name (that is, the directory name cannot exceed MAX_PATH
|
||||||
|
// minus 12)." Since MAX_PATH is 260, 260 - 12 = 248.
|
||||||
|
//
|
||||||
|
// The MSDN docs appear to say that a normal path that is 248 bytes long
|
||||||
|
// will work; empirically the path must be less then 248 bytes long.
|
||||||
|
if len(path) < 248 {
|
||||||
|
// Don't fix. (This is how Go 1.7 and earlier worked,
|
||||||
|
// not automatically generating the \\?\ form)
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
// The extended form begins with \\?\, as in
|
||||||
|
// \\?\c:\windows\foo.txt or \\?\UNC\server\share\foo.txt.
|
||||||
|
// The extended form disables evaluation of . and .. path
|
||||||
|
// elements and disables the interpretation of / as equivalent
|
||||||
|
// to \. The conversion here rewrites / to \ and elides
|
||||||
|
// . elements as well as trailing or duplicate separators. For
|
||||||
|
// simplicity it avoids the conversion entirely for relative
|
||||||
|
// paths or paths containing .. elements. For now,
|
||||||
|
// \\server\share paths are not converted to
|
||||||
|
// \\?\UNC\server\share paths because the rules for doing so
|
||||||
|
// are less well-specified.
|
||||||
|
if len(path) >= 2 && path[:2] == `\\` {
|
||||||
|
// Don't canonicalize UNC paths.
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
if !filepath.IsAbs(path) {
|
||||||
|
// Relative path
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
const prefix = `\\?`
|
||||||
|
|
||||||
|
pathbuf := make([]byte, len(prefix)+len(path)+len(`\`))
|
||||||
|
copy(pathbuf, prefix)
|
||||||
|
n := len(path)
|
||||||
|
r, w := 0, len(prefix)
|
||||||
|
for r < n {
|
||||||
|
switch {
|
||||||
|
case os.IsPathSeparator(path[r]):
|
||||||
|
// empty block
|
||||||
|
r++
|
||||||
|
case path[r] == '.' && (r+1 == n || os.IsPathSeparator(path[r+1])):
|
||||||
|
// /./
|
||||||
|
r++
|
||||||
|
case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || os.IsPathSeparator(path[r+2])):
|
||||||
|
// /../ is currently unhandled
|
||||||
|
return path
|
||||||
|
default:
|
||||||
|
pathbuf[w] = '\\'
|
||||||
|
w++
|
||||||
|
for ; r < n && !os.IsPathSeparator(path[r]); r++ {
|
||||||
|
pathbuf[w] = path[r]
|
||||||
|
w++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// A drive's root directory needs a trailing \
|
||||||
|
if w == len(`\\?\c:`) {
|
||||||
|
pathbuf[w] = '\\'
|
||||||
|
w++
|
||||||
|
}
|
||||||
|
return string(pathbuf[:w])
|
||||||
|
}
|
52
vendor/github.com/containerd/continuity/sysx/generate.sh
generated
vendored
Normal file
52
vendor/github.com/containerd/continuity/sysx/generate.sh
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Copyright The containerd Authors.
|
||||||
|
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
mksyscall="$(go env GOROOT)/src/syscall/mksyscall.pl"
|
||||||
|
|
||||||
|
fix() {
|
||||||
|
sed 's,^package syscall$,package sysx,' \
|
||||||
|
| sed 's,^import "unsafe"$,import (\n\t"syscall"\n\t"unsafe"\n),' \
|
||||||
|
| gofmt -r='BytePtrFromString -> syscall.BytePtrFromString' \
|
||||||
|
| gofmt -r='Syscall6 -> syscall.Syscall6' \
|
||||||
|
| gofmt -r='Syscall -> syscall.Syscall' \
|
||||||
|
| gofmt -r='SYS_GETXATTR -> syscall.SYS_GETXATTR' \
|
||||||
|
| gofmt -r='SYS_LISTXATTR -> syscall.SYS_LISTXATTR' \
|
||||||
|
| gofmt -r='SYS_SETXATTR -> syscall.SYS_SETXATTR' \
|
||||||
|
| gofmt -r='SYS_REMOVEXATTR -> syscall.SYS_REMOVEXATTR' \
|
||||||
|
| gofmt -r='SYS_LGETXATTR -> syscall.SYS_LGETXATTR' \
|
||||||
|
| gofmt -r='SYS_LLISTXATTR -> syscall.SYS_LLISTXATTR' \
|
||||||
|
| gofmt -r='SYS_LSETXATTR -> syscall.SYS_LSETXATTR' \
|
||||||
|
| gofmt -r='SYS_LREMOVEXATTR -> syscall.SYS_LREMOVEXATTR'
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ "$GOARCH" == "" ] || [ "$GOOS" == "" ]; then
|
||||||
|
echo "Must specify \$GOARCH and \$GOOS"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkargs=""
|
||||||
|
|
||||||
|
if [ "$GOARCH" == "386" ] || [ "$GOARCH" == "arm" ]; then
|
||||||
|
mkargs="-l32"
|
||||||
|
fi
|
||||||
|
|
||||||
|
for f in "$@"; do
|
||||||
|
$mksyscall $mkargs "${f}_${GOOS}.go" | fix > "${f}_${GOOS}_${GOARCH}.go"
|
||||||
|
done
|
||||||
|
|
23
vendor/github.com/containerd/continuity/sysx/nodata_linux.go
generated
vendored
Normal file
23
vendor/github.com/containerd/continuity/sysx/nodata_linux.go
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sysx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ENODATA = syscall.ENODATA
|
24
vendor/github.com/containerd/continuity/sysx/nodata_solaris.go
generated
vendored
Normal file
24
vendor/github.com/containerd/continuity/sysx/nodata_solaris.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sysx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This should actually be a set that contains ENOENT and EPERM
|
||||||
|
const ENODATA = syscall.ENOENT
|
25
vendor/github.com/containerd/continuity/sysx/nodata_unix.go
generated
vendored
Normal file
25
vendor/github.com/containerd/continuity/sysx/nodata_unix.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// +build darwin freebsd
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sysx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ENODATA = syscall.ENOATTR
|
125
vendor/github.com/containerd/continuity/sysx/xattr.go
generated
vendored
Normal file
125
vendor/github.com/containerd/continuity/sysx/xattr.go
generated
vendored
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
// +build linux darwin
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sysx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Listxattr calls syscall listxattr and reads all content
|
||||||
|
// and returns a string array
|
||||||
|
func Listxattr(path string) ([]string, error) {
|
||||||
|
return listxattrAll(path, unix.Listxattr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removexattr calls syscall removexattr
|
||||||
|
func Removexattr(path string, attr string) (err error) {
|
||||||
|
return unix.Removexattr(path, attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setxattr calls syscall setxattr
|
||||||
|
func Setxattr(path string, attr string, data []byte, flags int) (err error) {
|
||||||
|
return unix.Setxattr(path, attr, data, flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getxattr calls syscall getxattr
|
||||||
|
func Getxattr(path, attr string) ([]byte, error) {
|
||||||
|
return getxattrAll(path, attr, unix.Getxattr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LListxattr lists xattrs, not following symlinks
|
||||||
|
func LListxattr(path string) ([]string, error) {
|
||||||
|
return listxattrAll(path, unix.Llistxattr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LRemovexattr removes an xattr, not following symlinks
|
||||||
|
func LRemovexattr(path string, attr string) (err error) {
|
||||||
|
return unix.Lremovexattr(path, attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LSetxattr sets an xattr, not following symlinks
|
||||||
|
func LSetxattr(path string, attr string, data []byte, flags int) (err error) {
|
||||||
|
return unix.Lsetxattr(path, attr, data, flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LGetxattr gets an xattr, not following symlinks
|
||||||
|
func LGetxattr(path, attr string) ([]byte, error) {
|
||||||
|
return getxattrAll(path, attr, unix.Lgetxattr)
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultXattrBufferSize = 5
|
||||||
|
|
||||||
|
type listxattrFunc func(path string, dest []byte) (int, error)
|
||||||
|
|
||||||
|
func listxattrAll(path string, listFunc listxattrFunc) ([]string, error) {
|
||||||
|
var p []byte // nil on first execution
|
||||||
|
|
||||||
|
for {
|
||||||
|
n, err := listFunc(path, p) // first call gets buffer size.
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if n > len(p) {
|
||||||
|
p = make([]byte, n)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
p = p[:n]
|
||||||
|
|
||||||
|
ps := bytes.Split(bytes.TrimSuffix(p, []byte{0}), []byte{0})
|
||||||
|
var entries []string
|
||||||
|
for _, p := range ps {
|
||||||
|
s := string(p)
|
||||||
|
if s != "" {
|
||||||
|
entries = append(entries, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type getxattrFunc func(string, string, []byte) (int, error)
|
||||||
|
|
||||||
|
func getxattrAll(path, attr string, getFunc getxattrFunc) ([]byte, error) {
|
||||||
|
p := make([]byte, defaultXattrBufferSize)
|
||||||
|
for {
|
||||||
|
n, err := getFunc(path, attr, p)
|
||||||
|
if err != nil {
|
||||||
|
if errno, ok := err.(syscall.Errno); ok && errno == syscall.ERANGE {
|
||||||
|
p = make([]byte, len(p)*2) // this can't be ideal.
|
||||||
|
continue // try again!
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// realloc to correct size and repeat
|
||||||
|
if n > len(p) {
|
||||||
|
p = make([]byte, n)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return p[:n], nil
|
||||||
|
}
|
||||||
|
}
|
67
vendor/github.com/containerd/continuity/sysx/xattr_unsupported.go
generated
vendored
Normal file
67
vendor/github.com/containerd/continuity/sysx/xattr_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// +build !linux,!darwin
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sysx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
var unsupported = errors.New("extended attributes unsupported on " + runtime.GOOS)
|
||||||
|
|
||||||
|
// Listxattr calls syscall listxattr and reads all content
|
||||||
|
// and returns a string array
|
||||||
|
func Listxattr(path string) ([]string, error) {
|
||||||
|
return []string{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removexattr calls syscall removexattr
|
||||||
|
func Removexattr(path string, attr string) (err error) {
|
||||||
|
return unsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setxattr calls syscall setxattr
|
||||||
|
func Setxattr(path string, attr string, data []byte, flags int) (err error) {
|
||||||
|
return unsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getxattr calls syscall getxattr
|
||||||
|
func Getxattr(path, attr string) ([]byte, error) {
|
||||||
|
return []byte{}, unsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
// LListxattr lists xattrs, not following symlinks
|
||||||
|
func LListxattr(path string) ([]string, error) {
|
||||||
|
return []string{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LRemovexattr removes an xattr, not following symlinks
|
||||||
|
func LRemovexattr(path string, attr string) (err error) {
|
||||||
|
return unsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
// LSetxattr sets an xattr, not following symlinks
|
||||||
|
func LSetxattr(path string, attr string, data []byte, flags int) (err error) {
|
||||||
|
return unsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
// LGetxattr gets an xattr, not following symlinks
|
||||||
|
func LGetxattr(path, attr string) ([]byte, error) {
|
||||||
|
return []byte{}, nil
|
||||||
|
}
|
202
vendor/github.com/docker/distribution/LICENSE
generated
vendored
Normal file
202
vendor/github.com/docker/distribution/LICENSE
generated
vendored
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright {yyyy} {name of copyright owner}
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
247
vendor/github.com/docker/distribution/digestset/set.go
generated
vendored
Normal file
247
vendor/github.com/docker/distribution/digestset/set.go
generated
vendored
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
package digestset
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
digest "github.com/opencontainers/go-digest"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrDigestNotFound is used when a matching digest
|
||||||
|
// could not be found in a set.
|
||||||
|
ErrDigestNotFound = errors.New("digest not found")
|
||||||
|
|
||||||
|
// ErrDigestAmbiguous is used when multiple digests
|
||||||
|
// are found in a set. None of the matching digests
|
||||||
|
// should be considered valid matches.
|
||||||
|
ErrDigestAmbiguous = errors.New("ambiguous digest string")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Set is used to hold a unique set of digests which
|
||||||
|
// may be easily referenced by easily referenced by a string
|
||||||
|
// representation of the digest as well as short representation.
|
||||||
|
// The uniqueness of the short representation is based on other
|
||||||
|
// digests in the set. If digests are omitted from this set,
|
||||||
|
// collisions in a larger set may not be detected, therefore it
|
||||||
|
// is important to always do short representation lookups on
|
||||||
|
// the complete set of digests. To mitigate collisions, an
|
||||||
|
// appropriately long short code should be used.
|
||||||
|
type Set struct {
|
||||||
|
mutex sync.RWMutex
|
||||||
|
entries digestEntries
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSet creates an empty set of digests
|
||||||
|
// which may have digests added.
|
||||||
|
func NewSet() *Set {
|
||||||
|
return &Set{
|
||||||
|
entries: digestEntries{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkShortMatch checks whether two digests match as either whole
|
||||||
|
// values or short values. This function does not test equality,
|
||||||
|
// rather whether the second value could match against the first
|
||||||
|
// value.
|
||||||
|
func checkShortMatch(alg digest.Algorithm, hex, shortAlg, shortHex string) bool {
|
||||||
|
if len(hex) == len(shortHex) {
|
||||||
|
if hex != shortHex {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(shortAlg) > 0 && string(alg) != shortAlg {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else if !strings.HasPrefix(hex, shortHex) {
|
||||||
|
return false
|
||||||
|
} else if len(shortAlg) > 0 && string(alg) != shortAlg {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lookup looks for a digest matching the given string representation.
|
||||||
|
// If no digests could be found ErrDigestNotFound will be returned
|
||||||
|
// with an empty digest value. If multiple matches are found
|
||||||
|
// ErrDigestAmbiguous will be returned with an empty digest value.
|
||||||
|
func (dst *Set) Lookup(d string) (digest.Digest, error) {
|
||||||
|
dst.mutex.RLock()
|
||||||
|
defer dst.mutex.RUnlock()
|
||||||
|
if len(dst.entries) == 0 {
|
||||||
|
return "", ErrDigestNotFound
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
searchFunc func(int) bool
|
||||||
|
alg digest.Algorithm
|
||||||
|
hex string
|
||||||
|
)
|
||||||
|
dgst, err := digest.Parse(d)
|
||||||
|
if err == digest.ErrDigestInvalidFormat {
|
||||||
|
hex = d
|
||||||
|
searchFunc = func(i int) bool {
|
||||||
|
return dst.entries[i].val >= d
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hex = dgst.Hex()
|
||||||
|
alg = dgst.Algorithm()
|
||||||
|
searchFunc = func(i int) bool {
|
||||||
|
if dst.entries[i].val == hex {
|
||||||
|
return dst.entries[i].alg >= alg
|
||||||
|
}
|
||||||
|
return dst.entries[i].val >= hex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
idx := sort.Search(len(dst.entries), searchFunc)
|
||||||
|
if idx == len(dst.entries) || !checkShortMatch(dst.entries[idx].alg, dst.entries[idx].val, string(alg), hex) {
|
||||||
|
return "", ErrDigestNotFound
|
||||||
|
}
|
||||||
|
if dst.entries[idx].alg == alg && dst.entries[idx].val == hex {
|
||||||
|
return dst.entries[idx].digest, nil
|
||||||
|
}
|
||||||
|
if idx+1 < len(dst.entries) && checkShortMatch(dst.entries[idx+1].alg, dst.entries[idx+1].val, string(alg), hex) {
|
||||||
|
return "", ErrDigestAmbiguous
|
||||||
|
}
|
||||||
|
|
||||||
|
return dst.entries[idx].digest, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds the given digest to the set. An error will be returned
|
||||||
|
// if the given digest is invalid. If the digest already exists in the
|
||||||
|
// set, this operation will be a no-op.
|
||||||
|
func (dst *Set) Add(d digest.Digest) error {
|
||||||
|
if err := d.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dst.mutex.Lock()
|
||||||
|
defer dst.mutex.Unlock()
|
||||||
|
entry := &digestEntry{alg: d.Algorithm(), val: d.Hex(), digest: d}
|
||||||
|
searchFunc := func(i int) bool {
|
||||||
|
if dst.entries[i].val == entry.val {
|
||||||
|
return dst.entries[i].alg >= entry.alg
|
||||||
|
}
|
||||||
|
return dst.entries[i].val >= entry.val
|
||||||
|
}
|
||||||
|
idx := sort.Search(len(dst.entries), searchFunc)
|
||||||
|
if idx == len(dst.entries) {
|
||||||
|
dst.entries = append(dst.entries, entry)
|
||||||
|
return nil
|
||||||
|
} else if dst.entries[idx].digest == d {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
entries := append(dst.entries, nil)
|
||||||
|
copy(entries[idx+1:], entries[idx:len(entries)-1])
|
||||||
|
entries[idx] = entry
|
||||||
|
dst.entries = entries
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove removes the given digest from the set. An err will be
|
||||||
|
// returned if the given digest is invalid. If the digest does
|
||||||
|
// not exist in the set, this operation will be a no-op.
|
||||||
|
func (dst *Set) Remove(d digest.Digest) error {
|
||||||
|
if err := d.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dst.mutex.Lock()
|
||||||
|
defer dst.mutex.Unlock()
|
||||||
|
entry := &digestEntry{alg: d.Algorithm(), val: d.Hex(), digest: d}
|
||||||
|
searchFunc := func(i int) bool {
|
||||||
|
if dst.entries[i].val == entry.val {
|
||||||
|
return dst.entries[i].alg >= entry.alg
|
||||||
|
}
|
||||||
|
return dst.entries[i].val >= entry.val
|
||||||
|
}
|
||||||
|
idx := sort.Search(len(dst.entries), searchFunc)
|
||||||
|
// Not found if idx is after or value at idx is not digest
|
||||||
|
if idx == len(dst.entries) || dst.entries[idx].digest != d {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
entries := dst.entries
|
||||||
|
copy(entries[idx:], entries[idx+1:])
|
||||||
|
entries = entries[:len(entries)-1]
|
||||||
|
dst.entries = entries
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// All returns all the digests in the set
|
||||||
|
func (dst *Set) All() []digest.Digest {
|
||||||
|
dst.mutex.RLock()
|
||||||
|
defer dst.mutex.RUnlock()
|
||||||
|
retValues := make([]digest.Digest, len(dst.entries))
|
||||||
|
for i := range dst.entries {
|
||||||
|
retValues[i] = dst.entries[i].digest
|
||||||
|
}
|
||||||
|
|
||||||
|
return retValues
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShortCodeTable returns a map of Digest to unique short codes. The
|
||||||
|
// length represents the minimum value, the maximum length may be the
|
||||||
|
// entire value of digest if uniqueness cannot be achieved without the
|
||||||
|
// full value. This function will attempt to make short codes as short
|
||||||
|
// as possible to be unique.
|
||||||
|
func ShortCodeTable(dst *Set, length int) map[digest.Digest]string {
|
||||||
|
dst.mutex.RLock()
|
||||||
|
defer dst.mutex.RUnlock()
|
||||||
|
m := make(map[digest.Digest]string, len(dst.entries))
|
||||||
|
l := length
|
||||||
|
resetIdx := 0
|
||||||
|
for i := 0; i < len(dst.entries); i++ {
|
||||||
|
var short string
|
||||||
|
extended := true
|
||||||
|
for extended {
|
||||||
|
extended = false
|
||||||
|
if len(dst.entries[i].val) <= l {
|
||||||
|
short = dst.entries[i].digest.String()
|
||||||
|
} else {
|
||||||
|
short = dst.entries[i].val[:l]
|
||||||
|
for j := i + 1; j < len(dst.entries); j++ {
|
||||||
|
if checkShortMatch(dst.entries[j].alg, dst.entries[j].val, "", short) {
|
||||||
|
if j > resetIdx {
|
||||||
|
resetIdx = j
|
||||||
|
}
|
||||||
|
extended = true
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if extended {
|
||||||
|
l++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m[dst.entries[i].digest] = short
|
||||||
|
if i >= resetIdx {
|
||||||
|
l = length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
type digestEntry struct {
|
||||||
|
alg digest.Algorithm
|
||||||
|
val string
|
||||||
|
digest digest.Digest
|
||||||
|
}
|
||||||
|
|
||||||
|
type digestEntries []*digestEntry
|
||||||
|
|
||||||
|
func (d digestEntries) Len() int {
|
||||||
|
return len(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d digestEntries) Less(i, j int) bool {
|
||||||
|
if d[i].val != d[j].val {
|
||||||
|
return d[i].val < d[j].val
|
||||||
|
}
|
||||||
|
return d[i].alg < d[j].alg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d digestEntries) Swap(i, j int) {
|
||||||
|
d[i], d[j] = d[j], d[i]
|
||||||
|
}
|
42
vendor/github.com/docker/distribution/reference/helpers.go
generated
vendored
Normal file
42
vendor/github.com/docker/distribution/reference/helpers.go
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package reference
|
||||||
|
|
||||||
|
import "path"
|
||||||
|
|
||||||
|
// IsNameOnly returns true if reference only contains a repo name.
|
||||||
|
func IsNameOnly(ref Named) bool {
|
||||||
|
if _, ok := ref.(NamedTagged); ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if _, ok := ref.(Canonical); ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// FamiliarName returns the familiar name string
|
||||||
|
// for the given named, familiarizing if needed.
|
||||||
|
func FamiliarName(ref Named) string {
|
||||||
|
if nn, ok := ref.(normalizedNamed); ok {
|
||||||
|
return nn.Familiar().Name()
|
||||||
|
}
|
||||||
|
return ref.Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FamiliarString returns the familiar string representation
|
||||||
|
// for the given reference, familiarizing if needed.
|
||||||
|
func FamiliarString(ref Reference) string {
|
||||||
|
if nn, ok := ref.(normalizedNamed); ok {
|
||||||
|
return nn.Familiar().String()
|
||||||
|
}
|
||||||
|
return ref.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FamiliarMatch reports whether ref matches the specified pattern.
|
||||||
|
// See https://godoc.org/path#Match for supported patterns.
|
||||||
|
func FamiliarMatch(pattern string, ref Reference) (bool, error) {
|
||||||
|
matched, err := path.Match(pattern, FamiliarString(ref))
|
||||||
|
if namedRef, isNamed := ref.(Named); isNamed && !matched {
|
||||||
|
matched, _ = path.Match(pattern, FamiliarName(namedRef))
|
||||||
|
}
|
||||||
|
return matched, err
|
||||||
|
}
|
199
vendor/github.com/docker/distribution/reference/normalize.go
generated
vendored
Normal file
199
vendor/github.com/docker/distribution/reference/normalize.go
generated
vendored
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
package reference
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/distribution/digestset"
|
||||||
|
"github.com/opencontainers/go-digest"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
legacyDefaultDomain = "index.docker.io"
|
||||||
|
defaultDomain = "docker.io"
|
||||||
|
officialRepoName = "library"
|
||||||
|
defaultTag = "latest"
|
||||||
|
)
|
||||||
|
|
||||||
|
// normalizedNamed represents a name which has been
|
||||||
|
// normalized and has a familiar form. A familiar name
|
||||||
|
// is what is used in Docker UI. An example normalized
|
||||||
|
// name is "docker.io/library/ubuntu" and corresponding
|
||||||
|
// familiar name of "ubuntu".
|
||||||
|
type normalizedNamed interface {
|
||||||
|
Named
|
||||||
|
Familiar() Named
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseNormalizedNamed parses a string into a named reference
|
||||||
|
// transforming a familiar name from Docker UI to a fully
|
||||||
|
// qualified reference. If the value may be an identifier
|
||||||
|
// use ParseAnyReference.
|
||||||
|
func ParseNormalizedNamed(s string) (Named, error) {
|
||||||
|
if ok := anchoredIdentifierRegexp.MatchString(s); ok {
|
||||||
|
return nil, fmt.Errorf("invalid repository name (%s), cannot specify 64-byte hexadecimal strings", s)
|
||||||
|
}
|
||||||
|
domain, remainder := splitDockerDomain(s)
|
||||||
|
var remoteName string
|
||||||
|
if tagSep := strings.IndexRune(remainder, ':'); tagSep > -1 {
|
||||||
|
remoteName = remainder[:tagSep]
|
||||||
|
} else {
|
||||||
|
remoteName = remainder
|
||||||
|
}
|
||||||
|
if strings.ToLower(remoteName) != remoteName {
|
||||||
|
return nil, errors.New("invalid reference format: repository name must be lowercase")
|
||||||
|
}
|
||||||
|
|
||||||
|
ref, err := Parse(domain + "/" + remainder)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
named, isNamed := ref.(Named)
|
||||||
|
if !isNamed {
|
||||||
|
return nil, fmt.Errorf("reference %s has no name", ref.String())
|
||||||
|
}
|
||||||
|
return named, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseDockerRef normalizes the image reference following the docker convention. This is added
|
||||||
|
// mainly for backward compatibility.
|
||||||
|
// The reference returned can only be either tagged or digested. For reference contains both tag
|
||||||
|
// and digest, the function returns digested reference, e.g. docker.io/library/busybox:latest@
|
||||||
|
// sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa will be returned as
|
||||||
|
// docker.io/library/busybox@sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa.
|
||||||
|
func ParseDockerRef(ref string) (Named, error) {
|
||||||
|
named, err := ParseNormalizedNamed(ref)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if _, ok := named.(NamedTagged); ok {
|
||||||
|
if canonical, ok := named.(Canonical); ok {
|
||||||
|
// The reference is both tagged and digested, only
|
||||||
|
// return digested.
|
||||||
|
newNamed, err := WithName(canonical.Name())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
newCanonical, err := WithDigest(newNamed, canonical.Digest())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return newCanonical, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TagNameOnly(named), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// splitDockerDomain splits a repository name to domain and remotename string.
|
||||||
|
// If no valid domain is found, the default domain is used. Repository name
|
||||||
|
// needs to be already validated before.
|
||||||
|
func splitDockerDomain(name string) (domain, remainder string) {
|
||||||
|
i := strings.IndexRune(name, '/')
|
||||||
|
if i == -1 || (!strings.ContainsAny(name[:i], ".:") && name[:i] != "localhost") {
|
||||||
|
domain, remainder = defaultDomain, name
|
||||||
|
} else {
|
||||||
|
domain, remainder = name[:i], name[i+1:]
|
||||||
|
}
|
||||||
|
if domain == legacyDefaultDomain {
|
||||||
|
domain = defaultDomain
|
||||||
|
}
|
||||||
|
if domain == defaultDomain && !strings.ContainsRune(remainder, '/') {
|
||||||
|
remainder = officialRepoName + "/" + remainder
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// familiarizeName returns a shortened version of the name familiar
|
||||||
|
// to to the Docker UI. Familiar names have the default domain
|
||||||
|
// "docker.io" and "library/" repository prefix removed.
|
||||||
|
// For example, "docker.io/library/redis" will have the familiar
|
||||||
|
// name "redis" and "docker.io/dmcgowan/myapp" will be "dmcgowan/myapp".
|
||||||
|
// Returns a familiarized named only reference.
|
||||||
|
func familiarizeName(named namedRepository) repository {
|
||||||
|
repo := repository{
|
||||||
|
domain: named.Domain(),
|
||||||
|
path: named.Path(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if repo.domain == defaultDomain {
|
||||||
|
repo.domain = ""
|
||||||
|
// Handle official repositories which have the pattern "library/<official repo name>"
|
||||||
|
if split := strings.Split(repo.path, "/"); len(split) == 2 && split[0] == officialRepoName {
|
||||||
|
repo.path = split[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return repo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r reference) Familiar() Named {
|
||||||
|
return reference{
|
||||||
|
namedRepository: familiarizeName(r.namedRepository),
|
||||||
|
tag: r.tag,
|
||||||
|
digest: r.digest,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r repository) Familiar() Named {
|
||||||
|
return familiarizeName(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t taggedReference) Familiar() Named {
|
||||||
|
return taggedReference{
|
||||||
|
namedRepository: familiarizeName(t.namedRepository),
|
||||||
|
tag: t.tag,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c canonicalReference) Familiar() Named {
|
||||||
|
return canonicalReference{
|
||||||
|
namedRepository: familiarizeName(c.namedRepository),
|
||||||
|
digest: c.digest,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TagNameOnly adds the default tag "latest" to a reference if it only has
|
||||||
|
// a repo name.
|
||||||
|
func TagNameOnly(ref Named) Named {
|
||||||
|
if IsNameOnly(ref) {
|
||||||
|
namedTagged, err := WithTag(ref, defaultTag)
|
||||||
|
if err != nil {
|
||||||
|
// Default tag must be valid, to create a NamedTagged
|
||||||
|
// type with non-validated input the WithTag function
|
||||||
|
// should be used instead
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return namedTagged
|
||||||
|
}
|
||||||
|
return ref
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseAnyReference parses a reference string as a possible identifier,
|
||||||
|
// full digest, or familiar name.
|
||||||
|
func ParseAnyReference(ref string) (Reference, error) {
|
||||||
|
if ok := anchoredIdentifierRegexp.MatchString(ref); ok {
|
||||||
|
return digestReference("sha256:" + ref), nil
|
||||||
|
}
|
||||||
|
if dgst, err := digest.Parse(ref); err == nil {
|
||||||
|
return digestReference(dgst), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return ParseNormalizedNamed(ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseAnyReferenceWithSet parses a reference string as a possible short
|
||||||
|
// identifier to be matched in a digest set, a full digest, or familiar name.
|
||||||
|
func ParseAnyReferenceWithSet(ref string, ds *digestset.Set) (Reference, error) {
|
||||||
|
if ok := anchoredShortIdentifierRegexp.MatchString(ref); ok {
|
||||||
|
dgst, err := ds.Lookup(ref)
|
||||||
|
if err == nil {
|
||||||
|
return digestReference(dgst), nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if dgst, err := digest.Parse(ref); err == nil {
|
||||||
|
return digestReference(dgst), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ParseNormalizedNamed(ref)
|
||||||
|
}
|
433
vendor/github.com/docker/distribution/reference/reference.go
generated
vendored
Normal file
433
vendor/github.com/docker/distribution/reference/reference.go
generated
vendored
Normal file
@ -0,0 +1,433 @@
|
|||||||
|
// Package reference provides a general type to represent any way of referencing images within the registry.
|
||||||
|
// Its main purpose is to abstract tags and digests (content-addressable hash).
|
||||||
|
//
|
||||||
|
// Grammar
|
||||||
|
//
|
||||||
|
// reference := name [ ":" tag ] [ "@" digest ]
|
||||||
|
// name := [domain '/'] path-component ['/' path-component]*
|
||||||
|
// domain := domain-component ['.' domain-component]* [':' port-number]
|
||||||
|
// domain-component := /([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])/
|
||||||
|
// port-number := /[0-9]+/
|
||||||
|
// path-component := alpha-numeric [separator alpha-numeric]*
|
||||||
|
// alpha-numeric := /[a-z0-9]+/
|
||||||
|
// separator := /[_.]|__|[-]*/
|
||||||
|
//
|
||||||
|
// tag := /[\w][\w.-]{0,127}/
|
||||||
|
//
|
||||||
|
// digest := digest-algorithm ":" digest-hex
|
||||||
|
// digest-algorithm := digest-algorithm-component [ digest-algorithm-separator digest-algorithm-component ]*
|
||||||
|
// digest-algorithm-separator := /[+.-_]/
|
||||||
|
// digest-algorithm-component := /[A-Za-z][A-Za-z0-9]*/
|
||||||
|
// digest-hex := /[0-9a-fA-F]{32,}/ ; At least 128 bit digest value
|
||||||
|
//
|
||||||
|
// identifier := /[a-f0-9]{64}/
|
||||||
|
// short-identifier := /[a-f0-9]{6,64}/
|
||||||
|
package reference
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/opencontainers/go-digest"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// NameTotalLengthMax is the maximum total number of characters in a repository name.
|
||||||
|
NameTotalLengthMax = 255
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrReferenceInvalidFormat represents an error while trying to parse a string as a reference.
|
||||||
|
ErrReferenceInvalidFormat = errors.New("invalid reference format")
|
||||||
|
|
||||||
|
// ErrTagInvalidFormat represents an error while trying to parse a string as a tag.
|
||||||
|
ErrTagInvalidFormat = errors.New("invalid tag format")
|
||||||
|
|
||||||
|
// ErrDigestInvalidFormat represents an error while trying to parse a string as a tag.
|
||||||
|
ErrDigestInvalidFormat = errors.New("invalid digest format")
|
||||||
|
|
||||||
|
// ErrNameContainsUppercase is returned for invalid repository names that contain uppercase characters.
|
||||||
|
ErrNameContainsUppercase = errors.New("repository name must be lowercase")
|
||||||
|
|
||||||
|
// ErrNameEmpty is returned for empty, invalid repository names.
|
||||||
|
ErrNameEmpty = errors.New("repository name must have at least one component")
|
||||||
|
|
||||||
|
// ErrNameTooLong is returned when a repository name is longer than NameTotalLengthMax.
|
||||||
|
ErrNameTooLong = fmt.Errorf("repository name must not be more than %v characters", NameTotalLengthMax)
|
||||||
|
|
||||||
|
// ErrNameNotCanonical is returned when a name is not canonical.
|
||||||
|
ErrNameNotCanonical = errors.New("repository name must be canonical")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reference is an opaque object reference identifier that may include
|
||||||
|
// modifiers such as a hostname, name, tag, and digest.
|
||||||
|
type Reference interface {
|
||||||
|
// String returns the full reference
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Field provides a wrapper type for resolving correct reference types when
|
||||||
|
// working with encoding.
|
||||||
|
type Field struct {
|
||||||
|
reference Reference
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsField wraps a reference in a Field for encoding.
|
||||||
|
func AsField(reference Reference) Field {
|
||||||
|
return Field{reference}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reference unwraps the reference type from the field to
|
||||||
|
// return the Reference object. This object should be
|
||||||
|
// of the appropriate type to further check for different
|
||||||
|
// reference types.
|
||||||
|
func (f Field) Reference() Reference {
|
||||||
|
return f.reference
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalText serializes the field to byte text which
|
||||||
|
// is the string of the reference.
|
||||||
|
func (f Field) MarshalText() (p []byte, err error) {
|
||||||
|
return []byte(f.reference.String()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalText parses text bytes by invoking the
|
||||||
|
// reference parser to ensure the appropriately
|
||||||
|
// typed reference object is wrapped by field.
|
||||||
|
func (f *Field) UnmarshalText(p []byte) error {
|
||||||
|
r, err := Parse(string(p))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
f.reference = r
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Named is an object with a full name
|
||||||
|
type Named interface {
|
||||||
|
Reference
|
||||||
|
Name() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tagged is an object which has a tag
|
||||||
|
type Tagged interface {
|
||||||
|
Reference
|
||||||
|
Tag() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NamedTagged is an object including a name and tag.
|
||||||
|
type NamedTagged interface {
|
||||||
|
Named
|
||||||
|
Tag() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Digested is an object which has a digest
|
||||||
|
// in which it can be referenced by
|
||||||
|
type Digested interface {
|
||||||
|
Reference
|
||||||
|
Digest() digest.Digest
|
||||||
|
}
|
||||||
|
|
||||||
|
// Canonical reference is an object with a fully unique
|
||||||
|
// name including a name with domain and digest
|
||||||
|
type Canonical interface {
|
||||||
|
Named
|
||||||
|
Digest() digest.Digest
|
||||||
|
}
|
||||||
|
|
||||||
|
// namedRepository is a reference to a repository with a name.
|
||||||
|
// A namedRepository has both domain and path components.
|
||||||
|
type namedRepository interface {
|
||||||
|
Named
|
||||||
|
Domain() string
|
||||||
|
Path() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Domain returns the domain part of the Named reference
|
||||||
|
func Domain(named Named) string {
|
||||||
|
if r, ok := named.(namedRepository); ok {
|
||||||
|
return r.Domain()
|
||||||
|
}
|
||||||
|
domain, _ := splitDomain(named.Name())
|
||||||
|
return domain
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path returns the name without the domain part of the Named reference
|
||||||
|
func Path(named Named) (name string) {
|
||||||
|
if r, ok := named.(namedRepository); ok {
|
||||||
|
return r.Path()
|
||||||
|
}
|
||||||
|
_, path := splitDomain(named.Name())
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitDomain(name string) (string, string) {
|
||||||
|
match := anchoredNameRegexp.FindStringSubmatch(name)
|
||||||
|
if len(match) != 3 {
|
||||||
|
return "", name
|
||||||
|
}
|
||||||
|
return match[1], match[2]
|
||||||
|
}
|
||||||
|
|
||||||
|
// SplitHostname splits a named reference into a
|
||||||
|
// hostname and name string. If no valid hostname is
|
||||||
|
// found, the hostname is empty and the full value
|
||||||
|
// is returned as name
|
||||||
|
// DEPRECATED: Use Domain or Path
|
||||||
|
func SplitHostname(named Named) (string, string) {
|
||||||
|
if r, ok := named.(namedRepository); ok {
|
||||||
|
return r.Domain(), r.Path()
|
||||||
|
}
|
||||||
|
return splitDomain(named.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse parses s and returns a syntactically valid Reference.
|
||||||
|
// If an error was encountered it is returned, along with a nil Reference.
|
||||||
|
// NOTE: Parse will not handle short digests.
|
||||||
|
func Parse(s string) (Reference, error) {
|
||||||
|
matches := ReferenceRegexp.FindStringSubmatch(s)
|
||||||
|
if matches == nil {
|
||||||
|
if s == "" {
|
||||||
|
return nil, ErrNameEmpty
|
||||||
|
}
|
||||||
|
if ReferenceRegexp.FindStringSubmatch(strings.ToLower(s)) != nil {
|
||||||
|
return nil, ErrNameContainsUppercase
|
||||||
|
}
|
||||||
|
return nil, ErrReferenceInvalidFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(matches[1]) > NameTotalLengthMax {
|
||||||
|
return nil, ErrNameTooLong
|
||||||
|
}
|
||||||
|
|
||||||
|
var repo repository
|
||||||
|
|
||||||
|
nameMatch := anchoredNameRegexp.FindStringSubmatch(matches[1])
|
||||||
|
if len(nameMatch) == 3 {
|
||||||
|
repo.domain = nameMatch[1]
|
||||||
|
repo.path = nameMatch[2]
|
||||||
|
} else {
|
||||||
|
repo.domain = ""
|
||||||
|
repo.path = matches[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
ref := reference{
|
||||||
|
namedRepository: repo,
|
||||||
|
tag: matches[2],
|
||||||
|
}
|
||||||
|
if matches[3] != "" {
|
||||||
|
var err error
|
||||||
|
ref.digest, err = digest.Parse(matches[3])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r := getBestReferenceType(ref)
|
||||||
|
if r == nil {
|
||||||
|
return nil, ErrNameEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseNamed parses s and returns a syntactically valid reference implementing
|
||||||
|
// the Named interface. The reference must have a name and be in the canonical
|
||||||
|
// form, otherwise an error is returned.
|
||||||
|
// If an error was encountered it is returned, along with a nil Reference.
|
||||||
|
// NOTE: ParseNamed will not handle short digests.
|
||||||
|
func ParseNamed(s string) (Named, error) {
|
||||||
|
named, err := ParseNormalizedNamed(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if named.String() != s {
|
||||||
|
return nil, ErrNameNotCanonical
|
||||||
|
}
|
||||||
|
return named, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithName returns a named object representing the given string. If the input
|
||||||
|
// is invalid ErrReferenceInvalidFormat will be returned.
|
||||||
|
func WithName(name string) (Named, error) {
|
||||||
|
if len(name) > NameTotalLengthMax {
|
||||||
|
return nil, ErrNameTooLong
|
||||||
|
}
|
||||||
|
|
||||||
|
match := anchoredNameRegexp.FindStringSubmatch(name)
|
||||||
|
if match == nil || len(match) != 3 {
|
||||||
|
return nil, ErrReferenceInvalidFormat
|
||||||
|
}
|
||||||
|
return repository{
|
||||||
|
domain: match[1],
|
||||||
|
path: match[2],
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithTag combines the name from "name" and the tag from "tag" to form a
|
||||||
|
// reference incorporating both the name and the tag.
|
||||||
|
func WithTag(name Named, tag string) (NamedTagged, error) {
|
||||||
|
if !anchoredTagRegexp.MatchString(tag) {
|
||||||
|
return nil, ErrTagInvalidFormat
|
||||||
|
}
|
||||||
|
var repo repository
|
||||||
|
if r, ok := name.(namedRepository); ok {
|
||||||
|
repo.domain = r.Domain()
|
||||||
|
repo.path = r.Path()
|
||||||
|
} else {
|
||||||
|
repo.path = name.Name()
|
||||||
|
}
|
||||||
|
if canonical, ok := name.(Canonical); ok {
|
||||||
|
return reference{
|
||||||
|
namedRepository: repo,
|
||||||
|
tag: tag,
|
||||||
|
digest: canonical.Digest(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return taggedReference{
|
||||||
|
namedRepository: repo,
|
||||||
|
tag: tag,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithDigest combines the name from "name" and the digest from "digest" to form
|
||||||
|
// a reference incorporating both the name and the digest.
|
||||||
|
func WithDigest(name Named, digest digest.Digest) (Canonical, error) {
|
||||||
|
if !anchoredDigestRegexp.MatchString(digest.String()) {
|
||||||
|
return nil, ErrDigestInvalidFormat
|
||||||
|
}
|
||||||
|
var repo repository
|
||||||
|
if r, ok := name.(namedRepository); ok {
|
||||||
|
repo.domain = r.Domain()
|
||||||
|
repo.path = r.Path()
|
||||||
|
} else {
|
||||||
|
repo.path = name.Name()
|
||||||
|
}
|
||||||
|
if tagged, ok := name.(Tagged); ok {
|
||||||
|
return reference{
|
||||||
|
namedRepository: repo,
|
||||||
|
tag: tagged.Tag(),
|
||||||
|
digest: digest,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return canonicalReference{
|
||||||
|
namedRepository: repo,
|
||||||
|
digest: digest,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrimNamed removes any tag or digest from the named reference.
|
||||||
|
func TrimNamed(ref Named) Named {
|
||||||
|
domain, path := SplitHostname(ref)
|
||||||
|
return repository{
|
||||||
|
domain: domain,
|
||||||
|
path: path,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBestReferenceType(ref reference) Reference {
|
||||||
|
if ref.Name() == "" {
|
||||||
|
// Allow digest only references
|
||||||
|
if ref.digest != "" {
|
||||||
|
return digestReference(ref.digest)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if ref.tag == "" {
|
||||||
|
if ref.digest != "" {
|
||||||
|
return canonicalReference{
|
||||||
|
namedRepository: ref.namedRepository,
|
||||||
|
digest: ref.digest,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ref.namedRepository
|
||||||
|
}
|
||||||
|
if ref.digest == "" {
|
||||||
|
return taggedReference{
|
||||||
|
namedRepository: ref.namedRepository,
|
||||||
|
tag: ref.tag,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ref
|
||||||
|
}
|
||||||
|
|
||||||
|
type reference struct {
|
||||||
|
namedRepository
|
||||||
|
tag string
|
||||||
|
digest digest.Digest
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r reference) String() string {
|
||||||
|
return r.Name() + ":" + r.tag + "@" + r.digest.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r reference) Tag() string {
|
||||||
|
return r.tag
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r reference) Digest() digest.Digest {
|
||||||
|
return r.digest
|
||||||
|
}
|
||||||
|
|
||||||
|
type repository struct {
|
||||||
|
domain string
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r repository) String() string {
|
||||||
|
return r.Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r repository) Name() string {
|
||||||
|
if r.domain == "" {
|
||||||
|
return r.path
|
||||||
|
}
|
||||||
|
return r.domain + "/" + r.path
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r repository) Domain() string {
|
||||||
|
return r.domain
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r repository) Path() string {
|
||||||
|
return r.path
|
||||||
|
}
|
||||||
|
|
||||||
|
type digestReference digest.Digest
|
||||||
|
|
||||||
|
func (d digestReference) String() string {
|
||||||
|
return digest.Digest(d).String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d digestReference) Digest() digest.Digest {
|
||||||
|
return digest.Digest(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
type taggedReference struct {
|
||||||
|
namedRepository
|
||||||
|
tag string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t taggedReference) String() string {
|
||||||
|
return t.Name() + ":" + t.tag
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t taggedReference) Tag() string {
|
||||||
|
return t.tag
|
||||||
|
}
|
||||||
|
|
||||||
|
type canonicalReference struct {
|
||||||
|
namedRepository
|
||||||
|
digest digest.Digest
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c canonicalReference) String() string {
|
||||||
|
return c.Name() + "@" + c.digest.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c canonicalReference) Digest() digest.Digest {
|
||||||
|
return c.digest
|
||||||
|
}
|
143
vendor/github.com/docker/distribution/reference/regexp.go
generated
vendored
Normal file
143
vendor/github.com/docker/distribution/reference/regexp.go
generated
vendored
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
package reference
|
||||||
|
|
||||||
|
import "regexp"
|
||||||
|
|
||||||
|
var (
|
||||||
|
// alphaNumericRegexp defines the alpha numeric atom, typically a
|
||||||
|
// component of names. This only allows lower case characters and digits.
|
||||||
|
alphaNumericRegexp = match(`[a-z0-9]+`)
|
||||||
|
|
||||||
|
// separatorRegexp defines the separators allowed to be embedded in name
|
||||||
|
// components. This allow one period, one or two underscore and multiple
|
||||||
|
// dashes.
|
||||||
|
separatorRegexp = match(`(?:[._]|__|[-]*)`)
|
||||||
|
|
||||||
|
// nameComponentRegexp restricts registry path component names to start
|
||||||
|
// with at least one letter or number, with following parts able to be
|
||||||
|
// separated by one period, one or two underscore and multiple dashes.
|
||||||
|
nameComponentRegexp = expression(
|
||||||
|
alphaNumericRegexp,
|
||||||
|
optional(repeated(separatorRegexp, alphaNumericRegexp)))
|
||||||
|
|
||||||
|
// domainComponentRegexp restricts the registry domain component of a
|
||||||
|
// repository name to start with a component as defined by DomainRegexp
|
||||||
|
// and followed by an optional port.
|
||||||
|
domainComponentRegexp = match(`(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`)
|
||||||
|
|
||||||
|
// DomainRegexp defines the structure of potential domain components
|
||||||
|
// that may be part of image names. This is purposely a subset of what is
|
||||||
|
// allowed by DNS to ensure backwards compatibility with Docker image
|
||||||
|
// names.
|
||||||
|
DomainRegexp = expression(
|
||||||
|
domainComponentRegexp,
|
||||||
|
optional(repeated(literal(`.`), domainComponentRegexp)),
|
||||||
|
optional(literal(`:`), match(`[0-9]+`)))
|
||||||
|
|
||||||
|
// TagRegexp matches valid tag names. From docker/docker:graph/tags.go.
|
||||||
|
TagRegexp = match(`[\w][\w.-]{0,127}`)
|
||||||
|
|
||||||
|
// anchoredTagRegexp matches valid tag names, anchored at the start and
|
||||||
|
// end of the matched string.
|
||||||
|
anchoredTagRegexp = anchored(TagRegexp)
|
||||||
|
|
||||||
|
// DigestRegexp matches valid digests.
|
||||||
|
DigestRegexp = match(`[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][[:xdigit:]]{32,}`)
|
||||||
|
|
||||||
|
// anchoredDigestRegexp matches valid digests, anchored at the start and
|
||||||
|
// end of the matched string.
|
||||||
|
anchoredDigestRegexp = anchored(DigestRegexp)
|
||||||
|
|
||||||
|
// NameRegexp is the format for the name component of references. The
|
||||||
|
// regexp has capturing groups for the domain and name part omitting
|
||||||
|
// the separating forward slash from either.
|
||||||
|
NameRegexp = expression(
|
||||||
|
optional(DomainRegexp, literal(`/`)),
|
||||||
|
nameComponentRegexp,
|
||||||
|
optional(repeated(literal(`/`), nameComponentRegexp)))
|
||||||
|
|
||||||
|
// anchoredNameRegexp is used to parse a name value, capturing the
|
||||||
|
// domain and trailing components.
|
||||||
|
anchoredNameRegexp = anchored(
|
||||||
|
optional(capture(DomainRegexp), literal(`/`)),
|
||||||
|
capture(nameComponentRegexp,
|
||||||
|
optional(repeated(literal(`/`), nameComponentRegexp))))
|
||||||
|
|
||||||
|
// ReferenceRegexp is the full supported format of a reference. The regexp
|
||||||
|
// is anchored and has capturing groups for name, tag, and digest
|
||||||
|
// components.
|
||||||
|
ReferenceRegexp = anchored(capture(NameRegexp),
|
||||||
|
optional(literal(":"), capture(TagRegexp)),
|
||||||
|
optional(literal("@"), capture(DigestRegexp)))
|
||||||
|
|
||||||
|
// IdentifierRegexp is the format for string identifier used as a
|
||||||
|
// content addressable identifier using sha256. These identifiers
|
||||||
|
// are like digests without the algorithm, since sha256 is used.
|
||||||
|
IdentifierRegexp = match(`([a-f0-9]{64})`)
|
||||||
|
|
||||||
|
// ShortIdentifierRegexp is the format used to represent a prefix
|
||||||
|
// of an identifier. A prefix may be used to match a sha256 identifier
|
||||||
|
// within a list of trusted identifiers.
|
||||||
|
ShortIdentifierRegexp = match(`([a-f0-9]{6,64})`)
|
||||||
|
|
||||||
|
// anchoredIdentifierRegexp is used to check or match an
|
||||||
|
// identifier value, anchored at start and end of string.
|
||||||
|
anchoredIdentifierRegexp = anchored(IdentifierRegexp)
|
||||||
|
|
||||||
|
// anchoredShortIdentifierRegexp is used to check if a value
|
||||||
|
// is a possible identifier prefix, anchored at start and end
|
||||||
|
// of string.
|
||||||
|
anchoredShortIdentifierRegexp = anchored(ShortIdentifierRegexp)
|
||||||
|
)
|
||||||
|
|
||||||
|
// match compiles the string to a regular expression.
|
||||||
|
var match = regexp.MustCompile
|
||||||
|
|
||||||
|
// literal compiles s into a literal regular expression, escaping any regexp
|
||||||
|
// reserved characters.
|
||||||
|
func literal(s string) *regexp.Regexp {
|
||||||
|
re := match(regexp.QuoteMeta(s))
|
||||||
|
|
||||||
|
if _, complete := re.LiteralPrefix(); !complete {
|
||||||
|
panic("must be a literal")
|
||||||
|
}
|
||||||
|
|
||||||
|
return re
|
||||||
|
}
|
||||||
|
|
||||||
|
// expression defines a full expression, where each regular expression must
|
||||||
|
// follow the previous.
|
||||||
|
func expression(res ...*regexp.Regexp) *regexp.Regexp {
|
||||||
|
var s string
|
||||||
|
for _, re := range res {
|
||||||
|
s += re.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return match(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// optional wraps the expression in a non-capturing group and makes the
|
||||||
|
// production optional.
|
||||||
|
func optional(res ...*regexp.Regexp) *regexp.Regexp {
|
||||||
|
return match(group(expression(res...)).String() + `?`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// repeated wraps the regexp in a non-capturing group to get one or more
|
||||||
|
// matches.
|
||||||
|
func repeated(res ...*regexp.Regexp) *regexp.Regexp {
|
||||||
|
return match(group(expression(res...)).String() + `+`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// group wraps the regexp in a non-capturing group.
|
||||||
|
func group(res ...*regexp.Regexp) *regexp.Regexp {
|
||||||
|
return match(`(?:` + expression(res...).String() + `)`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// capture wraps the expression in a capturing group.
|
||||||
|
func capture(res ...*regexp.Regexp) *regexp.Regexp {
|
||||||
|
return match(`(` + expression(res...).String() + `)`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// anchored anchors the regular expression by adding start and end delimiters.
|
||||||
|
func anchored(res ...*regexp.Regexp) *regexp.Regexp {
|
||||||
|
return match(`^` + expression(res...).String() + `$`)
|
||||||
|
}
|
267
vendor/github.com/docker/distribution/registry/api/errcode/errors.go
generated
vendored
Normal file
267
vendor/github.com/docker/distribution/registry/api/errcode/errors.go
generated
vendored
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
package errcode
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrorCoder is the base interface for ErrorCode and Error allowing
|
||||||
|
// users of each to just call ErrorCode to get the real ID of each
|
||||||
|
type ErrorCoder interface {
|
||||||
|
ErrorCode() ErrorCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorCode represents the error type. The errors are serialized via strings
|
||||||
|
// and the integer format may change and should *never* be exported.
|
||||||
|
type ErrorCode int
|
||||||
|
|
||||||
|
var _ error = ErrorCode(0)
|
||||||
|
|
||||||
|
// ErrorCode just returns itself
|
||||||
|
func (ec ErrorCode) ErrorCode() ErrorCode {
|
||||||
|
return ec
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns the ID/Value
|
||||||
|
func (ec ErrorCode) Error() string {
|
||||||
|
// NOTE(stevvooe): Cannot use message here since it may have unpopulated args.
|
||||||
|
return strings.ToLower(strings.Replace(ec.String(), "_", " ", -1))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Descriptor returns the descriptor for the error code.
|
||||||
|
func (ec ErrorCode) Descriptor() ErrorDescriptor {
|
||||||
|
d, ok := errorCodeToDescriptors[ec]
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
return ErrorCodeUnknown.Descriptor()
|
||||||
|
}
|
||||||
|
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the canonical identifier for this error code.
|
||||||
|
func (ec ErrorCode) String() string {
|
||||||
|
return ec.Descriptor().Value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message returned the human-readable error message for this error code.
|
||||||
|
func (ec ErrorCode) Message() string {
|
||||||
|
return ec.Descriptor().Message
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalText encodes the receiver into UTF-8-encoded text and returns the
|
||||||
|
// result.
|
||||||
|
func (ec ErrorCode) MarshalText() (text []byte, err error) {
|
||||||
|
return []byte(ec.String()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalText decodes the form generated by MarshalText.
|
||||||
|
func (ec *ErrorCode) UnmarshalText(text []byte) error {
|
||||||
|
desc, ok := idToDescriptors[string(text)]
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
desc = ErrorCodeUnknown.Descriptor()
|
||||||
|
}
|
||||||
|
|
||||||
|
*ec = desc.Code
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMessage creates a new Error struct based on the passed-in info and
|
||||||
|
// overrides the Message property.
|
||||||
|
func (ec ErrorCode) WithMessage(message string) Error {
|
||||||
|
return Error{
|
||||||
|
Code: ec,
|
||||||
|
Message: message,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithDetail creates a new Error struct based on the passed-in info and
|
||||||
|
// set the Detail property appropriately
|
||||||
|
func (ec ErrorCode) WithDetail(detail interface{}) Error {
|
||||||
|
return Error{
|
||||||
|
Code: ec,
|
||||||
|
Message: ec.Message(),
|
||||||
|
}.WithDetail(detail)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithArgs creates a new Error struct and sets the Args slice
|
||||||
|
func (ec ErrorCode) WithArgs(args ...interface{}) Error {
|
||||||
|
return Error{
|
||||||
|
Code: ec,
|
||||||
|
Message: ec.Message(),
|
||||||
|
}.WithArgs(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error provides a wrapper around ErrorCode with extra Details provided.
|
||||||
|
type Error struct {
|
||||||
|
Code ErrorCode `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Detail interface{} `json:"detail,omitempty"`
|
||||||
|
|
||||||
|
// TODO(duglin): See if we need an "args" property so we can do the
|
||||||
|
// variable substitution right before showing the message to the user
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ error = Error{}
|
||||||
|
|
||||||
|
// ErrorCode returns the ID/Value of this Error
|
||||||
|
func (e Error) ErrorCode() ErrorCode {
|
||||||
|
return e.Code
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns a human readable representation of the error.
|
||||||
|
func (e Error) Error() string {
|
||||||
|
return fmt.Sprintf("%s: %s", e.Code.Error(), e.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithDetail will return a new Error, based on the current one, but with
|
||||||
|
// some Detail info added
|
||||||
|
func (e Error) WithDetail(detail interface{}) Error {
|
||||||
|
return Error{
|
||||||
|
Code: e.Code,
|
||||||
|
Message: e.Message,
|
||||||
|
Detail: detail,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithArgs uses the passed-in list of interface{} as the substitution
|
||||||
|
// variables in the Error's Message string, but returns a new Error
|
||||||
|
func (e Error) WithArgs(args ...interface{}) Error {
|
||||||
|
return Error{
|
||||||
|
Code: e.Code,
|
||||||
|
Message: fmt.Sprintf(e.Code.Message(), args...),
|
||||||
|
Detail: e.Detail,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorDescriptor provides relevant information about a given error code.
|
||||||
|
type ErrorDescriptor struct {
|
||||||
|
// Code is the error code that this descriptor describes.
|
||||||
|
Code ErrorCode
|
||||||
|
|
||||||
|
// Value provides a unique, string key, often captilized with
|
||||||
|
// underscores, to identify the error code. This value is used as the
|
||||||
|
// keyed value when serializing api errors.
|
||||||
|
Value string
|
||||||
|
|
||||||
|
// Message is a short, human readable decription of the error condition
|
||||||
|
// included in API responses.
|
||||||
|
Message string
|
||||||
|
|
||||||
|
// Description provides a complete account of the errors purpose, suitable
|
||||||
|
// for use in documentation.
|
||||||
|
Description string
|
||||||
|
|
||||||
|
// HTTPStatusCode provides the http status code that is associated with
|
||||||
|
// this error condition.
|
||||||
|
HTTPStatusCode int
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseErrorCode returns the value by the string error code.
|
||||||
|
// `ErrorCodeUnknown` will be returned if the error is not known.
|
||||||
|
func ParseErrorCode(value string) ErrorCode {
|
||||||
|
ed, ok := idToDescriptors[value]
|
||||||
|
if ok {
|
||||||
|
return ed.Code
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorCodeUnknown
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errors provides the envelope for multiple errors and a few sugar methods
|
||||||
|
// for use within the application.
|
||||||
|
type Errors []error
|
||||||
|
|
||||||
|
var _ error = Errors{}
|
||||||
|
|
||||||
|
func (errs Errors) Error() string {
|
||||||
|
switch len(errs) {
|
||||||
|
case 0:
|
||||||
|
return "<nil>"
|
||||||
|
case 1:
|
||||||
|
return errs[0].Error()
|
||||||
|
default:
|
||||||
|
msg := "errors:\n"
|
||||||
|
for _, err := range errs {
|
||||||
|
msg += err.Error() + "\n"
|
||||||
|
}
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the current number of errors.
|
||||||
|
func (errs Errors) Len() int {
|
||||||
|
return len(errs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON converts slice of error, ErrorCode or Error into a
|
||||||
|
// slice of Error - then serializes
|
||||||
|
func (errs Errors) MarshalJSON() ([]byte, error) {
|
||||||
|
var tmpErrs struct {
|
||||||
|
Errors []Error `json:"errors,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, daErr := range errs {
|
||||||
|
var err Error
|
||||||
|
|
||||||
|
switch daErr := daErr.(type) {
|
||||||
|
case ErrorCode:
|
||||||
|
err = daErr.WithDetail(nil)
|
||||||
|
case Error:
|
||||||
|
err = daErr
|
||||||
|
default:
|
||||||
|
err = ErrorCodeUnknown.WithDetail(daErr)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the Error struct was setup and they forgot to set the
|
||||||
|
// Message field (meaning its "") then grab it from the ErrCode
|
||||||
|
msg := err.Message
|
||||||
|
if msg == "" {
|
||||||
|
msg = err.Code.Message()
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpErrs.Errors = append(tmpErrs.Errors, Error{
|
||||||
|
Code: err.Code,
|
||||||
|
Message: msg,
|
||||||
|
Detail: err.Detail,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(tmpErrs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON deserializes []Error and then converts it into slice of
|
||||||
|
// Error or ErrorCode
|
||||||
|
func (errs *Errors) UnmarshalJSON(data []byte) error {
|
||||||
|
var tmpErrs struct {
|
||||||
|
Errors []Error
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(data, &tmpErrs); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var newErrs Errors
|
||||||
|
for _, daErr := range tmpErrs.Errors {
|
||||||
|
// If Message is empty or exactly matches the Code's message string
|
||||||
|
// then just use the Code, no need for a full Error struct
|
||||||
|
if daErr.Detail == nil && (daErr.Message == "" || daErr.Message == daErr.Code.Message()) {
|
||||||
|
// Error's w/o details get converted to ErrorCode
|
||||||
|
newErrs = append(newErrs, daErr.Code)
|
||||||
|
} else {
|
||||||
|
// Error's w/ details are untouched
|
||||||
|
newErrs = append(newErrs, Error{
|
||||||
|
Code: daErr.Code,
|
||||||
|
Message: daErr.Message,
|
||||||
|
Detail: daErr.Detail,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*errs = newErrs
|
||||||
|
return nil
|
||||||
|
}
|
40
vendor/github.com/docker/distribution/registry/api/errcode/handler.go
generated
vendored
Normal file
40
vendor/github.com/docker/distribution/registry/api/errcode/handler.go
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package errcode
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ServeJSON attempts to serve the errcode in a JSON envelope. It marshals err
|
||||||
|
// and sets the content-type header to 'application/json'. It will handle
|
||||||
|
// ErrorCoder and Errors, and if necessary will create an envelope.
|
||||||
|
func ServeJSON(w http.ResponseWriter, err error) error {
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
var sc int
|
||||||
|
|
||||||
|
switch errs := err.(type) {
|
||||||
|
case Errors:
|
||||||
|
if len(errs) < 1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if err, ok := errs[0].(ErrorCoder); ok {
|
||||||
|
sc = err.ErrorCode().Descriptor().HTTPStatusCode
|
||||||
|
}
|
||||||
|
case ErrorCoder:
|
||||||
|
sc = errs.ErrorCode().Descriptor().HTTPStatusCode
|
||||||
|
err = Errors{err} // create an envelope.
|
||||||
|
default:
|
||||||
|
// We just have an unhandled error type, so just place in an envelope
|
||||||
|
// and move along.
|
||||||
|
err = Errors{err}
|
||||||
|
}
|
||||||
|
|
||||||
|
if sc == 0 {
|
||||||
|
sc = http.StatusInternalServerError
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(sc)
|
||||||
|
|
||||||
|
return json.NewEncoder(w).Encode(err)
|
||||||
|
}
|
138
vendor/github.com/docker/distribution/registry/api/errcode/register.go
generated
vendored
Normal file
138
vendor/github.com/docker/distribution/registry/api/errcode/register.go
generated
vendored
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
package errcode
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"sort"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errorCodeToDescriptors = map[ErrorCode]ErrorDescriptor{}
|
||||||
|
idToDescriptors = map[string]ErrorDescriptor{}
|
||||||
|
groupToDescriptors = map[string][]ErrorDescriptor{}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrorCodeUnknown is a generic error that can be used as a last
|
||||||
|
// resort if there is no situation-specific error message that can be used
|
||||||
|
ErrorCodeUnknown = Register("errcode", ErrorDescriptor{
|
||||||
|
Value: "UNKNOWN",
|
||||||
|
Message: "unknown error",
|
||||||
|
Description: `Generic error returned when the error does not have an
|
||||||
|
API classification.`,
|
||||||
|
HTTPStatusCode: http.StatusInternalServerError,
|
||||||
|
})
|
||||||
|
|
||||||
|
// ErrorCodeUnsupported is returned when an operation is not supported.
|
||||||
|
ErrorCodeUnsupported = Register("errcode", ErrorDescriptor{
|
||||||
|
Value: "UNSUPPORTED",
|
||||||
|
Message: "The operation is unsupported.",
|
||||||
|
Description: `The operation was unsupported due to a missing
|
||||||
|
implementation or invalid set of parameters.`,
|
||||||
|
HTTPStatusCode: http.StatusMethodNotAllowed,
|
||||||
|
})
|
||||||
|
|
||||||
|
// ErrorCodeUnauthorized is returned if a request requires
|
||||||
|
// authentication.
|
||||||
|
ErrorCodeUnauthorized = Register("errcode", ErrorDescriptor{
|
||||||
|
Value: "UNAUTHORIZED",
|
||||||
|
Message: "authentication required",
|
||||||
|
Description: `The access controller was unable to authenticate
|
||||||
|
the client. Often this will be accompanied by a
|
||||||
|
Www-Authenticate HTTP response header indicating how to
|
||||||
|
authenticate.`,
|
||||||
|
HTTPStatusCode: http.StatusUnauthorized,
|
||||||
|
})
|
||||||
|
|
||||||
|
// ErrorCodeDenied is returned if a client does not have sufficient
|
||||||
|
// permission to perform an action.
|
||||||
|
ErrorCodeDenied = Register("errcode", ErrorDescriptor{
|
||||||
|
Value: "DENIED",
|
||||||
|
Message: "requested access to the resource is denied",
|
||||||
|
Description: `The access controller denied access for the
|
||||||
|
operation on a resource.`,
|
||||||
|
HTTPStatusCode: http.StatusForbidden,
|
||||||
|
})
|
||||||
|
|
||||||
|
// ErrorCodeUnavailable provides a common error to report unavailability
|
||||||
|
// of a service or endpoint.
|
||||||
|
ErrorCodeUnavailable = Register("errcode", ErrorDescriptor{
|
||||||
|
Value: "UNAVAILABLE",
|
||||||
|
Message: "service unavailable",
|
||||||
|
Description: "Returned when a service is not available",
|
||||||
|
HTTPStatusCode: http.StatusServiceUnavailable,
|
||||||
|
})
|
||||||
|
|
||||||
|
// ErrorCodeTooManyRequests is returned if a client attempts too many
|
||||||
|
// times to contact a service endpoint.
|
||||||
|
ErrorCodeTooManyRequests = Register("errcode", ErrorDescriptor{
|
||||||
|
Value: "TOOMANYREQUESTS",
|
||||||
|
Message: "too many requests",
|
||||||
|
Description: `Returned when a client attempts to contact a
|
||||||
|
service too many times`,
|
||||||
|
HTTPStatusCode: http.StatusTooManyRequests,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
var nextCode = 1000
|
||||||
|
var registerLock sync.Mutex
|
||||||
|
|
||||||
|
// Register will make the passed-in error known to the environment and
|
||||||
|
// return a new ErrorCode
|
||||||
|
func Register(group string, descriptor ErrorDescriptor) ErrorCode {
|
||||||
|
registerLock.Lock()
|
||||||
|
defer registerLock.Unlock()
|
||||||
|
|
||||||
|
descriptor.Code = ErrorCode(nextCode)
|
||||||
|
|
||||||
|
if _, ok := idToDescriptors[descriptor.Value]; ok {
|
||||||
|
panic(fmt.Sprintf("ErrorValue %q is already registered", descriptor.Value))
|
||||||
|
}
|
||||||
|
if _, ok := errorCodeToDescriptors[descriptor.Code]; ok {
|
||||||
|
panic(fmt.Sprintf("ErrorCode %v is already registered", descriptor.Code))
|
||||||
|
}
|
||||||
|
|
||||||
|
groupToDescriptors[group] = append(groupToDescriptors[group], descriptor)
|
||||||
|
errorCodeToDescriptors[descriptor.Code] = descriptor
|
||||||
|
idToDescriptors[descriptor.Value] = descriptor
|
||||||
|
|
||||||
|
nextCode++
|
||||||
|
return descriptor.Code
|
||||||
|
}
|
||||||
|
|
||||||
|
type byValue []ErrorDescriptor
|
||||||
|
|
||||||
|
func (a byValue) Len() int { return len(a) }
|
||||||
|
func (a byValue) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||||
|
func (a byValue) Less(i, j int) bool { return a[i].Value < a[j].Value }
|
||||||
|
|
||||||
|
// GetGroupNames returns the list of Error group names that are registered
|
||||||
|
func GetGroupNames() []string {
|
||||||
|
keys := []string{}
|
||||||
|
|
||||||
|
for k := range groupToDescriptors {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetErrorCodeGroup returns the named group of error descriptors
|
||||||
|
func GetErrorCodeGroup(name string) []ErrorDescriptor {
|
||||||
|
desc := groupToDescriptors[name]
|
||||||
|
sort.Sort(byValue(desc))
|
||||||
|
return desc
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetErrorAllDescriptors returns a slice of all ErrorDescriptors that are
|
||||||
|
// registered, irrespective of what group they're in
|
||||||
|
func GetErrorAllDescriptors() []ErrorDescriptor {
|
||||||
|
result := []ErrorDescriptor{}
|
||||||
|
|
||||||
|
for _, group := range GetGroupNames() {
|
||||||
|
result = append(result, GetErrorCodeGroup(group)...)
|
||||||
|
}
|
||||||
|
sort.Sort(byValue(result))
|
||||||
|
return result
|
||||||
|
}
|
2082
vendor/github.com/docker/docker/AUTHORS
generated
vendored
Normal file
2082
vendor/github.com/docker/docker/AUTHORS
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
191
vendor/github.com/docker/docker/LICENSE
generated
vendored
Normal file
191
vendor/github.com/docker/docker/LICENSE
generated
vendored
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
https://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
Copyright 2013-2018 Docker, Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
19
vendor/github.com/docker/docker/NOTICE
generated
vendored
Normal file
19
vendor/github.com/docker/docker/NOTICE
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Docker
|
||||||
|
Copyright 2012-2017 Docker, Inc.
|
||||||
|
|
||||||
|
This product includes software developed at Docker, Inc. (https://www.docker.com).
|
||||||
|
|
||||||
|
This product contains software (https://github.com/kr/pty) developed
|
||||||
|
by Keith Rarick, licensed under the MIT License.
|
||||||
|
|
||||||
|
The following is courtesy of our legal counsel:
|
||||||
|
|
||||||
|
|
||||||
|
Use and transfer of Docker may be subject to certain restrictions by the
|
||||||
|
United States and other governments.
|
||||||
|
It is your responsibility to ensure that your use and/or transfer does not
|
||||||
|
violate applicable laws.
|
||||||
|
|
||||||
|
For more information, please see https://www.bis.doc.gov
|
||||||
|
|
||||||
|
See also https://www.apache.org/dev/crypto.html and/or seek legal counsel.
|
42
vendor/github.com/docker/docker/api/README.md
generated
vendored
Normal file
42
vendor/github.com/docker/docker/api/README.md
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# Working on the Engine API
|
||||||
|
|
||||||
|
The Engine API is an HTTP API used by the command-line client to communicate with the daemon. It can also be used by third-party software to control the daemon.
|
||||||
|
|
||||||
|
It consists of various components in this repository:
|
||||||
|
|
||||||
|
- `api/swagger.yaml` A Swagger definition of the API.
|
||||||
|
- `api/types/` Types shared by both the client and server, representing various objects, options, responses, etc. Most are written manually, but some are automatically generated from the Swagger definition. See [#27919](https://github.com/docker/docker/issues/27919) for progress on this.
|
||||||
|
- `cli/` The command-line client.
|
||||||
|
- `client/` The Go client used by the command-line client. It can also be used by third-party Go programs.
|
||||||
|
- `daemon/` The daemon, which serves the API.
|
||||||
|
|
||||||
|
## Swagger definition
|
||||||
|
|
||||||
|
The API is defined by the [Swagger](http://swagger.io/specification/) definition in `api/swagger.yaml`. This definition can be used to:
|
||||||
|
|
||||||
|
1. Automatically generate documentation.
|
||||||
|
2. Automatically generate the Go server and client. (A work-in-progress.)
|
||||||
|
3. Provide a machine readable version of the API for introspecting what it can do, automatically generating clients for other languages, etc.
|
||||||
|
|
||||||
|
## Updating the API documentation
|
||||||
|
|
||||||
|
The API documentation is generated entirely from `api/swagger.yaml`. If you make updates to the API, edit this file to represent the change in the documentation.
|
||||||
|
|
||||||
|
The file is split into two main sections:
|
||||||
|
|
||||||
|
- `definitions`, which defines re-usable objects used in requests and responses
|
||||||
|
- `paths`, which defines the API endpoints (and some inline objects which don't need to be reusable)
|
||||||
|
|
||||||
|
To make an edit, first look for the endpoint you want to edit under `paths`, then make the required edits. Endpoints may reference reusable objects with `$ref`, which can be found in the `definitions` section.
|
||||||
|
|
||||||
|
There is hopefully enough example material in the file for you to copy a similar pattern from elsewhere in the file (e.g. adding new fields or endpoints), but for the full reference, see the [Swagger specification](https://github.com/docker/docker/issues/27919).
|
||||||
|
|
||||||
|
`swagger.yaml` is validated by `hack/validate/swagger` to ensure it is a valid Swagger definition. This is useful when making edits to ensure you are doing the right thing.
|
||||||
|
|
||||||
|
## Viewing the API documentation
|
||||||
|
|
||||||
|
When you make edits to `swagger.yaml`, you may want to check the generated API documentation to ensure it renders correctly.
|
||||||
|
|
||||||
|
Run `make swagger-docs` and a preview will be running at `http://localhost`. Some of the styling may be incorrect, but you'll be able to ensure that it is generating the correct documentation.
|
||||||
|
|
||||||
|
The production documentation is generated by vendoring `swagger.yaml` into [docker/docker.github.io](https://github.com/docker/docker.github.io).
|
11
vendor/github.com/docker/docker/api/common.go
generated
vendored
Normal file
11
vendor/github.com/docker/docker/api/common.go
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package api // import "github.com/docker/docker/api"
|
||||||
|
|
||||||
|
// Common constants for daemon and client.
|
||||||
|
const (
|
||||||
|
// DefaultVersion of Current REST API
|
||||||
|
DefaultVersion = "1.40"
|
||||||
|
|
||||||
|
// NoBaseImageSpecifier is the symbol used by the FROM
|
||||||
|
// command to specify that no base image is to be used.
|
||||||
|
NoBaseImageSpecifier = "scratch"
|
||||||
|
)
|
6
vendor/github.com/docker/docker/api/common_unix.go
generated
vendored
Normal file
6
vendor/github.com/docker/docker/api/common_unix.go
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package api // import "github.com/docker/docker/api"
|
||||||
|
|
||||||
|
// MinVersion represents Minimum REST API version supported
|
||||||
|
const MinVersion = "1.12"
|
8
vendor/github.com/docker/docker/api/common_windows.go
generated
vendored
Normal file
8
vendor/github.com/docker/docker/api/common_windows.go
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package api // import "github.com/docker/docker/api"
|
||||||
|
|
||||||
|
// MinVersion represents Minimum REST API version supported
|
||||||
|
// Technically the first daemon API version released on Windows is v1.25 in
|
||||||
|
// engine version 1.13. However, some clients are explicitly using downlevel
|
||||||
|
// APIs (e.g. docker-compose v2.1 file format) and that is just too restrictive.
|
||||||
|
// Hence also allowing 1.24 on Windows.
|
||||||
|
const MinVersion string = "1.24"
|
12
vendor/github.com/docker/docker/api/swagger-gen.yaml
generated
vendored
Normal file
12
vendor/github.com/docker/docker/api/swagger-gen.yaml
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
layout:
|
||||||
|
models:
|
||||||
|
- name: definition
|
||||||
|
source: asset:model
|
||||||
|
target: "{{ joinFilePath .Target .ModelPackage }}"
|
||||||
|
file_name: "{{ (snakize (pascalize .Name)) }}.go"
|
||||||
|
operations:
|
||||||
|
- name: handler
|
||||||
|
source: asset:serverOperation
|
||||||
|
target: "{{ joinFilePath .Target .APIPackage .Package }}"
|
||||||
|
file_name: "{{ (snakize (pascalize .Name)) }}.go"
|
10414
vendor/github.com/docker/docker/api/swagger.yaml
generated
vendored
Normal file
10414
vendor/github.com/docker/docker/api/swagger.yaml
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
22
vendor/github.com/docker/docker/api/types/auth.go
generated
vendored
Normal file
22
vendor/github.com/docker/docker/api/types/auth.go
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package types // import "github.com/docker/docker/api/types"
|
||||||
|
|
||||||
|
// AuthConfig contains authorization information for connecting to a Registry
|
||||||
|
type AuthConfig struct {
|
||||||
|
Username string `json:"username,omitempty"`
|
||||||
|
Password string `json:"password,omitempty"`
|
||||||
|
Auth string `json:"auth,omitempty"`
|
||||||
|
|
||||||
|
// Email is an optional value associated with the username.
|
||||||
|
// This field is deprecated and will be removed in a later
|
||||||
|
// version of docker.
|
||||||
|
Email string `json:"email,omitempty"`
|
||||||
|
|
||||||
|
ServerAddress string `json:"serveraddress,omitempty"`
|
||||||
|
|
||||||
|
// IdentityToken is used to authenticate the user and get
|
||||||
|
// an access token for the registry.
|
||||||
|
IdentityToken string `json:"identitytoken,omitempty"`
|
||||||
|
|
||||||
|
// RegistryToken is a bearer token to be sent to a registry
|
||||||
|
RegistryToken string `json:"registrytoken,omitempty"`
|
||||||
|
}
|
23
vendor/github.com/docker/docker/api/types/blkiodev/blkio.go
generated
vendored
Normal file
23
vendor/github.com/docker/docker/api/types/blkiodev/blkio.go
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package blkiodev // import "github.com/docker/docker/api/types/blkiodev"
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// WeightDevice is a structure that holds device:weight pair
|
||||||
|
type WeightDevice struct {
|
||||||
|
Path string
|
||||||
|
Weight uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WeightDevice) String() string {
|
||||||
|
return fmt.Sprintf("%s:%d", w.Path, w.Weight)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ThrottleDevice is a structure that holds device:rate_per_second pair
|
||||||
|
type ThrottleDevice struct {
|
||||||
|
Path string
|
||||||
|
Rate uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *ThrottleDevice) String() string {
|
||||||
|
return fmt.Sprintf("%s:%d", t.Path, t.Rate)
|
||||||
|
}
|
415
vendor/github.com/docker/docker/api/types/client.go
generated
vendored
Normal file
415
vendor/github.com/docker/docker/api/types/client.go
generated
vendored
Normal file
@ -0,0 +1,415 @@
|
|||||||
|
package types // import "github.com/docker/docker/api/types"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types/container"
|
||||||
|
"github.com/docker/docker/api/types/filters"
|
||||||
|
units "github.com/docker/go-units"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CheckpointCreateOptions holds parameters to create a checkpoint from a container
|
||||||
|
type CheckpointCreateOptions struct {
|
||||||
|
CheckpointID string
|
||||||
|
CheckpointDir string
|
||||||
|
Exit bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckpointListOptions holds parameters to list checkpoints for a container
|
||||||
|
type CheckpointListOptions struct {
|
||||||
|
CheckpointDir string
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckpointDeleteOptions holds parameters to delete a checkpoint from a container
|
||||||
|
type CheckpointDeleteOptions struct {
|
||||||
|
CheckpointID string
|
||||||
|
CheckpointDir string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerAttachOptions holds parameters to attach to a container.
|
||||||
|
type ContainerAttachOptions struct {
|
||||||
|
Stream bool
|
||||||
|
Stdin bool
|
||||||
|
Stdout bool
|
||||||
|
Stderr bool
|
||||||
|
DetachKeys string
|
||||||
|
Logs bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerCommitOptions holds parameters to commit changes into a container.
|
||||||
|
type ContainerCommitOptions struct {
|
||||||
|
Reference string
|
||||||
|
Comment string
|
||||||
|
Author string
|
||||||
|
Changes []string
|
||||||
|
Pause bool
|
||||||
|
Config *container.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerExecInspect holds information returned by exec inspect.
|
||||||
|
type ContainerExecInspect struct {
|
||||||
|
ExecID string
|
||||||
|
ContainerID string
|
||||||
|
Running bool
|
||||||
|
ExitCode int
|
||||||
|
Pid int
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerListOptions holds parameters to list containers with.
|
||||||
|
type ContainerListOptions struct {
|
||||||
|
Quiet bool
|
||||||
|
Size bool
|
||||||
|
All bool
|
||||||
|
Latest bool
|
||||||
|
Since string
|
||||||
|
Before string
|
||||||
|
Limit int
|
||||||
|
Filters filters.Args
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerLogsOptions holds parameters to filter logs with.
|
||||||
|
type ContainerLogsOptions struct {
|
||||||
|
ShowStdout bool
|
||||||
|
ShowStderr bool
|
||||||
|
Since string
|
||||||
|
Until string
|
||||||
|
Timestamps bool
|
||||||
|
Follow bool
|
||||||
|
Tail string
|
||||||
|
Details bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerRemoveOptions holds parameters to remove containers.
|
||||||
|
type ContainerRemoveOptions struct {
|
||||||
|
RemoveVolumes bool
|
||||||
|
RemoveLinks bool
|
||||||
|
Force bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerStartOptions holds parameters to start containers.
|
||||||
|
type ContainerStartOptions struct {
|
||||||
|
CheckpointID string
|
||||||
|
CheckpointDir string
|
||||||
|
}
|
||||||
|
|
||||||
|
// CopyToContainerOptions holds information
|
||||||
|
// about files to copy into a container
|
||||||
|
type CopyToContainerOptions struct {
|
||||||
|
AllowOverwriteDirWithFile bool
|
||||||
|
CopyUIDGID bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// EventsOptions holds parameters to filter events with.
|
||||||
|
type EventsOptions struct {
|
||||||
|
Since string
|
||||||
|
Until string
|
||||||
|
Filters filters.Args
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkListOptions holds parameters to filter the list of networks with.
|
||||||
|
type NetworkListOptions struct {
|
||||||
|
Filters filters.Args
|
||||||
|
}
|
||||||
|
|
||||||
|
// HijackedResponse holds connection information for a hijacked request.
|
||||||
|
type HijackedResponse struct {
|
||||||
|
Conn net.Conn
|
||||||
|
Reader *bufio.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the hijacked connection and reader.
|
||||||
|
func (h *HijackedResponse) Close() {
|
||||||
|
h.Conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloseWriter is an interface that implements structs
|
||||||
|
// that close input streams to prevent from writing.
|
||||||
|
type CloseWriter interface {
|
||||||
|
CloseWrite() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloseWrite closes a readWriter for writing.
|
||||||
|
func (h *HijackedResponse) CloseWrite() error {
|
||||||
|
if conn, ok := h.Conn.(CloseWriter); ok {
|
||||||
|
return conn.CloseWrite()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageBuildOptions holds the information
|
||||||
|
// necessary to build images.
|
||||||
|
type ImageBuildOptions struct {
|
||||||
|
Tags []string
|
||||||
|
SuppressOutput bool
|
||||||
|
RemoteContext string
|
||||||
|
NoCache bool
|
||||||
|
Remove bool
|
||||||
|
ForceRemove bool
|
||||||
|
PullParent bool
|
||||||
|
Isolation container.Isolation
|
||||||
|
CPUSetCPUs string
|
||||||
|
CPUSetMems string
|
||||||
|
CPUShares int64
|
||||||
|
CPUQuota int64
|
||||||
|
CPUPeriod int64
|
||||||
|
Memory int64
|
||||||
|
MemorySwap int64
|
||||||
|
CgroupParent string
|
||||||
|
NetworkMode string
|
||||||
|
ShmSize int64
|
||||||
|
Dockerfile string
|
||||||
|
Ulimits []*units.Ulimit
|
||||||
|
// BuildArgs needs to be a *string instead of just a string so that
|
||||||
|
// we can tell the difference between "" (empty string) and no value
|
||||||
|
// at all (nil). See the parsing of buildArgs in
|
||||||
|
// api/server/router/build/build_routes.go for even more info.
|
||||||
|
BuildArgs map[string]*string
|
||||||
|
AuthConfigs map[string]AuthConfig
|
||||||
|
Context io.Reader
|
||||||
|
Labels map[string]string
|
||||||
|
// squash the resulting image's layers to the parent
|
||||||
|
// preserves the original image and creates a new one from the parent with all
|
||||||
|
// the changes applied to a single layer
|
||||||
|
Squash bool
|
||||||
|
// CacheFrom specifies images that are used for matching cache. Images
|
||||||
|
// specified here do not need to have a valid parent chain to match cache.
|
||||||
|
CacheFrom []string
|
||||||
|
SecurityOpt []string
|
||||||
|
ExtraHosts []string // List of extra hosts
|
||||||
|
Target string
|
||||||
|
SessionID string
|
||||||
|
Platform string
|
||||||
|
// Version specifies the version of the unerlying builder to use
|
||||||
|
Version BuilderVersion
|
||||||
|
// BuildID is an optional identifier that can be passed together with the
|
||||||
|
// build request. The same identifier can be used to gracefully cancel the
|
||||||
|
// build with the cancel request.
|
||||||
|
BuildID string
|
||||||
|
// Outputs defines configurations for exporting build results. Only supported
|
||||||
|
// in BuildKit mode
|
||||||
|
Outputs []ImageBuildOutput
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageBuildOutput defines configuration for exporting a build result
|
||||||
|
type ImageBuildOutput struct {
|
||||||
|
Type string
|
||||||
|
Attrs map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuilderVersion sets the version of underlying builder to use
|
||||||
|
type BuilderVersion string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// BuilderV1 is the first generation builder in docker daemon
|
||||||
|
BuilderV1 BuilderVersion = "1"
|
||||||
|
// BuilderBuildKit is builder based on moby/buildkit project
|
||||||
|
BuilderBuildKit = "2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ImageBuildResponse holds information
|
||||||
|
// returned by a server after building
|
||||||
|
// an image.
|
||||||
|
type ImageBuildResponse struct {
|
||||||
|
Body io.ReadCloser
|
||||||
|
OSType string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageCreateOptions holds information to create images.
|
||||||
|
type ImageCreateOptions struct {
|
||||||
|
RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry.
|
||||||
|
Platform string // Platform is the target platform of the image if it needs to be pulled from the registry.
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageImportSource holds source information for ImageImport
|
||||||
|
type ImageImportSource struct {
|
||||||
|
Source io.Reader // Source is the data to send to the server to create this image from. You must set SourceName to "-" to leverage this.
|
||||||
|
SourceName string // SourceName is the name of the image to pull. Set to "-" to leverage the Source attribute.
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageImportOptions holds information to import images from the client host.
|
||||||
|
type ImageImportOptions struct {
|
||||||
|
Tag string // Tag is the name to tag this image with. This attribute is deprecated.
|
||||||
|
Message string // Message is the message to tag the image with
|
||||||
|
Changes []string // Changes are the raw changes to apply to this image
|
||||||
|
Platform string // Platform is the target platform of the image
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageListOptions holds parameters to filter the list of images with.
|
||||||
|
type ImageListOptions struct {
|
||||||
|
All bool
|
||||||
|
Filters filters.Args
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageLoadResponse returns information to the client about a load process.
|
||||||
|
type ImageLoadResponse struct {
|
||||||
|
// Body must be closed to avoid a resource leak
|
||||||
|
Body io.ReadCloser
|
||||||
|
JSON bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImagePullOptions holds information to pull images.
|
||||||
|
type ImagePullOptions struct {
|
||||||
|
All bool
|
||||||
|
RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry
|
||||||
|
PrivilegeFunc RequestPrivilegeFunc
|
||||||
|
Platform string
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestPrivilegeFunc is a function interface that
|
||||||
|
// clients can supply to retry operations after
|
||||||
|
// getting an authorization error.
|
||||||
|
// This function returns the registry authentication
|
||||||
|
// header value in base 64 format, or an error
|
||||||
|
// if the privilege request fails.
|
||||||
|
type RequestPrivilegeFunc func() (string, error)
|
||||||
|
|
||||||
|
//ImagePushOptions holds information to push images.
|
||||||
|
type ImagePushOptions ImagePullOptions
|
||||||
|
|
||||||
|
// ImageRemoveOptions holds parameters to remove images.
|
||||||
|
type ImageRemoveOptions struct {
|
||||||
|
Force bool
|
||||||
|
PruneChildren bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageSearchOptions holds parameters to search images with.
|
||||||
|
type ImageSearchOptions struct {
|
||||||
|
RegistryAuth string
|
||||||
|
PrivilegeFunc RequestPrivilegeFunc
|
||||||
|
Filters filters.Args
|
||||||
|
Limit int
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResizeOptions holds parameters to resize a tty.
|
||||||
|
// It can be used to resize container ttys and
|
||||||
|
// exec process ttys too.
|
||||||
|
type ResizeOptions struct {
|
||||||
|
Height uint
|
||||||
|
Width uint
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeListOptions holds parameters to list nodes with.
|
||||||
|
type NodeListOptions struct {
|
||||||
|
Filters filters.Args
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeRemoveOptions holds parameters to remove nodes with.
|
||||||
|
type NodeRemoveOptions struct {
|
||||||
|
Force bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceCreateOptions contains the options to use when creating a service.
|
||||||
|
type ServiceCreateOptions struct {
|
||||||
|
// EncodedRegistryAuth is the encoded registry authorization credentials to
|
||||||
|
// use when updating the service.
|
||||||
|
//
|
||||||
|
// This field follows the format of the X-Registry-Auth header.
|
||||||
|
EncodedRegistryAuth string
|
||||||
|
|
||||||
|
// QueryRegistry indicates whether the service update requires
|
||||||
|
// contacting a registry. A registry may be contacted to retrieve
|
||||||
|
// the image digest and manifest, which in turn can be used to update
|
||||||
|
// platform or other information about the service.
|
||||||
|
QueryRegistry bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceCreateResponse contains the information returned to a client
|
||||||
|
// on the creation of a new service.
|
||||||
|
type ServiceCreateResponse struct {
|
||||||
|
// ID is the ID of the created service.
|
||||||
|
ID string
|
||||||
|
// Warnings is a set of non-fatal warning messages to pass on to the user.
|
||||||
|
Warnings []string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Values for RegistryAuthFrom in ServiceUpdateOptions
|
||||||
|
const (
|
||||||
|
RegistryAuthFromSpec = "spec"
|
||||||
|
RegistryAuthFromPreviousSpec = "previous-spec"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ServiceUpdateOptions contains the options to be used for updating services.
|
||||||
|
type ServiceUpdateOptions struct {
|
||||||
|
// EncodedRegistryAuth is the encoded registry authorization credentials to
|
||||||
|
// use when updating the service.
|
||||||
|
//
|
||||||
|
// This field follows the format of the X-Registry-Auth header.
|
||||||
|
EncodedRegistryAuth string
|
||||||
|
|
||||||
|
// TODO(stevvooe): Consider moving the version parameter of ServiceUpdate
|
||||||
|
// into this field. While it does open API users up to racy writes, most
|
||||||
|
// users may not need that level of consistency in practice.
|
||||||
|
|
||||||
|
// RegistryAuthFrom specifies where to find the registry authorization
|
||||||
|
// credentials if they are not given in EncodedRegistryAuth. Valid
|
||||||
|
// values are "spec" and "previous-spec".
|
||||||
|
RegistryAuthFrom string
|
||||||
|
|
||||||
|
// Rollback indicates whether a server-side rollback should be
|
||||||
|
// performed. When this is set, the provided spec will be ignored.
|
||||||
|
// The valid values are "previous" and "none". An empty value is the
|
||||||
|
// same as "none".
|
||||||
|
Rollback string
|
||||||
|
|
||||||
|
// QueryRegistry indicates whether the service update requires
|
||||||
|
// contacting a registry. A registry may be contacted to retrieve
|
||||||
|
// the image digest and manifest, which in turn can be used to update
|
||||||
|
// platform or other information about the service.
|
||||||
|
QueryRegistry bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceListOptions holds parameters to list services with.
|
||||||
|
type ServiceListOptions struct {
|
||||||
|
Filters filters.Args
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceInspectOptions holds parameters related to the "service inspect"
|
||||||
|
// operation.
|
||||||
|
type ServiceInspectOptions struct {
|
||||||
|
InsertDefaults bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// TaskListOptions holds parameters to list tasks with.
|
||||||
|
type TaskListOptions struct {
|
||||||
|
Filters filters.Args
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginRemoveOptions holds parameters to remove plugins.
|
||||||
|
type PluginRemoveOptions struct {
|
||||||
|
Force bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginEnableOptions holds parameters to enable plugins.
|
||||||
|
type PluginEnableOptions struct {
|
||||||
|
Timeout int
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginDisableOptions holds parameters to disable plugins.
|
||||||
|
type PluginDisableOptions struct {
|
||||||
|
Force bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginInstallOptions holds parameters to install a plugin.
|
||||||
|
type PluginInstallOptions struct {
|
||||||
|
Disabled bool
|
||||||
|
AcceptAllPermissions bool
|
||||||
|
RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry
|
||||||
|
RemoteRef string // RemoteRef is the plugin name on the registry
|
||||||
|
PrivilegeFunc RequestPrivilegeFunc
|
||||||
|
AcceptPermissionsFunc func(PluginPrivileges) (bool, error)
|
||||||
|
Args []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// SwarmUnlockKeyResponse contains the response for Engine API:
|
||||||
|
// GET /swarm/unlockkey
|
||||||
|
type SwarmUnlockKeyResponse struct {
|
||||||
|
// UnlockKey is the unlock key in ASCII-armored format.
|
||||||
|
UnlockKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginCreateOptions hold all options to plugin create.
|
||||||
|
type PluginCreateOptions struct {
|
||||||
|
RepoName string
|
||||||
|
}
|
64
vendor/github.com/docker/docker/api/types/configs.go
generated
vendored
Normal file
64
vendor/github.com/docker/docker/api/types/configs.go
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
package types // import "github.com/docker/docker/api/types"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/docker/docker/api/types/container"
|
||||||
|
"github.com/docker/docker/api/types/network"
|
||||||
|
)
|
||||||
|
|
||||||
|
// configs holds structs used for internal communication between the
|
||||||
|
// frontend (such as an http server) and the backend (such as the
|
||||||
|
// docker daemon).
|
||||||
|
|
||||||
|
// ContainerCreateConfig is the parameter set to ContainerCreate()
|
||||||
|
type ContainerCreateConfig struct {
|
||||||
|
Name string
|
||||||
|
Config *container.Config
|
||||||
|
HostConfig *container.HostConfig
|
||||||
|
NetworkingConfig *network.NetworkingConfig
|
||||||
|
AdjustCPUShares bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerRmConfig holds arguments for the container remove
|
||||||
|
// operation. This struct is used to tell the backend what operations
|
||||||
|
// to perform.
|
||||||
|
type ContainerRmConfig struct {
|
||||||
|
ForceRemove, RemoveVolume, RemoveLink bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecConfig is a small subset of the Config struct that holds the configuration
|
||||||
|
// for the exec feature of docker.
|
||||||
|
type ExecConfig struct {
|
||||||
|
User string // User that will run the command
|
||||||
|
Privileged bool // Is the container in privileged mode
|
||||||
|
Tty bool // Attach standard streams to a tty.
|
||||||
|
AttachStdin bool // Attach the standard input, makes possible user interaction
|
||||||
|
AttachStderr bool // Attach the standard error
|
||||||
|
AttachStdout bool // Attach the standard output
|
||||||
|
Detach bool // Execute in detach mode
|
||||||
|
DetachKeys string // Escape keys for detach
|
||||||
|
Env []string // Environment variables
|
||||||
|
WorkingDir string // Working directory
|
||||||
|
Cmd []string // Execution commands and args
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginRmConfig holds arguments for plugin remove.
|
||||||
|
type PluginRmConfig struct {
|
||||||
|
ForceRemove bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginEnableConfig holds arguments for plugin enable
|
||||||
|
type PluginEnableConfig struct {
|
||||||
|
Timeout int
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginDisableConfig holds arguments for plugin disable.
|
||||||
|
type PluginDisableConfig struct {
|
||||||
|
ForceDisable bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkListConfig stores the options available for listing networks
|
||||||
|
type NetworkListConfig struct {
|
||||||
|
// TODO(@cpuguy83): naming is hard, this is pulled from what was being used in the router before moving here
|
||||||
|
Detailed bool
|
||||||
|
Verbose bool
|
||||||
|
}
|
69
vendor/github.com/docker/docker/api/types/container/config.go
generated
vendored
Normal file
69
vendor/github.com/docker/docker/api/types/container/config.go
generated
vendored
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package container // import "github.com/docker/docker/api/types/container"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types/strslice"
|
||||||
|
"github.com/docker/go-connections/nat"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MinimumDuration puts a minimum on user configured duration.
|
||||||
|
// This is to prevent API error on time unit. For example, API may
|
||||||
|
// set 3 as healthcheck interval with intention of 3 seconds, but
|
||||||
|
// Docker interprets it as 3 nanoseconds.
|
||||||
|
const MinimumDuration = 1 * time.Millisecond
|
||||||
|
|
||||||
|
// HealthConfig holds configuration settings for the HEALTHCHECK feature.
|
||||||
|
type HealthConfig struct {
|
||||||
|
// Test is the test to perform to check that the container is healthy.
|
||||||
|
// An empty slice means to inherit the default.
|
||||||
|
// The options are:
|
||||||
|
// {} : inherit healthcheck
|
||||||
|
// {"NONE"} : disable healthcheck
|
||||||
|
// {"CMD", args...} : exec arguments directly
|
||||||
|
// {"CMD-SHELL", command} : run command with system's default shell
|
||||||
|
Test []string `json:",omitempty"`
|
||||||
|
|
||||||
|
// Zero means to inherit. Durations are expressed as integer nanoseconds.
|
||||||
|
Interval time.Duration `json:",omitempty"` // Interval is the time to wait between checks.
|
||||||
|
Timeout time.Duration `json:",omitempty"` // Timeout is the time to wait before considering the check to have hung.
|
||||||
|
StartPeriod time.Duration `json:",omitempty"` // The start period for the container to initialize before the retries starts to count down.
|
||||||
|
|
||||||
|
// Retries is the number of consecutive failures needed to consider a container as unhealthy.
|
||||||
|
// Zero means inherit.
|
||||||
|
Retries int `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config contains the configuration data about a container.
|
||||||
|
// It should hold only portable information about the container.
|
||||||
|
// Here, "portable" means "independent from the host we are running on".
|
||||||
|
// Non-portable information *should* appear in HostConfig.
|
||||||
|
// All fields added to this struct must be marked `omitempty` to keep getting
|
||||||
|
// predictable hashes from the old `v1Compatibility` configuration.
|
||||||
|
type Config struct {
|
||||||
|
Hostname string // Hostname
|
||||||
|
Domainname string // Domainname
|
||||||
|
User string // User that will run the command(s) inside the container, also support user:group
|
||||||
|
AttachStdin bool // Attach the standard input, makes possible user interaction
|
||||||
|
AttachStdout bool // Attach the standard output
|
||||||
|
AttachStderr bool // Attach the standard error
|
||||||
|
ExposedPorts nat.PortSet `json:",omitempty"` // List of exposed ports
|
||||||
|
Tty bool // Attach standard streams to a tty, including stdin if it is not closed.
|
||||||
|
OpenStdin bool // Open stdin
|
||||||
|
StdinOnce bool // If true, close stdin after the 1 attached client disconnects.
|
||||||
|
Env []string // List of environment variable to set in the container
|
||||||
|
Cmd strslice.StrSlice // Command to run when starting the container
|
||||||
|
Healthcheck *HealthConfig `json:",omitempty"` // Healthcheck describes how to check the container is healthy
|
||||||
|
ArgsEscaped bool `json:",omitempty"` // True if command is already escaped (meaning treat as a command line) (Windows specific).
|
||||||
|
Image string // Name of the image as it was passed by the operator (e.g. could be symbolic)
|
||||||
|
Volumes map[string]struct{} // List of volumes (mounts) used for the container
|
||||||
|
WorkingDir string // Current directory (PWD) in the command will be launched
|
||||||
|
Entrypoint strslice.StrSlice // Entrypoint to run when starting the container
|
||||||
|
NetworkDisabled bool `json:",omitempty"` // Is network disabled
|
||||||
|
MacAddress string `json:",omitempty"` // Mac Address of the container
|
||||||
|
OnBuild []string // ONBUILD metadata that were defined on the image Dockerfile
|
||||||
|
Labels map[string]string // List of labels set to this container
|
||||||
|
StopSignal string `json:",omitempty"` // Signal to stop a container
|
||||||
|
StopTimeout *int `json:",omitempty"` // Timeout (in seconds) to stop a container
|
||||||
|
Shell strslice.StrSlice `json:",omitempty"` // Shell for shell-form of RUN, CMD, ENTRYPOINT
|
||||||
|
}
|
21
vendor/github.com/docker/docker/api/types/container/container_changes.go
generated
vendored
Normal file
21
vendor/github.com/docker/docker/api/types/container/container_changes.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package container // import "github.com/docker/docker/api/types/container"
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// DO NOT EDIT THIS FILE
|
||||||
|
// This file was generated by `swagger generate operation`
|
||||||
|
//
|
||||||
|
// See hack/generate-swagger-api.sh
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// ContainerChangeResponseItem change item in response to ContainerChanges operation
|
||||||
|
// swagger:model ContainerChangeResponseItem
|
||||||
|
type ContainerChangeResponseItem struct {
|
||||||
|
|
||||||
|
// Kind of change
|
||||||
|
// Required: true
|
||||||
|
Kind uint8 `json:"Kind"`
|
||||||
|
|
||||||
|
// Path to file that has changed
|
||||||
|
// Required: true
|
||||||
|
Path string `json:"Path"`
|
||||||
|
}
|
21
vendor/github.com/docker/docker/api/types/container/container_create.go
generated
vendored
Normal file
21
vendor/github.com/docker/docker/api/types/container/container_create.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package container // import "github.com/docker/docker/api/types/container"
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// DO NOT EDIT THIS FILE
|
||||||
|
// This file was generated by `swagger generate operation`
|
||||||
|
//
|
||||||
|
// See hack/generate-swagger-api.sh
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// ContainerCreateCreatedBody OK response to ContainerCreate operation
|
||||||
|
// swagger:model ContainerCreateCreatedBody
|
||||||
|
type ContainerCreateCreatedBody struct {
|
||||||
|
|
||||||
|
// The ID of the created container
|
||||||
|
// Required: true
|
||||||
|
ID string `json:"Id"`
|
||||||
|
|
||||||
|
// Warnings encountered when creating the container
|
||||||
|
// Required: true
|
||||||
|
Warnings []string `json:"Warnings"`
|
||||||
|
}
|
21
vendor/github.com/docker/docker/api/types/container/container_top.go
generated
vendored
Normal file
21
vendor/github.com/docker/docker/api/types/container/container_top.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package container // import "github.com/docker/docker/api/types/container"
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// DO NOT EDIT THIS FILE
|
||||||
|
// This file was generated by `swagger generate operation`
|
||||||
|
//
|
||||||
|
// See hack/generate-swagger-api.sh
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// ContainerTopOKBody OK response to ContainerTop operation
|
||||||
|
// swagger:model ContainerTopOKBody
|
||||||
|
type ContainerTopOKBody struct {
|
||||||
|
|
||||||
|
// Each process running in the container, where each is process is an array of values corresponding to the titles
|
||||||
|
// Required: true
|
||||||
|
Processes [][]string `json:"Processes"`
|
||||||
|
|
||||||
|
// The ps column titles
|
||||||
|
// Required: true
|
||||||
|
Titles []string `json:"Titles"`
|
||||||
|
}
|
17
vendor/github.com/docker/docker/api/types/container/container_update.go
generated
vendored
Normal file
17
vendor/github.com/docker/docker/api/types/container/container_update.go
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package container // import "github.com/docker/docker/api/types/container"
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// DO NOT EDIT THIS FILE
|
||||||
|
// This file was generated by `swagger generate operation`
|
||||||
|
//
|
||||||
|
// See hack/generate-swagger-api.sh
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// ContainerUpdateOKBody OK response to ContainerUpdate operation
|
||||||
|
// swagger:model ContainerUpdateOKBody
|
||||||
|
type ContainerUpdateOKBody struct {
|
||||||
|
|
||||||
|
// warnings
|
||||||
|
// Required: true
|
||||||
|
Warnings []string `json:"Warnings"`
|
||||||
|
}
|
29
vendor/github.com/docker/docker/api/types/container/container_wait.go
generated
vendored
Normal file
29
vendor/github.com/docker/docker/api/types/container/container_wait.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package container // import "github.com/docker/docker/api/types/container"
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// DO NOT EDIT THIS FILE
|
||||||
|
// This file was generated by `swagger generate operation`
|
||||||
|
//
|
||||||
|
// See hack/generate-swagger-api.sh
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// ContainerWaitOKBodyError container waiting error, if any
|
||||||
|
// swagger:model ContainerWaitOKBodyError
|
||||||
|
type ContainerWaitOKBodyError struct {
|
||||||
|
|
||||||
|
// Details of an error
|
||||||
|
Message string `json:"Message,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerWaitOKBody OK response to ContainerWait operation
|
||||||
|
// swagger:model ContainerWaitOKBody
|
||||||
|
type ContainerWaitOKBody struct {
|
||||||
|
|
||||||
|
// error
|
||||||
|
// Required: true
|
||||||
|
Error *ContainerWaitOKBodyError `json:"Error"`
|
||||||
|
|
||||||
|
// Exit code of the container
|
||||||
|
// Required: true
|
||||||
|
StatusCode int64 `json:"StatusCode"`
|
||||||
|
}
|
425
vendor/github.com/docker/docker/api/types/container/host_config.go
generated
vendored
Normal file
425
vendor/github.com/docker/docker/api/types/container/host_config.go
generated
vendored
Normal file
@ -0,0 +1,425 @@
|
|||||||
|
package container // import "github.com/docker/docker/api/types/container"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types/blkiodev"
|
||||||
|
"github.com/docker/docker/api/types/mount"
|
||||||
|
"github.com/docker/docker/api/types/strslice"
|
||||||
|
"github.com/docker/go-connections/nat"
|
||||||
|
"github.com/docker/go-units"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Isolation represents the isolation technology of a container. The supported
|
||||||
|
// values are platform specific
|
||||||
|
type Isolation string
|
||||||
|
|
||||||
|
// IsDefault indicates the default isolation technology of a container. On Linux this
|
||||||
|
// is the native driver. On Windows, this is a Windows Server Container.
|
||||||
|
func (i Isolation) IsDefault() bool {
|
||||||
|
return strings.ToLower(string(i)) == "default" || string(i) == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsHyperV indicates the use of a Hyper-V partition for isolation
|
||||||
|
func (i Isolation) IsHyperV() bool {
|
||||||
|
return strings.ToLower(string(i)) == "hyperv"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsProcess indicates the use of process isolation
|
||||||
|
func (i Isolation) IsProcess() bool {
|
||||||
|
return strings.ToLower(string(i)) == "process"
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// IsolationEmpty is unspecified (same behavior as default)
|
||||||
|
IsolationEmpty = Isolation("")
|
||||||
|
// IsolationDefault is the default isolation mode on current daemon
|
||||||
|
IsolationDefault = Isolation("default")
|
||||||
|
// IsolationProcess is process isolation mode
|
||||||
|
IsolationProcess = Isolation("process")
|
||||||
|
// IsolationHyperV is HyperV isolation mode
|
||||||
|
IsolationHyperV = Isolation("hyperv")
|
||||||
|
)
|
||||||
|
|
||||||
|
// IpcMode represents the container ipc stack.
|
||||||
|
type IpcMode string
|
||||||
|
|
||||||
|
// IsPrivate indicates whether the container uses its own private ipc namespace which can not be shared.
|
||||||
|
func (n IpcMode) IsPrivate() bool {
|
||||||
|
return n == "private"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsHost indicates whether the container shares the host's ipc namespace.
|
||||||
|
func (n IpcMode) IsHost() bool {
|
||||||
|
return n == "host"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsShareable indicates whether the container's ipc namespace can be shared with another container.
|
||||||
|
func (n IpcMode) IsShareable() bool {
|
||||||
|
return n == "shareable"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsContainer indicates whether the container uses another container's ipc namespace.
|
||||||
|
func (n IpcMode) IsContainer() bool {
|
||||||
|
parts := strings.SplitN(string(n), ":", 2)
|
||||||
|
return len(parts) > 1 && parts[0] == "container"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNone indicates whether container IpcMode is set to "none".
|
||||||
|
func (n IpcMode) IsNone() bool {
|
||||||
|
return n == "none"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty indicates whether container IpcMode is empty
|
||||||
|
func (n IpcMode) IsEmpty() bool {
|
||||||
|
return n == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid indicates whether the ipc mode is valid.
|
||||||
|
func (n IpcMode) Valid() bool {
|
||||||
|
return n.IsEmpty() || n.IsNone() || n.IsPrivate() || n.IsHost() || n.IsShareable() || n.IsContainer()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Container returns the name of the container ipc stack is going to be used.
|
||||||
|
func (n IpcMode) Container() string {
|
||||||
|
parts := strings.SplitN(string(n), ":", 2)
|
||||||
|
if len(parts) > 1 && parts[0] == "container" {
|
||||||
|
return parts[1]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkMode represents the container network stack.
|
||||||
|
type NetworkMode string
|
||||||
|
|
||||||
|
// IsNone indicates whether container isn't using a network stack.
|
||||||
|
func (n NetworkMode) IsNone() bool {
|
||||||
|
return n == "none"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsDefault indicates whether container uses the default network stack.
|
||||||
|
func (n NetworkMode) IsDefault() bool {
|
||||||
|
return n == "default"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPrivate indicates whether container uses its private network stack.
|
||||||
|
func (n NetworkMode) IsPrivate() bool {
|
||||||
|
return !(n.IsHost() || n.IsContainer())
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsContainer indicates whether container uses a container network stack.
|
||||||
|
func (n NetworkMode) IsContainer() bool {
|
||||||
|
parts := strings.SplitN(string(n), ":", 2)
|
||||||
|
return len(parts) > 1 && parts[0] == "container"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnectedContainer is the id of the container which network this container is connected to.
|
||||||
|
func (n NetworkMode) ConnectedContainer() string {
|
||||||
|
parts := strings.SplitN(string(n), ":", 2)
|
||||||
|
if len(parts) > 1 {
|
||||||
|
return parts[1]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
//UserDefined indicates user-created network
|
||||||
|
func (n NetworkMode) UserDefined() string {
|
||||||
|
if n.IsUserDefined() {
|
||||||
|
return string(n)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// UsernsMode represents userns mode in the container.
|
||||||
|
type UsernsMode string
|
||||||
|
|
||||||
|
// IsHost indicates whether the container uses the host's userns.
|
||||||
|
func (n UsernsMode) IsHost() bool {
|
||||||
|
return n == "host"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPrivate indicates whether the container uses the a private userns.
|
||||||
|
func (n UsernsMode) IsPrivate() bool {
|
||||||
|
return !(n.IsHost())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid indicates whether the userns is valid.
|
||||||
|
func (n UsernsMode) Valid() bool {
|
||||||
|
parts := strings.Split(string(n), ":")
|
||||||
|
switch mode := parts[0]; mode {
|
||||||
|
case "", "host":
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// CgroupSpec represents the cgroup to use for the container.
|
||||||
|
type CgroupSpec string
|
||||||
|
|
||||||
|
// IsContainer indicates whether the container is using another container cgroup
|
||||||
|
func (c CgroupSpec) IsContainer() bool {
|
||||||
|
parts := strings.SplitN(string(c), ":", 2)
|
||||||
|
return len(parts) > 1 && parts[0] == "container"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid indicates whether the cgroup spec is valid.
|
||||||
|
func (c CgroupSpec) Valid() bool {
|
||||||
|
return c.IsContainer() || c == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Container returns the name of the container whose cgroup will be used.
|
||||||
|
func (c CgroupSpec) Container() string {
|
||||||
|
parts := strings.SplitN(string(c), ":", 2)
|
||||||
|
if len(parts) > 1 {
|
||||||
|
return parts[1]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// UTSMode represents the UTS namespace of the container.
|
||||||
|
type UTSMode string
|
||||||
|
|
||||||
|
// IsPrivate indicates whether the container uses its private UTS namespace.
|
||||||
|
func (n UTSMode) IsPrivate() bool {
|
||||||
|
return !(n.IsHost())
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsHost indicates whether the container uses the host's UTS namespace.
|
||||||
|
func (n UTSMode) IsHost() bool {
|
||||||
|
return n == "host"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid indicates whether the UTS namespace is valid.
|
||||||
|
func (n UTSMode) Valid() bool {
|
||||||
|
parts := strings.Split(string(n), ":")
|
||||||
|
switch mode := parts[0]; mode {
|
||||||
|
case "", "host":
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// PidMode represents the pid namespace of the container.
|
||||||
|
type PidMode string
|
||||||
|
|
||||||
|
// IsPrivate indicates whether the container uses its own new pid namespace.
|
||||||
|
func (n PidMode) IsPrivate() bool {
|
||||||
|
return !(n.IsHost() || n.IsContainer())
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsHost indicates whether the container uses the host's pid namespace.
|
||||||
|
func (n PidMode) IsHost() bool {
|
||||||
|
return n == "host"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsContainer indicates whether the container uses a container's pid namespace.
|
||||||
|
func (n PidMode) IsContainer() bool {
|
||||||
|
parts := strings.SplitN(string(n), ":", 2)
|
||||||
|
return len(parts) > 1 && parts[0] == "container"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid indicates whether the pid namespace is valid.
|
||||||
|
func (n PidMode) Valid() bool {
|
||||||
|
parts := strings.Split(string(n), ":")
|
||||||
|
switch mode := parts[0]; mode {
|
||||||
|
case "", "host":
|
||||||
|
case "container":
|
||||||
|
if len(parts) != 2 || parts[1] == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Container returns the name of the container whose pid namespace is going to be used.
|
||||||
|
func (n PidMode) Container() string {
|
||||||
|
parts := strings.SplitN(string(n), ":", 2)
|
||||||
|
if len(parts) > 1 {
|
||||||
|
return parts[1]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeviceRequest represents a request for devices from a device driver.
|
||||||
|
// Used by GPU device drivers.
|
||||||
|
type DeviceRequest struct {
|
||||||
|
Driver string // Name of device driver
|
||||||
|
Count int // Number of devices to request (-1 = All)
|
||||||
|
DeviceIDs []string // List of device IDs as recognizable by the device driver
|
||||||
|
Capabilities [][]string // An OR list of AND lists of device capabilities (e.g. "gpu")
|
||||||
|
Options map[string]string // Options to pass onto the device driver
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeviceMapping represents the device mapping between the host and the container.
|
||||||
|
type DeviceMapping struct {
|
||||||
|
PathOnHost string
|
||||||
|
PathInContainer string
|
||||||
|
CgroupPermissions string
|
||||||
|
}
|
||||||
|
|
||||||
|
// RestartPolicy represents the restart policies of the container.
|
||||||
|
type RestartPolicy struct {
|
||||||
|
Name string
|
||||||
|
MaximumRetryCount int
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNone indicates whether the container has the "no" restart policy.
|
||||||
|
// This means the container will not automatically restart when exiting.
|
||||||
|
func (rp *RestartPolicy) IsNone() bool {
|
||||||
|
return rp.Name == "no" || rp.Name == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAlways indicates whether the container has the "always" restart policy.
|
||||||
|
// This means the container will automatically restart regardless of the exit status.
|
||||||
|
func (rp *RestartPolicy) IsAlways() bool {
|
||||||
|
return rp.Name == "always"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsOnFailure indicates whether the container has the "on-failure" restart policy.
|
||||||
|
// This means the container will automatically restart of exiting with a non-zero exit status.
|
||||||
|
func (rp *RestartPolicy) IsOnFailure() bool {
|
||||||
|
return rp.Name == "on-failure"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUnlessStopped indicates whether the container has the
|
||||||
|
// "unless-stopped" restart policy. This means the container will
|
||||||
|
// automatically restart unless user has put it to stopped state.
|
||||||
|
func (rp *RestartPolicy) IsUnlessStopped() bool {
|
||||||
|
return rp.Name == "unless-stopped"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSame compares two RestartPolicy to see if they are the same
|
||||||
|
func (rp *RestartPolicy) IsSame(tp *RestartPolicy) bool {
|
||||||
|
return rp.Name == tp.Name && rp.MaximumRetryCount == tp.MaximumRetryCount
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogMode is a type to define the available modes for logging
|
||||||
|
// These modes affect how logs are handled when log messages start piling up.
|
||||||
|
type LogMode string
|
||||||
|
|
||||||
|
// Available logging modes
|
||||||
|
const (
|
||||||
|
LogModeUnset = ""
|
||||||
|
LogModeBlocking LogMode = "blocking"
|
||||||
|
LogModeNonBlock LogMode = "non-blocking"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LogConfig represents the logging configuration of the container.
|
||||||
|
type LogConfig struct {
|
||||||
|
Type string
|
||||||
|
Config map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resources contains container's resources (cgroups config, ulimits...)
|
||||||
|
type Resources struct {
|
||||||
|
// Applicable to all platforms
|
||||||
|
CPUShares int64 `json:"CpuShares"` // CPU shares (relative weight vs. other containers)
|
||||||
|
Memory int64 // Memory limit (in bytes)
|
||||||
|
NanoCPUs int64 `json:"NanoCpus"` // CPU quota in units of 10<sup>-9</sup> CPUs.
|
||||||
|
|
||||||
|
// Applicable to UNIX platforms
|
||||||
|
CgroupParent string // Parent cgroup.
|
||||||
|
BlkioWeight uint16 // Block IO weight (relative weight vs. other containers)
|
||||||
|
BlkioWeightDevice []*blkiodev.WeightDevice
|
||||||
|
BlkioDeviceReadBps []*blkiodev.ThrottleDevice
|
||||||
|
BlkioDeviceWriteBps []*blkiodev.ThrottleDevice
|
||||||
|
BlkioDeviceReadIOps []*blkiodev.ThrottleDevice
|
||||||
|
BlkioDeviceWriteIOps []*blkiodev.ThrottleDevice
|
||||||
|
CPUPeriod int64 `json:"CpuPeriod"` // CPU CFS (Completely Fair Scheduler) period
|
||||||
|
CPUQuota int64 `json:"CpuQuota"` // CPU CFS (Completely Fair Scheduler) quota
|
||||||
|
CPURealtimePeriod int64 `json:"CpuRealtimePeriod"` // CPU real-time period
|
||||||
|
CPURealtimeRuntime int64 `json:"CpuRealtimeRuntime"` // CPU real-time runtime
|
||||||
|
CpusetCpus string // CpusetCpus 0-2, 0,1
|
||||||
|
CpusetMems string // CpusetMems 0-2, 0,1
|
||||||
|
Devices []DeviceMapping // List of devices to map inside the container
|
||||||
|
DeviceCgroupRules []string // List of rule to be added to the device cgroup
|
||||||
|
DeviceRequests []DeviceRequest // List of device requests for device drivers
|
||||||
|
DiskQuota int64 // Disk limit (in bytes)
|
||||||
|
KernelMemory int64 // Kernel memory limit (in bytes)
|
||||||
|
KernelMemoryTCP int64 // Hard limit for kernel TCP buffer memory (in bytes)
|
||||||
|
MemoryReservation int64 // Memory soft limit (in bytes)
|
||||||
|
MemorySwap int64 // Total memory usage (memory + swap); set `-1` to enable unlimited swap
|
||||||
|
MemorySwappiness *int64 // Tuning container memory swappiness behaviour
|
||||||
|
OomKillDisable *bool // Whether to disable OOM Killer or not
|
||||||
|
PidsLimit *int64 // Setting PIDs limit for a container; Set `0` or `-1` for unlimited, or `null` to not change.
|
||||||
|
Ulimits []*units.Ulimit // List of ulimits to be set in the container
|
||||||
|
|
||||||
|
// Applicable to Windows
|
||||||
|
CPUCount int64 `json:"CpuCount"` // CPU count
|
||||||
|
CPUPercent int64 `json:"CpuPercent"` // CPU percent
|
||||||
|
IOMaximumIOps uint64 // Maximum IOps for the container system drive
|
||||||
|
IOMaximumBandwidth uint64 // Maximum IO in bytes per second for the container system drive
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateConfig holds the mutable attributes of a Container.
|
||||||
|
// Those attributes can be updated at runtime.
|
||||||
|
type UpdateConfig struct {
|
||||||
|
// Contains container's resources (cgroups, ulimits)
|
||||||
|
Resources
|
||||||
|
RestartPolicy RestartPolicy
|
||||||
|
}
|
||||||
|
|
||||||
|
// HostConfig the non-portable Config structure of a container.
|
||||||
|
// Here, "non-portable" means "dependent of the host we are running on".
|
||||||
|
// Portable information *should* appear in Config.
|
||||||
|
type HostConfig struct {
|
||||||
|
// Applicable to all platforms
|
||||||
|
Binds []string // List of volume bindings for this container
|
||||||
|
ContainerIDFile string // File (path) where the containerId is written
|
||||||
|
LogConfig LogConfig // Configuration of the logs for this container
|
||||||
|
NetworkMode NetworkMode // Network mode to use for the container
|
||||||
|
PortBindings nat.PortMap // Port mapping between the exposed port (container) and the host
|
||||||
|
RestartPolicy RestartPolicy // Restart policy to be used for the container
|
||||||
|
AutoRemove bool // Automatically remove container when it exits
|
||||||
|
VolumeDriver string // Name of the volume driver used to mount volumes
|
||||||
|
VolumesFrom []string // List of volumes to take from other container
|
||||||
|
|
||||||
|
// Applicable to UNIX platforms
|
||||||
|
CapAdd strslice.StrSlice // List of kernel capabilities to add to the container
|
||||||
|
CapDrop strslice.StrSlice // List of kernel capabilities to remove from the container
|
||||||
|
Capabilities []string `json:"Capabilities"` // List of kernel capabilities to be available for container (this overrides the default set)
|
||||||
|
DNS []string `json:"Dns"` // List of DNS server to lookup
|
||||||
|
DNSOptions []string `json:"DnsOptions"` // List of DNSOption to look for
|
||||||
|
DNSSearch []string `json:"DnsSearch"` // List of DNSSearch to look for
|
||||||
|
ExtraHosts []string // List of extra hosts
|
||||||
|
GroupAdd []string // List of additional groups that the container process will run as
|
||||||
|
IpcMode IpcMode // IPC namespace to use for the container
|
||||||
|
Cgroup CgroupSpec // Cgroup to use for the container
|
||||||
|
Links []string // List of links (in the name:alias form)
|
||||||
|
OomScoreAdj int // Container preference for OOM-killing
|
||||||
|
PidMode PidMode // PID namespace to use for the container
|
||||||
|
Privileged bool // Is the container in privileged mode
|
||||||
|
PublishAllPorts bool // Should docker publish all exposed port for the container
|
||||||
|
ReadonlyRootfs bool // Is the container root filesystem in read-only
|
||||||
|
SecurityOpt []string // List of string values to customize labels for MLS systems, such as SELinux.
|
||||||
|
StorageOpt map[string]string `json:",omitempty"` // Storage driver options per container.
|
||||||
|
Tmpfs map[string]string `json:",omitempty"` // List of tmpfs (mounts) used for the container
|
||||||
|
UTSMode UTSMode // UTS namespace to use for the container
|
||||||
|
UsernsMode UsernsMode // The user namespace to use for the container
|
||||||
|
ShmSize int64 // Total shm memory usage
|
||||||
|
Sysctls map[string]string `json:",omitempty"` // List of Namespaced sysctls used for the container
|
||||||
|
Runtime string `json:",omitempty"` // Runtime to use with this container
|
||||||
|
|
||||||
|
// Applicable to Windows
|
||||||
|
ConsoleSize [2]uint // Initial console size (height,width)
|
||||||
|
Isolation Isolation // Isolation technology of the container (e.g. default, hyperv)
|
||||||
|
|
||||||
|
// Contains container's resources (cgroups, ulimits)
|
||||||
|
Resources
|
||||||
|
|
||||||
|
// Mounts specs used by the container
|
||||||
|
Mounts []mount.Mount `json:",omitempty"`
|
||||||
|
|
||||||
|
// MaskedPaths is the list of paths to be masked inside the container (this overrides the default set of paths)
|
||||||
|
MaskedPaths []string
|
||||||
|
|
||||||
|
// ReadonlyPaths is the list of paths to be set as read-only inside the container (this overrides the default set of paths)
|
||||||
|
ReadonlyPaths []string
|
||||||
|
|
||||||
|
// Run a custom init inside the container, if null, use the daemon's configured settings
|
||||||
|
Init *bool `json:",omitempty"`
|
||||||
|
}
|
41
vendor/github.com/docker/docker/api/types/container/hostconfig_unix.go
generated
vendored
Normal file
41
vendor/github.com/docker/docker/api/types/container/hostconfig_unix.go
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package container // import "github.com/docker/docker/api/types/container"
|
||||||
|
|
||||||
|
// IsValid indicates if an isolation technology is valid
|
||||||
|
func (i Isolation) IsValid() bool {
|
||||||
|
return i.IsDefault()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkName returns the name of the network stack.
|
||||||
|
func (n NetworkMode) NetworkName() string {
|
||||||
|
if n.IsBridge() {
|
||||||
|
return "bridge"
|
||||||
|
} else if n.IsHost() {
|
||||||
|
return "host"
|
||||||
|
} else if n.IsContainer() {
|
||||||
|
return "container"
|
||||||
|
} else if n.IsNone() {
|
||||||
|
return "none"
|
||||||
|
} else if n.IsDefault() {
|
||||||
|
return "default"
|
||||||
|
} else if n.IsUserDefined() {
|
||||||
|
return n.UserDefined()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsBridge indicates whether container uses the bridge network stack
|
||||||
|
func (n NetworkMode) IsBridge() bool {
|
||||||
|
return n == "bridge"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsHost indicates whether container uses the host network stack.
|
||||||
|
func (n NetworkMode) IsHost() bool {
|
||||||
|
return n == "host"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUserDefined indicates user-created network
|
||||||
|
func (n NetworkMode) IsUserDefined() bool {
|
||||||
|
return !n.IsDefault() && !n.IsBridge() && !n.IsHost() && !n.IsNone() && !n.IsContainer()
|
||||||
|
}
|
40
vendor/github.com/docker/docker/api/types/container/hostconfig_windows.go
generated
vendored
Normal file
40
vendor/github.com/docker/docker/api/types/container/hostconfig_windows.go
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package container // import "github.com/docker/docker/api/types/container"
|
||||||
|
|
||||||
|
// IsBridge indicates whether container uses the bridge network stack
|
||||||
|
// in windows it is given the name NAT
|
||||||
|
func (n NetworkMode) IsBridge() bool {
|
||||||
|
return n == "nat"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsHost indicates whether container uses the host network stack.
|
||||||
|
// returns false as this is not supported by windows
|
||||||
|
func (n NetworkMode) IsHost() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUserDefined indicates user-created network
|
||||||
|
func (n NetworkMode) IsUserDefined() bool {
|
||||||
|
return !n.IsDefault() && !n.IsNone() && !n.IsBridge() && !n.IsContainer()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValid indicates if an isolation technology is valid
|
||||||
|
func (i Isolation) IsValid() bool {
|
||||||
|
return i.IsDefault() || i.IsHyperV() || i.IsProcess()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkName returns the name of the network stack.
|
||||||
|
func (n NetworkMode) NetworkName() string {
|
||||||
|
if n.IsDefault() {
|
||||||
|
return "default"
|
||||||
|
} else if n.IsBridge() {
|
||||||
|
return "nat"
|
||||||
|
} else if n.IsNone() {
|
||||||
|
return "none"
|
||||||
|
} else if n.IsContainer() {
|
||||||
|
return "container"
|
||||||
|
} else if n.IsUserDefined() {
|
||||||
|
return n.UserDefined()
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
22
vendor/github.com/docker/docker/api/types/container/waitcondition.go
generated
vendored
Normal file
22
vendor/github.com/docker/docker/api/types/container/waitcondition.go
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package container // import "github.com/docker/docker/api/types/container"
|
||||||
|
|
||||||
|
// WaitCondition is a type used to specify a container state for which
|
||||||
|
// to wait.
|
||||||
|
type WaitCondition string
|
||||||
|
|
||||||
|
// Possible WaitCondition Values.
|
||||||
|
//
|
||||||
|
// WaitConditionNotRunning (default) is used to wait for any of the non-running
|
||||||
|
// states: "created", "exited", "dead", "removing", or "removed".
|
||||||
|
//
|
||||||
|
// WaitConditionNextExit is used to wait for the next time the state changes
|
||||||
|
// to a non-running state. If the state is currently "created" or "exited",
|
||||||
|
// this would cause Wait() to block until either the container runs and exits
|
||||||
|
// or is removed.
|
||||||
|
//
|
||||||
|
// WaitConditionRemoved is used to wait for the container to be removed.
|
||||||
|
const (
|
||||||
|
WaitConditionNotRunning WaitCondition = "not-running"
|
||||||
|
WaitConditionNextExit WaitCondition = "next-exit"
|
||||||
|
WaitConditionRemoved WaitCondition = "removed"
|
||||||
|
)
|
13
vendor/github.com/docker/docker/api/types/error_response.go
generated
vendored
Normal file
13
vendor/github.com/docker/docker/api/types/error_response.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
// This file was generated by the swagger tool.
|
||||||
|
// Editing this file might prove futile when you re-run the swagger generate command
|
||||||
|
|
||||||
|
// ErrorResponse Represents an error.
|
||||||
|
// swagger:model ErrorResponse
|
||||||
|
type ErrorResponse struct {
|
||||||
|
|
||||||
|
// The error message.
|
||||||
|
// Required: true
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
52
vendor/github.com/docker/docker/api/types/events/events.go
generated
vendored
Normal file
52
vendor/github.com/docker/docker/api/types/events/events.go
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package events // import "github.com/docker/docker/api/types/events"
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ContainerEventType is the event type that containers generate
|
||||||
|
ContainerEventType = "container"
|
||||||
|
// DaemonEventType is the event type that daemon generate
|
||||||
|
DaemonEventType = "daemon"
|
||||||
|
// ImageEventType is the event type that images generate
|
||||||
|
ImageEventType = "image"
|
||||||
|
// NetworkEventType is the event type that networks generate
|
||||||
|
NetworkEventType = "network"
|
||||||
|
// PluginEventType is the event type that plugins generate
|
||||||
|
PluginEventType = "plugin"
|
||||||
|
// VolumeEventType is the event type that volumes generate
|
||||||
|
VolumeEventType = "volume"
|
||||||
|
// ServiceEventType is the event type that services generate
|
||||||
|
ServiceEventType = "service"
|
||||||
|
// NodeEventType is the event type that nodes generate
|
||||||
|
NodeEventType = "node"
|
||||||
|
// SecretEventType is the event type that secrets generate
|
||||||
|
SecretEventType = "secret"
|
||||||
|
// ConfigEventType is the event type that configs generate
|
||||||
|
ConfigEventType = "config"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Actor describes something that generates events,
|
||||||
|
// like a container, or a network, or a volume.
|
||||||
|
// It has a defined name and a set or attributes.
|
||||||
|
// The container attributes are its labels, other actors
|
||||||
|
// can generate these attributes from other properties.
|
||||||
|
type Actor struct {
|
||||||
|
ID string
|
||||||
|
Attributes map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message represents the information an event contains
|
||||||
|
type Message struct {
|
||||||
|
// Deprecated information from JSONMessage.
|
||||||
|
// With data only in container events.
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
From string `json:"from,omitempty"`
|
||||||
|
|
||||||
|
Type string
|
||||||
|
Action string
|
||||||
|
Actor Actor
|
||||||
|
// Engine events are local scope. Cluster events are swarm scope.
|
||||||
|
Scope string `json:"scope,omitempty"`
|
||||||
|
|
||||||
|
Time int64 `json:"time,omitempty"`
|
||||||
|
TimeNano int64 `json:"timeNano,omitempty"`
|
||||||
|
}
|
315
vendor/github.com/docker/docker/api/types/filters/parse.go
generated
vendored
Normal file
315
vendor/github.com/docker/docker/api/types/filters/parse.go
generated
vendored
Normal file
@ -0,0 +1,315 @@
|
|||||||
|
/*Package filters provides tools for encoding a mapping of keys to a set of
|
||||||
|
multiple values.
|
||||||
|
*/
|
||||||
|
package filters // import "github.com/docker/docker/api/types/filters"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types/versions"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Args stores a mapping of keys to a set of multiple values.
|
||||||
|
type Args struct {
|
||||||
|
fields map[string]map[string]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyValuePair are used to initialize a new Args
|
||||||
|
type KeyValuePair struct {
|
||||||
|
Key string
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arg creates a new KeyValuePair for initializing Args
|
||||||
|
func Arg(key, value string) KeyValuePair {
|
||||||
|
return KeyValuePair{Key: key, Value: value}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewArgs returns a new Args populated with the initial args
|
||||||
|
func NewArgs(initialArgs ...KeyValuePair) Args {
|
||||||
|
args := Args{fields: map[string]map[string]bool{}}
|
||||||
|
for _, arg := range initialArgs {
|
||||||
|
args.Add(arg.Key, arg.Value)
|
||||||
|
}
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON returns a JSON byte representation of the Args
|
||||||
|
func (args Args) MarshalJSON() ([]byte, error) {
|
||||||
|
if len(args.fields) == 0 {
|
||||||
|
return []byte{}, nil
|
||||||
|
}
|
||||||
|
return json.Marshal(args.fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToJSON returns the Args as a JSON encoded string
|
||||||
|
func ToJSON(a Args) (string, error) {
|
||||||
|
if a.Len() == 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
buf, err := json.Marshal(a)
|
||||||
|
return string(buf), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToParamWithVersion encodes Args as a JSON string. If version is less than 1.22
|
||||||
|
// then the encoded format will use an older legacy format where the values are a
|
||||||
|
// list of strings, instead of a set.
|
||||||
|
//
|
||||||
|
// Deprecated: Use ToJSON
|
||||||
|
func ToParamWithVersion(version string, a Args) (string, error) {
|
||||||
|
if a.Len() == 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if version != "" && versions.LessThan(version, "1.22") {
|
||||||
|
buf, err := json.Marshal(convertArgsToSlice(a.fields))
|
||||||
|
return string(buf), err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ToJSON(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromJSON decodes a JSON encoded string into Args
|
||||||
|
func FromJSON(p string) (Args, error) {
|
||||||
|
args := NewArgs()
|
||||||
|
|
||||||
|
if p == "" {
|
||||||
|
return args, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
raw := []byte(p)
|
||||||
|
err := json.Unmarshal(raw, &args)
|
||||||
|
if err == nil {
|
||||||
|
return args, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to parsing arguments in the legacy slice format
|
||||||
|
deprecated := map[string][]string{}
|
||||||
|
if legacyErr := json.Unmarshal(raw, &deprecated); legacyErr != nil {
|
||||||
|
return args, err
|
||||||
|
}
|
||||||
|
|
||||||
|
args.fields = deprecatedArgs(deprecated)
|
||||||
|
return args, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON populates the Args from JSON encode bytes
|
||||||
|
func (args Args) UnmarshalJSON(raw []byte) error {
|
||||||
|
if len(raw) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return json.Unmarshal(raw, &args.fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the list of values associated with the key
|
||||||
|
func (args Args) Get(key string) []string {
|
||||||
|
values := args.fields[key]
|
||||||
|
if values == nil {
|
||||||
|
return make([]string, 0)
|
||||||
|
}
|
||||||
|
slice := make([]string, 0, len(values))
|
||||||
|
for key := range values {
|
||||||
|
slice = append(slice, key)
|
||||||
|
}
|
||||||
|
return slice
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a new value to the set of values
|
||||||
|
func (args Args) Add(key, value string) {
|
||||||
|
if _, ok := args.fields[key]; ok {
|
||||||
|
args.fields[key][value] = true
|
||||||
|
} else {
|
||||||
|
args.fields[key] = map[string]bool{value: true}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Del removes a value from the set
|
||||||
|
func (args Args) Del(key, value string) {
|
||||||
|
if _, ok := args.fields[key]; ok {
|
||||||
|
delete(args.fields[key], value)
|
||||||
|
if len(args.fields[key]) == 0 {
|
||||||
|
delete(args.fields, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of keys in the mapping
|
||||||
|
func (args Args) Len() int {
|
||||||
|
return len(args.fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchKVList returns true if all the pairs in sources exist as key=value
|
||||||
|
// pairs in the mapping at key, or if there are no values at key.
|
||||||
|
func (args Args) MatchKVList(key string, sources map[string]string) bool {
|
||||||
|
fieldValues := args.fields[key]
|
||||||
|
|
||||||
|
//do not filter if there is no filter set or cannot determine filter
|
||||||
|
if len(fieldValues) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(sources) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for value := range fieldValues {
|
||||||
|
testKV := strings.SplitN(value, "=", 2)
|
||||||
|
|
||||||
|
v, ok := sources[testKV[0]]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(testKV) == 2 && testKV[1] != v {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match returns true if any of the values at key match the source string
|
||||||
|
func (args Args) Match(field, source string) bool {
|
||||||
|
if args.ExactMatch(field, source) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldValues := args.fields[field]
|
||||||
|
for name2match := range fieldValues {
|
||||||
|
match, err := regexp.MatchString(name2match, source)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if match {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExactMatch returns true if the source matches exactly one of the values.
|
||||||
|
func (args Args) ExactMatch(key, source string) bool {
|
||||||
|
fieldValues, ok := args.fields[key]
|
||||||
|
//do not filter if there is no filter set or cannot determine filter
|
||||||
|
if !ok || len(fieldValues) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to match full name value to avoid O(N) regular expression matching
|
||||||
|
return fieldValues[source]
|
||||||
|
}
|
||||||
|
|
||||||
|
// UniqueExactMatch returns true if there is only one value and the source
|
||||||
|
// matches exactly the value.
|
||||||
|
func (args Args) UniqueExactMatch(key, source string) bool {
|
||||||
|
fieldValues := args.fields[key]
|
||||||
|
//do not filter if there is no filter set or cannot determine filter
|
||||||
|
if len(fieldValues) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if len(args.fields[key]) != 1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to match full name value to avoid O(N) regular expression matching
|
||||||
|
return fieldValues[source]
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuzzyMatch returns true if the source matches exactly one value, or the
|
||||||
|
// source has one of the values as a prefix.
|
||||||
|
func (args Args) FuzzyMatch(key, source string) bool {
|
||||||
|
if args.ExactMatch(key, source) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldValues := args.fields[key]
|
||||||
|
for prefix := range fieldValues {
|
||||||
|
if strings.HasPrefix(source, prefix) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains returns true if the key exists in the mapping
|
||||||
|
func (args Args) Contains(field string) bool {
|
||||||
|
_, ok := args.fields[field]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
type invalidFilter string
|
||||||
|
|
||||||
|
func (e invalidFilter) Error() string {
|
||||||
|
return "Invalid filter '" + string(e) + "'"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (invalidFilter) InvalidParameter() {}
|
||||||
|
|
||||||
|
// Validate compared the set of accepted keys against the keys in the mapping.
|
||||||
|
// An error is returned if any mapping keys are not in the accepted set.
|
||||||
|
func (args Args) Validate(accepted map[string]bool) error {
|
||||||
|
for name := range args.fields {
|
||||||
|
if !accepted[name] {
|
||||||
|
return invalidFilter(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WalkValues iterates over the list of values for a key in the mapping and calls
|
||||||
|
// op() for each value. If op returns an error the iteration stops and the
|
||||||
|
// error is returned.
|
||||||
|
func (args Args) WalkValues(field string, op func(value string) error) error {
|
||||||
|
if _, ok := args.fields[field]; !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for v := range args.fields[field] {
|
||||||
|
if err := op(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone returns a copy of args.
|
||||||
|
func (args Args) Clone() (newArgs Args) {
|
||||||
|
newArgs.fields = make(map[string]map[string]bool, len(args.fields))
|
||||||
|
for k, m := range args.fields {
|
||||||
|
var mm map[string]bool
|
||||||
|
if m != nil {
|
||||||
|
mm = make(map[string]bool, len(m))
|
||||||
|
for kk, v := range m {
|
||||||
|
mm[kk] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newArgs.fields[k] = mm
|
||||||
|
}
|
||||||
|
return newArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
func deprecatedArgs(d map[string][]string) map[string]map[string]bool {
|
||||||
|
m := map[string]map[string]bool{}
|
||||||
|
for k, v := range d {
|
||||||
|
values := map[string]bool{}
|
||||||
|
for _, vv := range v {
|
||||||
|
values[vv] = true
|
||||||
|
}
|
||||||
|
m[k] = values
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertArgsToSlice(f map[string]map[string]bool) map[string][]string {
|
||||||
|
m := map[string][]string{}
|
||||||
|
for k, v := range f {
|
||||||
|
values := []string{}
|
||||||
|
for kk := range v {
|
||||||
|
if v[kk] {
|
||||||
|
values = append(values, kk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m[k] = values
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user