Initial FrameworkController: General-Purpose Kubernetes Pod Controller

This commit is contained in:
Yuqi Wang 2018-10-22 08:34:54 +00:00
Родитель 25910c98c9
Коммит 75dea76860
2446 изменённых файлов: 766096 добавлений и 329 удалений

329
.gitignore поставляемый
Просмотреть файл

@ -1,330 +1,3 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
**/Properties/launchSettings.json
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
dist/
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/

529
Gopkg.lock сгенерированный Normal file
Просмотреть файл

@ -0,0 +1,529 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
name = "github.com/PuerkitoBio/purell"
packages = ["."]
revision = "0bcb03f4b4d0a9428594752bd2a3b9aa0a9d4bd4"
version = "v1.1.0"
[[projects]]
branch = "master"
name = "github.com/PuerkitoBio/urlesc"
packages = ["."]
revision = "de5bf2ad457846296e2031421a34e2568e304e35"
[[projects]]
name = "github.com/davecgh/go-spew"
packages = ["spew"]
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
version = "v1.1.0"
[[projects]]
name = "github.com/emicklei/go-restful"
packages = [
".",
"log"
]
revision = "3eb9738c1697594ea6e71a7156a9bb32ed216cf0"
version = "v2.8.0"
[[projects]]
name = "github.com/ghodss/yaml"
packages = ["."]
revision = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7"
version = "v1.0.0"
[[projects]]
name = "github.com/go-openapi/jsonpointer"
packages = ["."]
revision = "3a0015ad55fa9873f41605d3e8f28cd279c32ab2"
version = "0.15.0"
[[projects]]
name = "github.com/go-openapi/jsonreference"
packages = ["."]
revision = "3fb327e6747da3043567ee86abd02bb6376b6be2"
version = "0.15.0"
[[projects]]
name = "github.com/go-openapi/spec"
packages = ["."]
revision = "bce47c9386f9ecd6b86f450478a80103c3fe1402"
version = "0.15.0"
[[projects]]
name = "github.com/go-openapi/swag"
packages = ["."]
revision = "2b0bd4f193d011c203529df626a65d63cb8a79e8"
version = "0.15.0"
[[projects]]
name = "github.com/gogo/protobuf"
packages = [
"proto",
"sortkeys"
]
revision = "636bf0302bc95575d69441b25a2603156ffdddf1"
version = "v1.1.1"
[[projects]]
branch = "master"
name = "github.com/golang/glog"
packages = ["."]
revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998"
[[projects]]
name = "github.com/golang/protobuf"
packages = [
"proto",
"ptypes",
"ptypes/any",
"ptypes/duration",
"ptypes/timestamp"
]
revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265"
version = "v1.1.0"
[[projects]]
branch = "master"
name = "github.com/google/gofuzz"
packages = ["."]
revision = "24818f796faf91cd76ec7bddd72458fbced7a6c1"
[[projects]]
name = "github.com/googleapis/gnostic"
packages = [
"OpenAPIv2",
"compiler",
"extensions"
]
revision = "7c663266750e7d82587642f65e60bc4083f1f84e"
version = "v0.2.0"
[[projects]]
branch = "master"
name = "github.com/hashicorp/golang-lru"
packages = [
".",
"simplelru"
]
revision = "0fb14efe8c47ae851c0034ed7a448854d3d34cf3"
[[projects]]
branch = "master"
name = "github.com/howeyc/gopass"
packages = ["."]
revision = "bf9dde6d0d2c004a008c27aaee91170c786f6db8"
[[projects]]
name = "github.com/imdario/mergo"
packages = ["."]
revision = "9316a62528ac99aaecb4e47eadd6dc8aa6533d58"
version = "v0.3.5"
[[projects]]
name = "github.com/json-iterator/go"
packages = ["."]
revision = "ab8a2e0c74be9d3be70b3184d9acc634935ded82"
version = "1.1.4"
[[projects]]
branch = "master"
name = "github.com/mailru/easyjson"
packages = [
"buffer",
"jlexer",
"jwriter"
]
revision = "d5012789d6659eeed305f54c1b1542e7b65829e6"
[[projects]]
name = "github.com/modern-go/concurrent"
packages = ["."]
revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94"
version = "1.0.3"
[[projects]]
name = "github.com/modern-go/reflect2"
packages = ["."]
revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd"
version = "1.0.1"
[[projects]]
name = "github.com/pkg/errors"
packages = ["."]
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
version = "v0.8.0"
[[projects]]
name = "github.com/sirupsen/logrus"
packages = ["."]
revision = "3e01752db0189b9157070a0e1668a620f9a85da2"
version = "v1.0.6"
[[projects]]
name = "github.com/spf13/pflag"
packages = ["."]
revision = "583c0c0531f06d5278b7d917446061adc344b5cd"
version = "v1.0.1"
[[projects]]
branch = "master"
name = "golang.org/x/crypto"
packages = ["ssh/terminal"]
revision = "c126467f60eb25f8f27e5a981f32a87e3965053f"
[[projects]]
branch = "master"
name = "golang.org/x/net"
packages = [
"context",
"http/httpguts",
"http2",
"http2/hpack",
"idna"
]
revision = "a680a1efc54dd51c040b3b5ce4939ea3cf2ea0d1"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = [
"unix",
"windows"
]
revision = "ac767d655b305d4e9612f5f6e33120b9176c4ad4"
[[projects]]
name = "golang.org/x/text"
packages = [
"collate",
"collate/build",
"internal/colltab",
"internal/gen",
"internal/tag",
"internal/triegen",
"internal/ucd",
"language",
"secure/bidirule",
"transform",
"unicode/bidi",
"unicode/cldr",
"unicode/norm",
"unicode/rangetable",
"width"
]
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
version = "v0.3.0"
[[projects]]
branch = "master"
name = "golang.org/x/time"
packages = ["rate"]
revision = "fbb02b2291d28baffd63558aa44b4b56f178d650"
[[projects]]
branch = "master"
name = "golang.org/x/tools"
packages = [
"go/ast/astutil",
"imports",
"internal/fastwalk"
]
revision = "ded554d0681e0cba3cf074977cdc12e2c0906fe6"
[[projects]]
name = "gopkg.in/inf.v0"
packages = ["."]
revision = "d2d2541c53f18d2a059457998ce2876cc8e67cbf"
version = "v0.9.1"
[[projects]]
name = "gopkg.in/yaml.v2"
packages = ["."]
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
version = "v2.2.1"
[[projects]]
name = "k8s.io/api"
packages = [
"admissionregistration/v1alpha1",
"admissionregistration/v1beta1",
"apps/v1",
"apps/v1beta1",
"apps/v1beta2",
"authentication/v1",
"authentication/v1beta1",
"authorization/v1",
"authorization/v1beta1",
"autoscaling/v1",
"autoscaling/v2beta1",
"batch/v1",
"batch/v1beta1",
"batch/v2alpha1",
"certificates/v1beta1",
"core/v1",
"events/v1beta1",
"extensions/v1beta1",
"networking/v1",
"policy/v1beta1",
"rbac/v1",
"rbac/v1alpha1",
"rbac/v1beta1",
"scheduling/v1alpha1",
"settings/v1alpha1",
"storage/v1",
"storage/v1alpha1",
"storage/v1beta1"
]
revision = "73d903622b7391f3312dcbac6483fed484e185f8"
version = "kubernetes-1.10.0"
[[projects]]
name = "k8s.io/apiextensions-apiserver"
packages = [
"pkg/apis/apiextensions",
"pkg/apis/apiextensions/v1beta1",
"pkg/client/clientset/clientset",
"pkg/client/clientset/clientset/scheme",
"pkg/client/clientset/clientset/typed/apiextensions/v1beta1"
]
revision = "750feebe2038bad00fdf68ba37c31f62c4f1f891"
version = "kubernetes-1.10.0"
[[projects]]
name = "k8s.io/apimachinery"
packages = [
"pkg/api/errors",
"pkg/api/meta",
"pkg/api/resource",
"pkg/apimachinery",
"pkg/apimachinery/registered",
"pkg/apis/meta/internalversion",
"pkg/apis/meta/v1",
"pkg/apis/meta/v1/unstructured",
"pkg/apis/meta/v1beta1",
"pkg/conversion",
"pkg/conversion/queryparams",
"pkg/fields",
"pkg/labels",
"pkg/runtime",
"pkg/runtime/schema",
"pkg/runtime/serializer",
"pkg/runtime/serializer/json",
"pkg/runtime/serializer/protobuf",
"pkg/runtime/serializer/recognizer",
"pkg/runtime/serializer/streaming",
"pkg/runtime/serializer/versioning",
"pkg/selection",
"pkg/types",
"pkg/util/cache",
"pkg/util/clock",
"pkg/util/diff",
"pkg/util/errors",
"pkg/util/framer",
"pkg/util/intstr",
"pkg/util/json",
"pkg/util/net",
"pkg/util/runtime",
"pkg/util/sets",
"pkg/util/validation",
"pkg/util/validation/field",
"pkg/util/wait",
"pkg/util/yaml",
"pkg/version",
"pkg/watch",
"third_party/forked/golang/reflect"
]
revision = "302974c03f7e50f16561ba237db776ab93594ef6"
version = "kubernetes-1.10.0"
[[projects]]
name = "k8s.io/client-go"
packages = [
"discovery",
"discovery/fake",
"informers",
"informers/admissionregistration",
"informers/admissionregistration/v1alpha1",
"informers/admissionregistration/v1beta1",
"informers/apps",
"informers/apps/v1",
"informers/apps/v1beta1",
"informers/apps/v1beta2",
"informers/autoscaling",
"informers/autoscaling/v1",
"informers/autoscaling/v2beta1",
"informers/batch",
"informers/batch/v1",
"informers/batch/v1beta1",
"informers/batch/v2alpha1",
"informers/certificates",
"informers/certificates/v1beta1",
"informers/core",
"informers/core/v1",
"informers/events",
"informers/events/v1beta1",
"informers/extensions",
"informers/extensions/v1beta1",
"informers/internalinterfaces",
"informers/networking",
"informers/networking/v1",
"informers/policy",
"informers/policy/v1beta1",
"informers/rbac",
"informers/rbac/v1",
"informers/rbac/v1alpha1",
"informers/rbac/v1beta1",
"informers/scheduling",
"informers/scheduling/v1alpha1",
"informers/settings",
"informers/settings/v1alpha1",
"informers/storage",
"informers/storage/v1",
"informers/storage/v1alpha1",
"informers/storage/v1beta1",
"kubernetes",
"kubernetes/scheme",
"kubernetes/typed/admissionregistration/v1alpha1",
"kubernetes/typed/admissionregistration/v1beta1",
"kubernetes/typed/apps/v1",
"kubernetes/typed/apps/v1beta1",
"kubernetes/typed/apps/v1beta2",
"kubernetes/typed/authentication/v1",
"kubernetes/typed/authentication/v1beta1",
"kubernetes/typed/authorization/v1",
"kubernetes/typed/authorization/v1beta1",
"kubernetes/typed/autoscaling/v1",
"kubernetes/typed/autoscaling/v2beta1",
"kubernetes/typed/batch/v1",
"kubernetes/typed/batch/v1beta1",
"kubernetes/typed/batch/v2alpha1",
"kubernetes/typed/certificates/v1beta1",
"kubernetes/typed/core/v1",
"kubernetes/typed/events/v1beta1",
"kubernetes/typed/extensions/v1beta1",
"kubernetes/typed/networking/v1",
"kubernetes/typed/policy/v1beta1",
"kubernetes/typed/rbac/v1",
"kubernetes/typed/rbac/v1alpha1",
"kubernetes/typed/rbac/v1beta1",
"kubernetes/typed/scheduling/v1alpha1",
"kubernetes/typed/settings/v1alpha1",
"kubernetes/typed/storage/v1",
"kubernetes/typed/storage/v1alpha1",
"kubernetes/typed/storage/v1beta1",
"listers/admissionregistration/v1alpha1",
"listers/admissionregistration/v1beta1",
"listers/apps/v1",
"listers/apps/v1beta1",
"listers/apps/v1beta2",
"listers/autoscaling/v1",
"listers/autoscaling/v2beta1",
"listers/batch/v1",
"listers/batch/v1beta1",
"listers/batch/v2alpha1",
"listers/certificates/v1beta1",
"listers/core/v1",
"listers/events/v1beta1",
"listers/extensions/v1beta1",
"listers/networking/v1",
"listers/policy/v1beta1",
"listers/rbac/v1",
"listers/rbac/v1alpha1",
"listers/rbac/v1beta1",
"listers/scheduling/v1alpha1",
"listers/settings/v1alpha1",
"listers/storage/v1",
"listers/storage/v1alpha1",
"listers/storage/v1beta1",
"pkg/apis/clientauthentication",
"pkg/apis/clientauthentication/v1alpha1",
"pkg/version",
"plugin/pkg/client/auth/exec",
"rest",
"rest/watch",
"testing",
"tools/auth",
"tools/cache",
"tools/clientcmd",
"tools/clientcmd/api",
"tools/clientcmd/api/latest",
"tools/clientcmd/api/v1",
"tools/metrics",
"tools/pager",
"tools/reference",
"transport",
"util/buffer",
"util/cert",
"util/flowcontrol",
"util/homedir",
"util/integer",
"util/retry",
"util/workqueue"
]
revision = "23781f4d6632d88e869066eaebb743857aa1ef9b"
version = "v7.0.0"
[[projects]]
name = "k8s.io/code-generator"
packages = [
"cmd/client-gen",
"cmd/client-gen/args",
"cmd/client-gen/generators",
"cmd/client-gen/generators/fake",
"cmd/client-gen/generators/scheme",
"cmd/client-gen/generators/util",
"cmd/client-gen/path",
"cmd/client-gen/types",
"cmd/deepcopy-gen",
"cmd/deepcopy-gen/args",
"cmd/defaulter-gen",
"cmd/defaulter-gen/args",
"cmd/informer-gen",
"cmd/informer-gen/args",
"cmd/informer-gen/generators",
"cmd/lister-gen",
"cmd/lister-gen/args",
"cmd/lister-gen/generators",
"cmd/openapi-gen",
"cmd/openapi-gen/args",
"pkg/util"
]
revision = "7ead8f38b01cf8653249f5af80ce7b2c8aba12e2"
version = "kubernetes-1.10.0"
[[projects]]
branch = "master"
name = "k8s.io/gengo"
packages = [
"args",
"examples/deepcopy-gen/generators",
"examples/defaulter-gen/generators",
"examples/set-gen/sets",
"generator",
"namer",
"parser",
"types"
]
revision = "906d99f89cd644eecf75ab547b29bf9f876f0b59"
[[projects]]
branch = "master"
name = "k8s.io/kube-openapi"
packages = [
"cmd/openapi-gen/args",
"pkg/common",
"pkg/generators",
"pkg/generators/rules",
"pkg/util/sets"
]
revision = "d8ea2fe547a448256204cfc68dfee7b26c720acb"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "cb5bee9e5ec1e65f463ce5bdd3d489484ddd3c564f13bcf3d681ef9c9da8d724"
solver-name = "gps-cdcl"
solver-version = 1

52
Gopkg.toml Normal file
Просмотреть файл

@ -0,0 +1,52 @@
ignored = ["github.com/microsoft/frameworkcontroller"]
required = [
"k8s.io/code-generator/cmd/client-gen",
"k8s.io/code-generator/cmd/informer-gen",
"k8s.io/code-generator/cmd/lister-gen",
"k8s.io/code-generator/cmd/deepcopy-gen",
"k8s.io/code-generator/cmd/defaulter-gen",
"k8s.io/code-generator/cmd/openapi-gen",
"k8s.io/apimachinery/pkg/apimachinery/registered",
]
[[constraint]]
name = "github.com/sirupsen/logrus"
version = "1.0.4"
[[constraint]]
name = "github.com/pkg/errors"
version = "v0.8.0"
[[constraint]]
name = "k8s.io/client-go"
version = "kubernetes-1.10.0"
[[constraint]]
name = "k8s.io/api"
version = "kubernetes-1.10.0"
[[constraint]]
name = "k8s.io/apimachinery"
version = "kubernetes-1.10.0"
[[constraint]]
name = "k8s.io/code-generator"
version = "kubernetes-1.10.0"
[[constraint]]
name = "k8s.io/apiextensions-apiserver"
version = "kubernetes-1.10.0"
[prune]
go-tests = true
unused-packages = true
non-go = true
[[prune.project]]
name = "k8s.io/code-generator"
unused-packages = false
non-go = false
[[prune.project]]
name = "k8s.io/client-go"
unused-packages = false
non-go = false

Просмотреть файл

@ -1,6 +1,82 @@
# FrameworkController
FrameworkController is built to orchestrate all kinds of applications on [Kubernetes](https://kubernetes.io) by a single controller.
# Contributing
These kinds of applications include but not limited to:
- __Stateless and Stateful Service__ (Nginx, TensorFlow Serving, HBase, Kafka, etc)
- __Stateless and Stateful Batch__ (KD-Tree Building, Batch Data Processing, etc)
- __Any combination of above applications__ (Distributed TensorFlow Training, Stream Data Processing, etc)
## Why Need It
### Problem
In the open source community, there are so many specialized Kubernetes Pod controllers which are built for a specific kind of application, such as [Kubernetes StatefulSet Controller](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset), [Kubernetes Job Controller](https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion), [KubeFlow TFJob Operator](https://www.kubeflow.org/docs/guides/components/tftraining). However, no one is built for all kinds of applications and combination of the existing ones still cannot support some kinds of applications. So, we have to learn, use, develop, deploy and maintain so many Pod controllers.
### Solution
Build a General-Purpose Kubernetes Pod Controller: FrameworkController.
And then we can get below benefits from it:
- __Support Kubernetes official unsupported applications__
Such as the [Stateful Batch with Service](example/framework/basic/batchwithservicesucceeded.yaml) application, like [Distributed TensorFlow
Training](https://www.tensorflow.org/deploy/distributed).
- __Only need to learn, use, develop, deploy and maintain a single controller__
- __All kinds of applications can be orchestrated through the same interface with a unified experience__
- __If really required, only need to build specialized controllers on top of it, instead of building from scratch__
The similar practice is also adopted by Kubernetes official controllers, such as the [Kubernetes Deployment Controller](https://kubernetes.io/docs/concepts/workloads/controllers/deployment) is built on top of the [Kubernetes ReplicaSet Controller](https://kubernetes.io/docs/concepts/workloads/controllers/replicaset).
## Feature
### Framework Feature
A Framework represents an application with a set of Tasks:
1. Executed by Kubernetes Pod
2. Partitioned to different heterogeneous TaskRoles which share the same lifecycle
3. Ordered in the same homogeneous TaskRole by TaskIndex
4. With consistent identity {FrameworkName}-{TaskRoleName}-{TaskIndex} as PodName
5. With fine grained RetryPolicy for each Task and the whole Framework
6. With fine grained FrameworkAttemptCompletionPolicy for each TaskRole
7. Guarantees at most one instance of a specific Task is running at any point in time
8. Guarantees at most one instance of a specific Framework is running at any point in time
### Controller Feature
1. Highly generalized as it is built for all kinds of applications
2. Light-weight as it is only responsible for Pod orchestration
3. Tolerate Pod/ConfigMap unexpected deletion, Node/Network/FrameworkController/Kubernetes failure
4. Well-defined Framework consistency, state machine and failure model
5. Idiomatic with Kubernetes official controllers, such as [Pod Spec](https://kubernetes.io/docs/concepts/workloads/pods/pod-overview/#pod-templates)
6. Compatible with other Kubernetes features, such as Kubernetes [Service](https://kubernetes.io/docs/concepts/services-networking/service), [Gpu Scheduling](https://kubernetes.io/docs/tasks/manage-gpus/scheduling-gpus), [Volume](https://kubernetes.io/docs/concepts/storage/volumes/), [Logging](https://kubernetes.io/docs/concepts/cluster-administration/logging)
7. Aligned with Kubernetes [Controller Design Guidelines](https://github.com/kubernetes/community/blob/master/contributors/devel/controllers.md) and [API Conventions](https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md)
## Prerequisite
1. A Kubernetes cluster, v1.10 or above, on-cloud or on-premise.
## Quick Start
1. [Build](build/frameworkcontroller)
2. [Run Example](example/run/frameworkcontroller)
3. [Config Usage](pkg/apis/frameworkcontroller/v1/config.go)
4. [Config Example](example/config)
5. [Framework Usage](pkg/apis/frameworkcontroller/v1/types.go)
6. [Framework Example](example/framework)
## Doc
1. [User Manual](doc/user-manual.md)
2. [Known Issue and Upcoming Feature](doc/known-issue-and-upcoming-feature.md)
3. FAQ
4. Release Note
## Third Party Controller Wrapper:
A specialized wrapper can be built on top of FrameworkController to optimize for a specific kind of application:
* [OpenPAI Controller Wrapper](https://github.com/Microsoft/pai)(Developing): A wrapper client optimized for AI applications
* [NNI Controller Wrapper](https://github.com/Microsoft/nni)(Developing): A wrapper client optimized for AutoML applications
## Official Image
[FrameworkController DockerHub](https://hub.docker.com/u/frameworkcontroller)
## Related Project
* [YARN FrameworkLauncher](https://github.com/Microsoft/pai/blob/master/subprojects/frameworklauncher/yarn): Similar offering natively supports [Apache YARN](http://hadoop.apache.org)
## Contributing
This project welcomes contributions and suggestions. Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
the rights to use your contribution. For details, visit https://cla.microsoft.com.
@ -12,3 +88,4 @@ provided by the bot. You will only need to do this once across all repos using o
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.

Просмотреть файл

@ -0,0 +1,33 @@
#!/bin/bash
# MIT License
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# 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
set -o errexit
set -o nounset
set -o pipefail
BASH_DIR=$(cd $(dirname ${BASH_SOURCE}) && pwd)
cd ${BASH_DIR}
./frameworkcontroller

Просмотреть файл

@ -0,0 +1,34 @@
# MIT License
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# 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
FROM golang:alpine
ENV PROJECT_DIR=${GOPATH}/src/github.com/microsoft/frameworkcontroller
RUN apk update && apk add bash && mkdir -p ${PROJECT_DIR}
COPY . ${PROJECT_DIR}
WORKDIR ${PROJECT_DIR}
RUN ./build/frameworkcontroller/go-build.sh
WORKDIR ./dist/frameworkcontroller
ENTRYPOINT ["./start.sh"]

Просмотреть файл

@ -0,0 +1,37 @@
#!/bin/bash
# MIT License
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# 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
set -o errexit
set -o nounset
set -o pipefail
BASH_DIR=$(cd $(dirname ${BASH_SOURCE}) && pwd)
PROJECT_DIR=${BASH_DIR}/../..
IMAGE_NAME=frameworkcontroller
cd ${PROJECT_DIR}
docker build -t ${IMAGE_NAME} -f ${BASH_DIR}/Dockerfile .
echo Succeeded to build docker image ${IMAGE_NAME}

Просмотреть файл

@ -0,0 +1,45 @@
#!/bin/bash
# MIT License
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# 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
set -o errexit
set -o nounset
set -o pipefail
BASH_DIR=$(cd $(dirname ${BASH_SOURCE}) && pwd)
# Ensure ${PROJECT_DIR} is ${GOPATH}/src/github.com/microsoft/frameworkcontroller
PROJECT_DIR=${BASH_DIR}/../..
DIST_DIR=${PROJECT_DIR}/dist/frameworkcontroller
cd ${PROJECT_DIR}
rm -rf ${DIST_DIR}
mkdir -p ${DIST_DIR}
go build -o ${DIST_DIR}/frameworkcontroller cmd/frameworkcontroller/*
chmod a+x ${DIST_DIR}/frameworkcontroller
cp -r bin/frameworkcontroller/* ${DIST_DIR}
cp -r example/config/default/* ${DIST_DIR}
echo Succeeded to build binary distribution into ${DIST_DIR}:
cd ${DIST_DIR} && ls -lR .

Просмотреть файл

@ -0,0 +1,47 @@
// MIT License
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// 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
package main
import (
"os"
"os/signal"
"syscall"
"github.com/microsoft/frameworkcontroller/pkg/common"
"github.com/microsoft/frameworkcontroller/pkg/controller"
)
func init() {
common.InitAll()
}
func main() {
stopCh := make(chan struct{})
defer close(stopCh)
go controller.NewQueueFrameworkController().Run(stopCh)
sigTerm := make(chan os.Signal, 1)
signal.Notify(sigTerm, syscall.SIGTERM)
signal.Notify(sigTerm, syscall.SIGINT)
<-sigTerm
}

143
doc/architecture.svg Normal file
Просмотреть файл

@ -0,0 +1,143 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
width="8.5in" height="11in" viewBox="0 0 612 792" xml:space="preserve" color-interpolation-filters="sRGB" class="st15">
<style type="text/css">
<![CDATA[
.st1 {marker-end:url(#mrkr1-6);stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:1}
.st2 {fill:#000000;fill-opacity:1;stroke:#000000;stroke-opacity:1;stroke-width:0.28409090909091}
.st3 {fill:#ffffff;stroke:none;stroke-linecap:butt;stroke-width:7.2}
.st4 {fill:#000000;font-family:Calibri;font-size:0.666664em}
.st5 {fill:#feffff;stroke:#000000;stroke-linecap:butt;stroke-width:1}
.st6 {fill:#000000;font-family:Calibri;font-size:0.666664em;font-weight:bold}
.st7 {fill:#ffffff;stroke:#000000;stroke-dasharray:3,3;stroke-linecap:round;stroke-linejoin:round;stroke-width:1}
.st8 {marker-end:url(#mrkr1-6);stroke:#000000;stroke-dasharray:3,3;stroke-linecap:round;stroke-linejoin:round;stroke-width:1}
.st9 {visibility:visible}
.st10 {fill:#5b9bd5;fill-opacity:0.22;filter:url(#filter_2);stroke:#5b9bd5;stroke-opacity:0.22}
.st11 {fill:#7ddbff;stroke:#000000;stroke-width:1}
.st12 {fill:#ffffff;stroke:#000000;stroke-width:1}
.st13 {fill:#000000;font-family:Calibri;font-size:0.666664em;font-style:italic;font-weight:bold}
.st14 {font-size:1em;font-style:normal}
.st15 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
]]>
</style>
<defs id="Markers">
<g id="lend1">
<path d="M 1 -1 L 0 0 L 1 1 " style="stroke-linecap:round;stroke-linejoin:round;fill:none"/>
</g>
<marker id="mrkr1-6" class="st2" orient="auto" markerUnits="strokeWidth" overflow="visible">
<use xlink:href="#lend1" transform="scale(-3.52,-3.52) "/>
</marker>
</defs>
<defs id="Filters">
<filter id="filter_2">
<feGaussianBlur stdDeviation="2"/>
</filter>
</defs>
<g>
<title>Page-1</title>
<g id="shape64-1" transform="translate(266.062,-541.25)">
<title>Dynamic connector</title>
<desc>3/6</desc>
<path d="M0.56 792 L17.44 792 L17.44 734.37" class="st1"/>
<rect x="11.8378" y="766.825" width="11.1995" height="9.59985" class="st3"/>
<text x="11.84" y="774.03" class="st4">3/6</text> </g>
<g id="shape93-9" transform="translate(230.062,-610.75)">
<title>Dynamic connector.99</title>
<desc>2/5/8</desc>
<path d="M-0.56 792 L-17.44 792 L-17.44 849.63" class="st1"/>
<rect x="-26.6097" y="807.575" width="18.3441" height="9.59985" class="st3"/>
<text x="-26.61" y="814.78" class="st4">2/5/8</text> </g>
<g id="shape97-16" transform="translate(229.5,-598.875)">
<title>External interactor.138</title>
<desc>KubernetesApiServer</desc>
<rect x="0" y="768.25" width="108" height="23.75" class="st5"/>
<text x="18.72" y="782.52" class="st6">KubernetesApiServer</text> </g>
<g id="shape106-19" transform="translate(229.5,-668.375)">
<title>External interactor.106</title>
<desc>KubernetesClient</desc>
<path d="M0 792 L108 792 L108 768.25 L0 768.25 L0 792 Z" class="st7"/>
<text x="25.3" y="782.52" class="st6">KubernetesClient</text> </g>
<g id="shape107-22" transform="translate(274.5,-668.375)">
<title>Dynamic connector.107</title>
<desc>0</desc>
<path d="M9 792 L9 837.75" class="st1"/>
<rect x="6.97253" y="810.075" width="4.05478" height="9.59985" class="st3"/>
<text x="6.97" y="817.28" class="st4">0</text> </g>
<g id="shape111-29" transform="translate(66.6,-483.625)">
<title>Dynamic connector.111</title>
<path d="M9 792 L9 746.25" class="st8"/>
</g>
<g id="shape112-34" transform="translate(212.625,-483.625)">
<title>Dynamic connector.112</title>
<path d="M0 792 L0 774 L-137.02 774 L-137.02 746.25" class="st8"/>
</g>
<g id="shape113-39" transform="translate(337.5,-610.75)">
<title>Dynamic connector.113</title>
<path d="M0 792 L18 792 L18 960.88 L-261.9 960.88 L-261.9 942.88" class="st1"/>
</g>
<g id="shape114-44" transform="translate(337.5,-610.75)">
<title>Dynamic connector.114</title>
<desc>7</desc>
<path d="M0 792 L18 792 L18 960.88 L-124.88 960.88 L-124.88 942.88" class="st1"/>
<rect x="15.9725" y="943.075" width="4.05478" height="9.59985" class="st3"/>
<text x="15.97" y="950.28" class="st4">7</text> </g>
<g id="shape128-51" transform="translate(158.625,-529.375)">
<title>External interactor.128</title>
<desc>FrameworkController</desc>
<g id="shadow128-52" transform="matrix(1,0,0,1,0.345598,1.97279)" class="st9">
<rect x="0" y="768.25" width="108" height="23.75" class="st10"/>
</g>
<rect x="0" y="768.25" width="108" height="23.75" class="st11"/>
<text x="18.25" y="782.52" class="st6">FrameworkController</text> </g>
<g id="shape130-57" transform="translate(158.625,-459.875)">
<title>External interactor.130</title>
<desc>[PodObject] TaskAttemptInstance</desc>
<g id="shadow130-58" transform="matrix(1,0,0,1,0.345598,1.97279)" class="st9">
<rect x="0" y="768.25" width="108" height="23.75" rx="11.875" ry="11.875" class="st10"/>
</g>
<rect x="0" y="768.25" width="108" height="23.75" rx="11.875" ry="11.875" class="st12"/>
<text x="34.25" y="777.72" class="st13">[PodObject] <tspan x="18.53" dy="1.2em" class="st14">TaskAttemptInstance</tspan></text> </g>
<g id="shape133-64" transform="translate(21.6,-459.875)">
<title>External interactor.133</title>
<desc>[PodObject] TaskAttemptInstance</desc>
<g id="shadow133-65" transform="matrix(1,0,0,1,0.345598,1.97279)" class="st9">
<rect x="0" y="768.25" width="108" height="23.75" rx="11.875" ry="11.875" class="st10"/>
</g>
<rect x="0" y="768.25" width="108" height="23.75" rx="11.875" ry="11.875" class="st12"/>
<text x="34.25" y="777.72" class="st13">[PodObject] <tspan x="18.53" dy="1.2em" class="st14">TaskAttemptInstance</tspan></text> </g>
<g id="shape137-71" transform="translate(229.5,-601.75)">
<title>Dynamic connector.137</title>
<desc>1</desc>
<path d="M0 783 L-99.9 783" class="st1"/>
<rect x="-51.9775" y="778.2" width="4.05478" height="9.59985" class="st3"/>
<text x="-51.98" y="785.4" class="st4">1</text> </g>
<g id="shape144-78" transform="translate(229.5,-610.75)">
<title>Dynamic connector.144</title>
<desc>4</desc>
<path d="M0 792 L-40.5 792 L-40.5 824.5 L-143.1 824.5 L-143.1 849.63" class="st1"/>
<rect x="-69.89" y="819.7" width="4.05478" height="9.59985" class="st3"/>
<text x="-69.89" y="826.9" class="st4">4</text> </g>
<g id="shape147-85" transform="translate(21.6,-529.375)">
<title>External interactor.147</title>
<desc>[ConfigMapObject] FrameworkAttemptInstance</desc>
<g id="shadow147-86" transform="matrix(1,0,0,1,0.345598,1.97279)" class="st9">
<rect x="0" y="768.25" width="108" height="23.75" rx="11.875" ry="11.875" class="st10"/>
</g>
<rect x="0" y="768.25" width="108" height="23.75" rx="11.875" ry="11.875" class="st12"/>
<text x="22.23" y="777.72" class="st13">[ConfigMapObject] <tspan x="7.03" dy="1.2em" class="st14">FrameworkAttemptInstance</tspan></text> </g>
<g id="shape149-92" transform="translate(21.6,-598.875)">
<title>External interactor.149</title>
<desc>[FrameworkObject] FrameworkInstance</desc>
<g id="shadow149-93" transform="matrix(1,0,0,1,0.345598,1.97279)" class="st9">
<rect x="0" y="768.25" width="108" height="23.75" rx="11.875" ry="11.875" class="st10"/>
</g>
<rect x="0" y="768.25" width="108" height="23.75" rx="11.875" ry="11.875" class="st12"/>
<text x="21.64" y="777.72" class="st13">[FrameworkObject] <tspan x="21.02" dy="1.2em" class="st14">FrameworkInstance</tspan></text> </g>
<g id="shape151-99" transform="translate(66.6,-553.125)">
<title>Dynamic connector.151</title>
<path d="M9 792 L9 746.25" class="st8"/>
</g>
</g>
</svg>

После

Ширина:  |  Высота:  |  Размер: 7.8 KiB

Просмотреть файл

@ -0,0 +1,22 @@
# <a name="KnownIssueAndUpcomingFeature">Known Issue and Upcoming Feature</a>
## <a name="Index">Index</a>
- [Internal Known Issue](#InternalKnownIssue)
- [External Known Issue](#ExternalKnownIssue)
- [Upcoming Feature](#UpcomingFeature)
## <a name="InternalKnownIssue">Internal Known Issue</a>
## <a name="ExternalKnownIssue">External Known Issue</a>
- [ ] Kubernetes Dashboard: Pod Detail Page: "Unknown reference kind ConfigMap".
Tracked in [Dashboard errors if pod's owner reference is not supported](https://github.com/kubernetes/dashboard/issues/3251)
## <a name="UpcomingFeature">Upcoming Feature</a>
- [ ] Add Distributed TensorFlow Training Example
- [ ] Support Framework Spec Update
- [ ] Support Framework Spec Validation and Defaulting
- [ ] Support Framework Status Subresource
- [ ] Support Framework CompletedRetainSec
- [ ] Add AttemptCreating state to move the object initialization time out of the ObjectLocalCacheCreationTimeoutSec
- [ ] Support Framework History Server

40
doc/user-manual.md Normal file
Просмотреть файл

@ -0,0 +1,40 @@
# <a name="UserManual">User Manual</a>
## <a name="Index">Index</a>
- [Architecture](#Architecture)
- [Framework Interop](#FrameworkInterop)
- [Container EnvironmentVariable](#ContainerEnvironmentVariable)
- [CompletionCode Convention](#CompletionCodeConvention)
- [RetryPolicy](#RetryPolicy)
- [FrameworkAttemptCompletionPolicy](#FrameworkAttemptCompletionPolicy)
- [Best Practice](#BestPractice)
## <a name="FrameworkInterop">Architecture</a>
<p style="text-align: left;">
<img src="architecture.svg" title="Architecture" alt="Architecture" width="150%"/>
</p>
## <a name="FrameworkInterop">Framework Interop</a>
Supported interoperations with a Framework
| API Kind | Operations |
|:---- |:---- |
| Framework | [POST](/apis/frameworkcontroller.microsoft.com/v1/namespaces/{namespace}/frameworks) [GET](/apis/frameworkcontroller.microsoft.com/v1/namespaces/{namespace}/frameworks/{name}) [DELETE](/apis/frameworkcontroller.microsoft.com/v1/namespaces/{namespace}/frameworks/{name}) |
| ConfigMap | GET DELETE |
| Pod | GET DELETE |
## <a name="ContainerEnvironmentVariable">Container EnvironmentVariable</a>
[Container EnvironmentVariable](../pkg/apis/frameworkcontroller/v1/constants.go)
## <a name="CompletionCodeConvention">CompletionCode Convention</a>
[CompletionCode Convention](../pkg/apis/frameworkcontroller/v1/types.go)
## <a name="RetryPolicy">RetryPolicy</a>
[RetryPolicy](../pkg/apis/frameworkcontroller/v1/types.go)
## <a name="FrameworkAttemptCompletionPolicy">FrameworkAttemptCompletionPolicy</a>
[FrameworkAttemptCompletionPolicy](../pkg/apis/frameworkcontroller/v1/types.go)
## <a name="BestPractice">Best Practice</a>
[Best Practice](../pkg/apis/frameworkcontroller/v1/types.go)

Просмотреть файл

@ -0,0 +1,18 @@
# Put it directly under frameworkcontroller's current working directory.
# For the full config setting and usage, see ./pkg/apis/frameworkcontroller/v1/config.go
# This is the default config for frameworkcontroller, so all settings are commented out.
# Setup k8s config:
# kubeApiServerAddress is default to ${KUBE_APISERVER_ADDRESS} and kubeConfigFilePath
# is default to ${KUBECONFIG} then falls back to ${HOME}/.kube/config.
# If both kubeApiServerAddress and kubeConfigFilePath after defaulting are still empty,
# falls back to k8s inClusterConfig.
#
# Address should be in format http[s]://host:port
#kubeApiServerAddress: http://10.10.10.10:8080
#
# See https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#config
#kubeConfigFilePath: ""
#workerNumber: 20

Просмотреть файл

@ -0,0 +1,34 @@
# Post to {kubeApiServerAddress}/apis/frameworkcontroller.microsoft.com/v1/namespaces/default/frameworks
# For the full spec setting and usage, see ./pkg/apis/frameworkcontroller/v1/types.go
apiVersion: frameworkcontroller.microsoft.com/v1
kind: Framework
metadata:
name: batchfailedpermanent
spec:
executionType: Start
retryPolicy:
fancyRetryPolicy: true
maxRetryCount: 1
taskRoles:
- name: worker
taskNumber: 1
frameworkAttemptCompletionPolicy:
minFailedTaskCount: 1
minSucceededTaskCount: -1
task:
retryPolicy:
fancyRetryPolicy: true
maxRetryCount: 1
pod:
spec:
restartPolicy: Never
containers:
- name: ubuntu
image: ubuntu:trusty
# See CompletionCode Convention in
# ./pkg/apis/frameworkcontroller/v1/constants.go
command: [
"sh", "-c",
"sleep 10 &&
echo exit with permanent failure to tell controller not to retry &&
exit 210"]

Просмотреть файл

@ -0,0 +1,34 @@
# Post to {kubeApiServerAddress}/apis/frameworkcontroller.microsoft.com/v1/namespaces/default/frameworks
# For the full spec setting and usage, see ./pkg/apis/frameworkcontroller/v1/types.go
apiVersion: frameworkcontroller.microsoft.com/v1
kind: Framework
metadata:
name: batchfailedtransient
spec:
executionType: Start
retryPolicy:
fancyRetryPolicy: true
maxRetryCount: 1
taskRoles:
- name: worker
taskNumber: 1
frameworkAttemptCompletionPolicy:
minFailedTaskCount: 1
minSucceededTaskCount: -1
task:
retryPolicy:
fancyRetryPolicy: true
maxRetryCount: 1
pod:
spec:
restartPolicy: Never
containers:
- name: ubuntu
image: ubuntu:trusty
# See CompletionCode Convention in
# ./pkg/apis/frameworkcontroller/v1/constants.go
command: [
"sh", "-c",
"sleep 10 &&
echo exit with transient failure to tell controller to retry &&
exit 200"]

Просмотреть файл

@ -0,0 +1,34 @@
# Post to {kubeApiServerAddress}/apis/frameworkcontroller.microsoft.com/v1/namespaces/default/frameworks
# For the full spec setting and usage, see ./pkg/apis/frameworkcontroller/v1/types.go
apiVersion: frameworkcontroller.microsoft.com/v1
kind: Framework
metadata:
name: batchfailedtransientconflict
spec:
executionType: Start
retryPolicy:
fancyRetryPolicy: true
maxRetryCount: 1
taskRoles:
- name: worker
taskNumber: 1
frameworkAttemptCompletionPolicy:
minFailedTaskCount: 1
minSucceededTaskCount: -1
task:
retryPolicy:
fancyRetryPolicy: false
maxRetryCount: 0
pod:
spec:
restartPolicy: Never
containers:
- name: ubuntu
image: ubuntu:trusty
# See CompletionCode Convention in
# ./pkg/apis/frameworkcontroller/v1/constants.go
command: [
"sh", "-c",
"sleep 10 &&
echo exit with transient conflict failure tell controller to back off retry &&
exit 201"]

Просмотреть файл

@ -0,0 +1,34 @@
# Post to {kubeApiServerAddress}/apis/frameworkcontroller.microsoft.com/v1/namespaces/default/frameworks
# For the full spec setting and usage, see ./pkg/apis/frameworkcontroller/v1/types.go
apiVersion: frameworkcontroller.microsoft.com/v1
kind: Framework
metadata:
name: batchfailedunknown
spec:
executionType: Start
retryPolicy:
fancyRetryPolicy: true
maxRetryCount: 1
taskRoles:
- name: worker
taskNumber: 1
frameworkAttemptCompletionPolicy:
minFailedTaskCount: 1
minSucceededTaskCount: -1
task:
retryPolicy:
fancyRetryPolicy: true
maxRetryCount: 1
pod:
spec:
restartPolicy: Never
containers:
- name: ubuntu
image: ubuntu:trusty
# See CompletionCode Convention in
# ./pkg/apis/frameworkcontroller/v1/constants.go
command: [
"sh", "-c",
"sleep 10 &&
echo exit with unknown failure to tell controller to retry within maxRetryCount &&
exit 1"]

Просмотреть файл

@ -0,0 +1,45 @@
# Post to {kubeApiServerAddress}/apis/frameworkcontroller.microsoft.com/v1/namespaces/default/frameworks
# For the full spec setting and usage, see ./pkg/apis/frameworkcontroller/v1/types.go
apiVersion: frameworkcontroller.microsoft.com/v1
kind: Framework
metadata:
name: batchstatefulfailed
spec:
executionType: Start
retryPolicy:
fancyRetryPolicy: true
maxRetryCount: 1
taskRoles:
- name: worker
taskNumber: 3
frameworkAttemptCompletionPolicy:
minFailedTaskCount: 1
minSucceededTaskCount: -1
task:
retryPolicy:
fancyRetryPolicy: true
maxRetryCount: 1
pod:
spec:
restartPolicy: Never
containers:
- name: ubuntu
image: ubuntu:trusty
# To locate a specific Task during its whole lifecycle regardless of
# any retry:
# Consistent Identity:
# PodName = {FrameworkName}-{TaskRoleName}-{TaskIndex}
# PodNamespace = {FrameworkNamespace}
# Consistent Environment Variable Value:
# ${FRAMEWORK_NAME}, ${TASKROLE_NAME}, ${TASK_INDEX}
# ${CONFIGMAP_NAME}, ${POD_NAME}, ${POD_NAMESPACE}
#
# To locate a specific execution attempt of a specific Task:
# Attempt Specific Environment Variable Value:
# ${FRAMEWORK_ATTEMPT_ID}, ${TASK_ATTEMPT_ID}
#
# To locate a specific execution attempt instance of a specific Task:
# Attempt Instance Specific Environment Variable Value:
# ${FRAMEWORK_ATTEMPT_INSTANCE_UID}, ${CONFIGMAP_UID}
# ${TASK_ATTEMPT_INSTANCE_UID}, ${POD_UID}
command: ["sh", "-c", "printenv && sleep 60 && exit 1"]

Просмотреть файл

@ -0,0 +1,28 @@
# Post to {kubeApiServerAddress}/apis/frameworkcontroller.microsoft.com/v1/namespaces/default/frameworks
# For the full spec setting and usage, see ./pkg/apis/frameworkcontroller/v1/types.go
apiVersion: frameworkcontroller.microsoft.com/v1
kind: Framework
metadata:
name: batchsucceeded
spec:
executionType: Start
retryPolicy:
fancyRetryPolicy: true
maxRetryCount: 1
taskRoles:
- name: worker
taskNumber: 1
frameworkAttemptCompletionPolicy:
minFailedTaskCount: 1
minSucceededTaskCount: -1
task:
retryPolicy:
fancyRetryPolicy: true
maxRetryCount: 1
pod:
spec:
restartPolicy: Never
containers:
- name: ubuntu
image: ubuntu:trusty
command: ["sh", "-c", "sleep 10 && printenv"]

Просмотреть файл

@ -0,0 +1,48 @@
# Post to {kubeApiServerAddress}/apis/frameworkcontroller.microsoft.com/v1/namespaces/default/frameworks
# For the full spec setting and usage, see ./pkg/apis/frameworkcontroller/v1/types.go
apiVersion: frameworkcontroller.microsoft.com/v1
kind: Framework
metadata:
name: batchwithservicesucceeded
spec:
executionType: Start
retryPolicy:
fancyRetryPolicy: true
maxRetryCount: 1
taskRoles:
- name: server
taskNumber: 2
frameworkAttemptCompletionPolicy:
minFailedTaskCount: 1
minSucceededTaskCount: -1
task:
retryPolicy:
fancyRetryPolicy: false
maxRetryCount: 0
pod:
spec:
restartPolicy: Never
containers:
- name: nginx
image: nginx:stable
ports:
- containerPort: 80
- name: worker
taskNumber: 3
frameworkAttemptCompletionPolicy:
minFailedTaskCount: 1
minSucceededTaskCount: 3
task:
retryPolicy:
fancyRetryPolicy: false
maxRetryCount: 0
pod:
spec:
restartPolicy: Never
containers:
- name: ubuntu
image: ubuntu:trusty
# The communication from worker to server is ignored in this
# example, however, in a real case, worker usually needs the
# help from server to make progress.
command: ["sh", "-c", "sleep 10 && printenv"]

Просмотреть файл

@ -0,0 +1,48 @@
# Post to {kubeApiServerAddress}/apis/frameworkcontroller.microsoft.com/v1/namespaces/default/frameworks
# For the full spec setting and usage, see ./pkg/apis/frameworkcontroller/v1/types.go
apiVersion: frameworkcontroller.microsoft.com/v1
kind: Framework
metadata:
name: service
spec:
executionType: Start
retryPolicy:
fancyRetryPolicy: false
maxRetryCount: -1
taskRoles:
- name: server
taskNumber: 1
frameworkAttemptCompletionPolicy:
minFailedTaskCount: 1
minSucceededTaskCount: -1
task:
retryPolicy:
fancyRetryPolicy: false
maxRetryCount: -1
pod:
#metadata:
# labels:
# app: server
spec:
restartPolicy: Always
containers:
- name: nginx
image: nginx:stable
ports:
- containerPort: 80
---
# Post to {kubeApiServerAddress}/api/v1/namespaces/default/services/
apiVersion: v1
kind: Service
metadata:
name: server
spec:
selector:
# Using predefined labels
FRAMEWORK_NAME: service
TASKROLE_NAME: server
# Also can use customized labels
#app: server
ports:
- port: 80
type: NodePort

Просмотреть файл

@ -0,0 +1,51 @@
# Post to {kubeApiServerAddress}/apis/frameworkcontroller.microsoft.com/v1/namespaces/default/frameworks
# For the full spec setting and usage, see ./pkg/apis/frameworkcontroller/v1/types.go
apiVersion: frameworkcontroller.microsoft.com/v1
kind: Framework
metadata:
name: servicestateful
spec:
executionType: Start
retryPolicy:
fancyRetryPolicy: false
maxRetryCount: -1
taskRoles:
- name: serverstateful
taskNumber: 3
frameworkAttemptCompletionPolicy:
minFailedTaskCount: 1
minSucceededTaskCount: -1
task:
retryPolicy:
fancyRetryPolicy: false
maxRetryCount: -1
pod:
spec:
# Using Never restartPolicy, ActiveDeadlineSeconds and initContainers
# just to demonstrate consistent identities in this example.
restartPolicy: Never
ActiveDeadlineSeconds: 60
containers:
- name: nginx
image: nginx:stable
ports:
- containerPort: 80
initContainers:
- name: ubuntu
image: ubuntu:trusty
# See comments in batchstatefulfailed.yaml
command: ["sh", "-c", "printenv"]
---
# Post to {kubeApiServerAddress}/api/v1/namespaces/default/services/
apiVersion: v1
kind: Service
metadata:
name: serverstateful
spec:
selector:
# See comments in service.yaml
FRAMEWORK_NAME: servicestateful
TASKROLE_NAME: serverstateful
ports:
- port: 80
type: NodePort

Просмотреть файл

@ -0,0 +1,7 @@
### Run FrameworkController by a Docker Container
Note at most one instance of FrameworkController can be run for a single k8s cluster.
```shell
docker run -e KUBE_APISERVER_ADDRESS={http[s]://host:port} frameworkcontroller
```

Просмотреть файл

@ -0,0 +1,36 @@
### Run FrameworkController by a Kubernetes StatefulSet
Note at most one instance of FrameworkController can be run for a single Kubernetes cluster.
```shell
kubectl create -f frameworkcontroller.yaml
```
frameworkcontroller.yaml:
```yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: frameworkcontroller
spec:
serviceName: frameworkcontroller
selector:
matchLabels:
app: frameworkcontroller
replicas: 1
template:
metadata:
labels:
app: frameworkcontroller
spec:
containers:
- name: frameworkcontroller
# Using local image just to demonstrate this example.
imagePullPolicy: Never
image: frameworkcontroller
env:
# May not need to specify KUBE_APISERVER_ADDRESS if the target
# cluster to control is the cluster running the StatefulSet.
- name: KUBE_APISERVER_ADDRESS
value: {http[s]://host:port}
```

Просмотреть файл

@ -0,0 +1,7 @@
### Run FrameworkController by a OS Process
Note at most one instance of FrameworkController can be run for a single k8s cluster.
```shell
export KUBE_APISERVER_ADDRESS={http[s]://host:port} && ./dist/frameworkcontroller/start.sh
```

40
hack/update-codegen.sh Executable file
Просмотреть файл

@ -0,0 +1,40 @@
#!/bin/bash
# MIT License
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# 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
set -o errexit
set -o nounset
set -o pipefail
BASH_DIR=$(cd $(dirname ${BASH_SOURCE}) && pwd)
PROJECT_DIR=${BASH_DIR}/..
cd ${PROJECT_DIR}
./vendor/k8s.io/code-generator/generate-groups.sh \
"all" \
github.com/microsoft/frameworkcontroller/pkg/client \
github.com/microsoft/frameworkcontroller/pkg/apis \
frameworkcontroller:v1
echo Succeeded to generate k8s CRD client code

36
hack/update-dep.sh Executable file
Просмотреть файл

@ -0,0 +1,36 @@
#!/bin/bash
# MIT License
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# 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
set -o errexit
set -o nounset
set -o pipefail
BASH_DIR=$(cd $(dirname ${BASH_SOURCE}) && pwd)
PROJECT_DIR=${BASH_DIR}/..
cd ${PROJECT_DIR}
dep ensure
echo Succeeded to update dependent package

41
hack/update-push-codegen.sh Executable file
Просмотреть файл

@ -0,0 +1,41 @@
#!/bin/bash
# MIT License
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# 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
set -o errexit
set -o nounset
set -o pipefail
BASH_DIR=$(cd $(dirname ${BASH_SOURCE}) && pwd)
PROJECT_DIR=${BASH_DIR}/..
cd ${PROJECT_DIR}
git pull
${BASH_DIR}/update-codegen.sh
git add --all
git status
read -rsp $'Press enter to continue...\n'
git commit -m "Update codegen"
git push

41
hack/update-push-dep.sh Executable file
Просмотреть файл

@ -0,0 +1,41 @@
#!/bin/bash
# MIT License
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# 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
set -o errexit
set -o nounset
set -o pipefail
BASH_DIR=$(cd $(dirname ${BASH_SOURCE}) && pwd)
PROJECT_DIR=${BASH_DIR}/..
cd ${PROJECT_DIR}
git pull
${BASH_DIR}/update-dep.sh
git add --all
git status
read -rsp $'Press enter to continue...\n'
git commit -m "Update dep"
git push

Просмотреть файл

@ -0,0 +1,160 @@
// MIT License
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// 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
package v1
import (
"os"
"fmt"
"io/ioutil"
"github.com/microsoft/frameworkcontroller/pkg/common"
)
type ControllerConfig struct {
// If both kubeApiServerAddress and kubeConfigFilePath after defaulting are still
// empty, falls back to k8s inClusterConfig.
// Address should be in format http[s]://host:port
KubeApiServerAddress *string `yaml:"kubeApiServerAddress"`
// See https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#config
KubeConfigFilePath *string `yaml:"kubeConfigFilePath"`
// Number of concurrent workers to process each different Frameworks
WorkerNumber *int32 `yaml:"workerNumber"`
// Check interval and timeout to expect the created CRD to be in Established condition.
CRDEstablishedCheckIntervalSec *int64 `yaml:"crdEstablishedCheckIntervalSec"`
CRDEstablishedCheckTimeoutSec *int64 `yaml:"crdEstablishedCheckTimeoutSec"`
// Timeout to expect the created object in ApiServer also appears in the local
// cache of the Controller's Informer.
// If the created object does not appear in the local cache within the timeout,
// it is considered as deleted.
ObjectLocalCacheCreationTimeoutSec *int64 `yaml:"objectLocalCacheCreationTimeoutSec"`
// If the Framework FancyRetryPolicy is enabled and its FrameworkAttempt is
// completed with Transient Conflict Failed CompletionType, it will be retried
// after a random delay within this range.
// This helps to avoid the resource deadlock for Framework which needs
// Gang Scheduling (Gang Allocation), i.e. all Tasks in the Framework should
// be run in an all-or-nothing fashion in order to perform any useful work.
FrameworkMinRetryDelaySecForTransientConflictFailed *int64 `yaml:"frameworkMinRetryDelaySecForTransientConflictFailed"`
FrameworkMaxRetryDelaySecForTransientConflictFailed *int64 `yaml:"frameworkMaxRetryDelaySecForTransientConflictFailed"`
}
func NewControllerConfig() *ControllerConfig {
c := initControllerConfig()
// Defaulting
if c.KubeApiServerAddress == nil {
c.KubeApiServerAddress = common.PtrString(os.Getenv("KUBE_APISERVER_ADDRESS"))
}
if c.KubeConfigFilePath == nil {
c.KubeConfigFilePath = defaultKubeConfigFilePath()
}
if c.WorkerNumber == nil {
c.WorkerNumber = common.PtrInt32(10)
}
if c.CRDEstablishedCheckIntervalSec == nil {
c.CRDEstablishedCheckIntervalSec = common.PtrInt64(1)
}
if c.CRDEstablishedCheckTimeoutSec == nil {
c.CRDEstablishedCheckTimeoutSec = common.PtrInt64(60)
}
if c.ObjectLocalCacheCreationTimeoutSec == nil {
// Default to k8s.io/kubernetes/pkg/controller.ExpectationsTimeout
c.ObjectLocalCacheCreationTimeoutSec = common.PtrInt64(5 * 60)
}
if c.FrameworkMinRetryDelaySecForTransientConflictFailed == nil {
c.FrameworkMinRetryDelaySecForTransientConflictFailed = common.PtrInt64(60)
}
if c.FrameworkMaxRetryDelaySecForTransientConflictFailed == nil {
c.FrameworkMaxRetryDelaySecForTransientConflictFailed = common.PtrInt64(15 * 60)
}
// Validation
errPrefix := "ControllerConfig Validation Failed: "
if *c.WorkerNumber <= 0 {
panic(fmt.Errorf(errPrefix+
"WorkerNumber %v should be positive",
*c.WorkerNumber))
}
if *c.CRDEstablishedCheckIntervalSec < 1 {
panic(fmt.Errorf(errPrefix+
"CRDEstablishedCheckIntervalSec %v should not be less than 1",
*c.CRDEstablishedCheckIntervalSec))
}
if *c.CRDEstablishedCheckTimeoutSec < 10 {
panic(fmt.Errorf(errPrefix+
"CRDEstablishedCheckTimeoutSec %v should not be less than 10",
*c.CRDEstablishedCheckTimeoutSec))
}
if *c.ObjectLocalCacheCreationTimeoutSec < 60 {
panic(fmt.Errorf(errPrefix+
"ObjectLocalCacheCreationTimeoutSec %v should not be less than 60",
*c.ObjectLocalCacheCreationTimeoutSec))
}
if *c.FrameworkMinRetryDelaySecForTransientConflictFailed < 0 {
panic(fmt.Errorf(errPrefix+
"FrameworkMinRetryDelaySecForTransientConflictFailed %v should not be negative",
*c.FrameworkMinRetryDelaySecForTransientConflictFailed))
}
if *c.FrameworkMaxRetryDelaySecForTransientConflictFailed <
*c.FrameworkMinRetryDelaySecForTransientConflictFailed {
panic(fmt.Errorf(errPrefix+
"FrameworkMaxRetryDelaySecForTransientConflictFailed %v should not be less than "+
"FrameworkMinRetryDelaySecForTransientConflictFailed %v",
*c.FrameworkMaxRetryDelaySecForTransientConflictFailed,
*c.FrameworkMinRetryDelaySecForTransientConflictFailed))
}
return c
}
func defaultKubeConfigFilePath() *string {
configPath := os.Getenv("KUBECONFIG")
_, err := os.Stat(configPath)
if err == nil {
return &configPath
}
configPath = os.Getenv("HOME") + "/.kube/config"
_, err = os.Stat(configPath)
if err == nil {
return &configPath
}
configPath = ""
return &configPath
}
func initControllerConfig() *ControllerConfig {
c := ControllerConfig{}
yamlBytes, err := ioutil.ReadFile(ControllerConfigFile)
if err != nil {
panic(fmt.Errorf(
"Failed to Read ControllerConfigFile: %v, %v", ControllerConfigFile, err))
}
common.FromYaml(string(yamlBytes), &c)
return &c
}

Просмотреть файл

@ -0,0 +1,180 @@
// MIT License
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// 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
package v1
import (
core "k8s.io/api/core/v1"
)
///////////////////////////////////////////////////////////////////////////////////////
// General Constants
///////////////////////////////////////////////////////////////////////////////////////
const (
GroupName = "frameworkcontroller.microsoft.com"
Version = "v1"
FrameworkPlural = "frameworks"
FrameworkCRDName = FrameworkPlural + "." + GroupName
FrameworkKind = "Framework"
ConfigMapKind = "ConfigMap"
ControllerName = "frameworkcontroller"
ObjectUIDFieldPath = "metadata.uid"
ControllerConfigFile = "./frameworkcontroller.yaml"
UnlimitedValue = -1
ExtendedUnlimitedValue = -2
// For all managed objects
AnnotationKeyFrameworkName = "FRAMEWORK_NAME"
AnnotationKeyTaskRoleName = "TASKROLE_NAME"
AnnotationKeyTaskIndex = "TASK_INDEX"
AnnotationKeyConfigMapName = "CONFIGMAP_NAME"
AnnotationKeyPodName = "POD_NAME"
AnnotationKeyPodNamespace = "POD_NAMESPACE"
AnnotationKeyFrameworkAttemptID = "FRAMEWORK_ATTEMPT_ID"
AnnotationKeyFrameworkAttemptInstanceUID = "FRAMEWORK_ATTEMPT_INSTANCE_UID"
AnnotationKeyConfigMapUID = "CONFIGMAP_UID"
AnnotationKeyTaskAttemptID = "TASK_ATTEMPT_ID"
LabelKeyFrameworkName = AnnotationKeyFrameworkName
LabelKeyTaskRoleName = AnnotationKeyTaskRoleName
// For all managed containers
EnvNameFrameworkName = AnnotationKeyFrameworkName
EnvNameTaskRoleName = AnnotationKeyTaskRoleName
EnvNameTaskIndex = AnnotationKeyTaskIndex
EnvNameConfigMapName = AnnotationKeyConfigMapName
EnvNamePodName = AnnotationKeyPodName
EnvNamePodNamespace = AnnotationKeyPodNamespace
EnvNameFrameworkAttemptID = AnnotationKeyFrameworkAttemptID
EnvNameFrameworkAttemptInstanceUID = AnnotationKeyFrameworkAttemptInstanceUID
EnvNameConfigMapUID = AnnotationKeyConfigMapUID
EnvNameTaskAttemptID = AnnotationKeyTaskAttemptID
EnvNameTaskAttemptInstanceUID = "TASK_ATTEMPT_INSTANCE_UID"
EnvNamePodUID = "POD_UID"
)
var FrameworkGroupVersionKind = SchemeGroupVersion.WithKind(FrameworkKind)
var ConfigMapGroupVersionKind = core.SchemeGroupVersion.WithKind(ConfigMapKind)
var ObjectUIDEnvVarSource = &core.EnvVarSource{
FieldRef: &core.ObjectFieldSelector{FieldPath: ObjectUIDFieldPath},
}
///////////////////////////////////////////////////////////////////////////////////////
// CompletionCodeInfos
///////////////////////////////////////////////////////////////////////////////////////
type CompletionCodeInfo struct {
Phrase CompletionPhrase
Type CompletionType
}
type ContainerStateTerminatedReason string
const (
ReasonOOMKilled ContainerStateTerminatedReason = "OOMKilled"
)
// Defined according to the CompletionCode Convention.
// See CompletionStatus.
const (
// NonNegative:
// ExitCode of the Framework's Container
// [129, 165]: Container Received Fatal Error Signal: ExitCode - 128
CompletionCodeContainerSigTermReceived CompletionCode = 143
CompletionCodeContainerSigKillReceived CompletionCode = 137
CompletionCodeContainerSigIntReceived CompletionCode = 130
// [200, 219]: Container ExitCode Contract
CompletionCodeContainerTransientFailed CompletionCode = 200
CompletionCodeContainerTransientConflictFailed CompletionCode = 201
CompletionCodeContainerPermanentFailed CompletionCode = 210
// 0: Succeeded
CompletionCodeSucceeded CompletionCode = 0
// Negative:
// ExitCode of the Framework's Predefined Error
// -1XX: Framework Predefined Transient Error
CompletionCodeConfigMapExternalDeleted CompletionCode = -100
CompletionCodePodExternalDeleted CompletionCode = -101
CompletionCodeConfigMapCreationTimeout CompletionCode = -110
CompletionCodePodCreationTimeout CompletionCode = -111
CompletionCodePodFailedWithoutFailedContainer CompletionCode = -120
// -2XX: Framework Predefined Permanent Error
CompletionCodePodSpecInvalid CompletionCode = -200
// -3XX: Framework Predefined Unknown Error
CompletionCodeContainerOOMKilled CompletionCode = -300
)
var CompletionCodeInfos = map[CompletionCode]CompletionCodeInfo{
CompletionCodeContainerSigTermReceived: {
"ContainerSigTermReceived", CompletionType{CompletionTypeNameFailed,
[]CompletionTypeAttribute{CompletionTypeAttributeTransient, CompletionTypeAttributeExternal}}},
CompletionCodeContainerSigKillReceived: {
"ContainerSigKillReceived", CompletionType{CompletionTypeNameFailed,
[]CompletionTypeAttribute{CompletionTypeAttributeTransient, CompletionTypeAttributeExternal}}},
CompletionCodeContainerSigIntReceived: {
"ContainerSigIntReceived", CompletionType{CompletionTypeNameFailed,
[]CompletionTypeAttribute{CompletionTypeAttributeTransient, CompletionTypeAttributeExternal}}},
CompletionCodeContainerTransientFailed: {
"ContainerTransientFailed", CompletionType{CompletionTypeNameFailed,
[]CompletionTypeAttribute{CompletionTypeAttributeTransient, CompletionTypeAttributeInternal}}},
CompletionCodeContainerTransientConflictFailed: {
"ContainerTransientConflictFailed", CompletionType{CompletionTypeNameFailed,
[]CompletionTypeAttribute{CompletionTypeAttributeTransient, CompletionTypeAttributeInternal,
CompletionTypeAttributeConflict}}},
CompletionCodeContainerPermanentFailed: {
"ContainerPermanentFailed", CompletionType{CompletionTypeNameFailed,
[]CompletionTypeAttribute{CompletionTypeAttributePermanent, CompletionTypeAttributeInternal}}},
CompletionCodeSucceeded: {
"Succeeded", CompletionType{CompletionTypeNameSucceeded,
[]CompletionTypeAttribute{CompletionTypeAttributeInternal}}},
CompletionCodeConfigMapExternalDeleted: {
"ConfigMapExternalDeleted", CompletionType{CompletionTypeNameFailed,
[]CompletionTypeAttribute{CompletionTypeAttributeTransient, CompletionTypeAttributeExternal}}},
CompletionCodePodExternalDeleted: {
// Possibly be due to Pod Eviction.
"PodExternalDeleted", CompletionType{CompletionTypeNameFailed,
[]CompletionTypeAttribute{CompletionTypeAttributeTransient, CompletionTypeAttributeExternal}}},
CompletionCodeConfigMapCreationTimeout: {
"ConfigMapCreationTimeout", CompletionType{CompletionTypeNameFailed,
[]CompletionTypeAttribute{CompletionTypeAttributeTransient, CompletionTypeAttributeExternal}}},
CompletionCodePodCreationTimeout: {
"PodCreationTimeout", CompletionType{CompletionTypeNameFailed,
[]CompletionTypeAttribute{CompletionTypeAttributeTransient, CompletionTypeAttributeExternal}}},
CompletionCodePodFailedWithoutFailedContainer: {
"PodFailedWithoutFailedContainer", CompletionType{CompletionTypeNameFailed,
[]CompletionTypeAttribute{CompletionTypeAttributeTransient, CompletionTypeAttributeExternal}}},
CompletionCodePodSpecInvalid: {
"PodSpecInvalid", CompletionType{CompletionTypeNameFailed,
[]CompletionTypeAttribute{CompletionTypeAttributePermanent, CompletionTypeAttributeInternal}}},
CompletionCodeContainerOOMKilled: {
// May be due to exceed the Container memory limit or the Container workload spike or
// OS memory pressure, so it may be Permanent or Transient, Internal or External.
"ContainerOOMKilled", CompletionType{CompletionTypeNameFailed,
[]CompletionTypeAttribute{}}},
}
var CompletionCodeInfoContainerFailedWithUnknownExitCode = CompletionCodeInfo{
"ContainerFailedWithUnknownExitCode", CompletionType{CompletionTypeNameFailed,
[]CompletionTypeAttribute{}},
}

Просмотреть файл

@ -0,0 +1,127 @@
// MIT License
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// 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
package v1
import (
apiExtensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/microsoft/frameworkcontroller/pkg/common"
)
const (
// Names in CRD should be up to 63 lower case alphanumeric characters.
NamingConvention = "^[a-z0-9]{1,63}$"
)
func BuildFrameworkCRD() *apiExtensions.CustomResourceDefinition {
crd := &apiExtensions.CustomResourceDefinition{
ObjectMeta: meta.ObjectMeta{
Name: FrameworkCRDName,
},
Spec: apiExtensions.CustomResourceDefinitionSpec{
Group: GroupName,
Version: SchemeGroupVersion.Version,
Scope: apiExtensions.NamespaceScoped,
Names: apiExtensions.CustomResourceDefinitionNames{
Plural: FrameworkPlural,
Kind: FrameworkKind,
},
Validation: buildFrameworkValidation(),
// TODO: Enable CRD Subresources after ApiServer has supported it.
//Subresources: &apiExtensions.CustomResourceSubresources{
// Status: &apiExtensions.CustomResourceSubresourceStatus{
// },
//},
},
}
return crd
}
func buildFrameworkValidation() *apiExtensions.CustomResourceValidation {
return &apiExtensions.CustomResourceValidation{
OpenAPIV3Schema: &apiExtensions.JSONSchemaProps{
Properties: map[string]apiExtensions.JSONSchemaProps{
"metadata": {
Properties: map[string]apiExtensions.JSONSchemaProps{
"name": {
Type: "string",
Pattern: NamingConvention,
},
},
},
"spec": {
Properties: map[string]apiExtensions.JSONSchemaProps{
"executionType": {
Enum: []apiExtensions.JSON{
{Raw: []byte(common.Quote(string(ExecutionStart)))},
{Raw: []byte(common.Quote(string(ExecutionStop)))},
},
},
"retryPolicy": {
Properties: map[string]apiExtensions.JSONSchemaProps{
"maxRetryCount": {
Type: "integer",
Minimum: common.PtrFloat64(ExtendedUnlimitedValue),
},
},
},
"taskRoles": {
// TODO: names in array should not duplicate
Type: "array",
Items: &apiExtensions.JSONSchemaPropsOrArray{
Schema: &apiExtensions.JSONSchemaProps{
Properties: map[string]apiExtensions.JSONSchemaProps{
"name": {
Type: "string",
Pattern: NamingConvention,
},
"taskNumber": {
Type: "integer",
Minimum: common.PtrFloat64(0),
Maximum: common.PtrFloat64(10000),
},
"frameworkAttemptCompletionPolicy": {
Properties: map[string]apiExtensions.JSONSchemaProps{
"minFailedTaskCount": {
Type: "integer",
// TODO: should not allow 0
Minimum: common.PtrFloat64(UnlimitedValue),
},
"minSucceededTaskCount": {
Type: "integer",
// TODO: should not allow 0
Minimum: common.PtrFloat64(UnlimitedValue),
},
},
},
},
},
},
},
},
},
},
},
}
}

Просмотреть файл

@ -0,0 +1,25 @@
// MIT License
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// 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
// +k8s:deepcopy-gen=package
// +groupName=frameworkcontroller.microsoft.com
package v1

Просмотреть файл

@ -0,0 +1,551 @@
// MIT License
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// 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
package v1
import (
"fmt"
"strings"
"strconv"
log "github.com/sirupsen/logrus"
core "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/microsoft/frameworkcontroller/pkg/common"
)
///////////////////////////////////////////////////////////////////////////////////////
// Utils
///////////////////////////////////////////////////////////////////////////////////////
func GetConfigMapName(frameworkName string) string {
return strings.Join([]string{frameworkName, "attempt"}, "-")
}
func SplitConfigMapName(configMapName string) (frameworkName string) {
parts := strings.Split(configMapName, "-")
return parts[0]
}
func GetPodName(frameworkName string, taskRoleName string, taskIndex int32) string {
return strings.Join([]string{frameworkName, taskRoleName, fmt.Sprint(taskIndex)}, "-")
}
func SplitPodName(podName string) (frameworkName string, taskRoleName string, taskIndex int32) {
parts := strings.Split(podName, "-")
i, err := strconv.ParseInt(parts[2], 10, 32)
if err != nil {
panic(fmt.Errorf("Failed to SplitPodName %v: %v", podName, err))
}
return parts[0], parts[1], int32(i)
}
func GetFrameworkAttemptInstanceUID(frameworkAttemptID int32, configMapUID *types.UID) *types.UID {
return common.PtrUIDStr(fmt.Sprintf("%v_%v", frameworkAttemptID, *configMapUID))
}
func SplitFrameworkAttemptInstanceUID(frameworkAttemptInstanceUID *types.UID) (
frameworkAttemptID int32, configMapUID *types.UID) {
parts := strings.Split(string(*frameworkAttemptInstanceUID), "_")
i, err := strconv.ParseInt(parts[0], 10, 32)
if err != nil {
panic(fmt.Errorf(
"Failed to SplitFrameworkAttemptInstanceUID %v: %v",
*frameworkAttemptInstanceUID, err))
}
return int32(i), common.PtrUIDStr(parts[1])
}
func GetTaskAttemptInstanceUID(taskAttemptID int32, podUID *types.UID) *types.UID {
return common.PtrUIDStr(fmt.Sprintf("%v_%v", taskAttemptID, *podUID))
}
func SplitTaskAttemptInstanceUID(taskAttemptInstanceUID *types.UID) (
taskAttemptID int32, podUID *types.UID) {
parts := strings.Split(string(*taskAttemptInstanceUID), "_")
i, err := strconv.ParseInt(parts[0], 10, 32)
if err != nil {
panic(fmt.Errorf(
"Failed to SplitTaskAttemptInstanceUID %v: %v",
*taskAttemptInstanceUID, err))
}
return int32(i), common.PtrUIDStr(parts[1])
}
///////////////////////////////////////////////////////////////////////////////////////
// Interfaces
///////////////////////////////////////////////////////////////////////////////////////
type TaskStatusSelector func(*TaskStatus) bool
///////////////////////////////////////////////////////////////////////////////////////
// Spec Read Methods
///////////////////////////////////////////////////////////////////////////////////////
func (f *Framework) Key() string {
return f.Namespace + "/" + f.Name
}
func (f *Framework) TaskRoleSpec(taskRoleName string) *TaskRoleSpec {
for _, taskRole := range f.Spec.TaskRoles {
if taskRole.Name == taskRoleName {
return &taskRole
}
}
panic(fmt.Errorf("[%v]: TaskRole is not found in Spec", taskRoleName))
}
///////////////////////////////////////////////////////////////////////////////////////
// Status Read Methods
///////////////////////////////////////////////////////////////////////////////////////
func (f *Framework) FrameworkAttemptID() int32 {
return f.Status.AttemptStatus.ID
}
func (ts *TaskStatus) TaskAttemptID() int32 {
return ts.AttemptStatus.ID
}
func (f *Framework) FrameworkAttemptInstanceUID() *types.UID {
return f.Status.AttemptStatus.InstanceUID
}
func (ts *TaskStatus) TaskAttemptInstanceUID() *types.UID {
return ts.AttemptStatus.InstanceUID
}
func (f *Framework) ConfigMapName() string {
return f.Status.AttemptStatus.ConfigMapName
}
func (ts *TaskStatus) PodName() string {
return ts.AttemptStatus.PodName
}
func (f *Framework) ConfigMapUID() *types.UID {
return f.Status.AttemptStatus.ConfigMapUID
}
func (ts *TaskStatus) PodUID() *types.UID {
return ts.AttemptStatus.PodUID
}
func (f *Framework) CompletionType() CompletionType {
return f.Status.AttemptStatus.CompletionStatus.Type
}
func (ts *TaskStatus) CompletionType() CompletionType {
return ts.AttemptStatus.CompletionStatus.Type
}
func (f *Framework) TaskRoleStatuses() []TaskRoleStatus {
return f.Status.AttemptStatus.TaskRoleStatuses
}
func (f *Framework) TaskRoleStatus(taskRoleName string) *TaskRoleStatus {
for _, taskRoleStatus := range f.TaskRoleStatuses() {
if taskRoleStatus.Name == taskRoleName {
return &taskRoleStatus
}
}
panic(fmt.Errorf("[%v]: TaskRole is not found in Status", taskRoleName))
}
func (f *Framework) TaskStatus(taskRoleName string, taskIndex int32) *TaskStatus {
taskRoleStatus := f.TaskRoleStatus(taskRoleName)
if 0 <= taskIndex && taskIndex < int32(len(taskRoleStatus.TaskStatuses)) {
return &taskRoleStatus.TaskStatuses[taskIndex]
}
panic(fmt.Errorf("[%v][%v]: Task is not found in Status", taskRoleName, taskIndex))
}
func (f *Framework) IsCompleted() bool {
return f.Status.State == FrameworkCompleted
}
func (ts *TaskStatus) IsCompleted() bool {
return ts.State == TaskCompleted
}
func (ct CompletionType) IsSucceeded() bool {
return ct.Name == CompletionTypeNameSucceeded
}
func (f *Framework) IsSucceeded() bool {
return f.IsCompleted() && f.CompletionType().IsSucceeded()
}
func (ts *TaskStatus) IsSucceeded() bool {
return ts.IsCompleted() && ts.CompletionType().IsSucceeded()
}
func (ct CompletionType) IsFailed() bool {
return ct.Name == CompletionTypeNameFailed
}
func (f *Framework) IsFailed() bool {
return f.IsCompleted() && f.CompletionType().IsFailed()
}
func (ts *TaskStatus) IsFailed() bool {
return ts.IsCompleted() && ts.CompletionType().IsFailed()
}
func (ct CompletionType) ContainsAttribute(attribute CompletionTypeAttribute) bool {
for i := range ct.Attributes {
if ct.Attributes[i] == attribute {
return true
}
}
return false
}
func (trs *TaskRoleStatus) GetTaskStatuses(selector TaskStatusSelector) []TaskStatus {
if selector == nil {
return trs.TaskStatuses
}
taskStatuses := []TaskStatus{}
for _, taskStatus := range trs.TaskStatuses {
if selector(&taskStatus) {
taskStatuses = append(taskStatuses, taskStatus)
}
}
return taskStatuses
}
func (trs *TaskRoleStatus) GetTaskCount(selector TaskStatusSelector) int32 {
return int32(len(trs.GetTaskStatuses(selector)))
}
func (f *Framework) GetTaskCount(selector TaskStatusSelector) int32 {
taskCount := int32(0)
for _, taskRoleStatus := range f.TaskRoleStatuses() {
taskCount += taskRoleStatus.GetTaskCount(selector)
}
return taskCount
}
func (f *Framework) AreAllTasksCompleted() bool {
return f.GetTaskCount(nil) == f.GetTaskCount((*TaskStatus).IsCompleted)
}
func (f *Framework) NewConfigMap() *core.ConfigMap {
frameworkAttemptIDStr := fmt.Sprint(f.FrameworkAttemptID())
cm := &core.ConfigMap{
ObjectMeta: meta.ObjectMeta{},
}
// Init ConfigMap
cm.Name = f.ConfigMapName()
cm.Namespace = f.Namespace
cm.OwnerReferences = []meta.OwnerReference{*meta.NewControllerRef(f, FrameworkGroupVersionKind)}
// By default, ensure there is no timeline overlap among different FrameworkAttemptInstances.
cm.Finalizers = []string{meta.FinalizerDeleteDependents}
cm.Annotations = map[string]string{}
cm.Annotations[AnnotationKeyFrameworkName] = f.Name
cm.Annotations[AnnotationKeyConfigMapName] = cm.Name
cm.Annotations[AnnotationKeyFrameworkAttemptID] = frameworkAttemptIDStr
cm.Labels = map[string]string{}
cm.Labels[LabelKeyFrameworkName] = f.Name
return cm
}
func (f *Framework) NewPod(cm *core.ConfigMap, taskRoleName string, taskIndex int32) *core.Pod {
taskPod := f.TaskRoleSpec(taskRoleName).Task.Pod.DeepCopy()
taskStatus := f.TaskStatus(taskRoleName, taskIndex)
taskIndexStr := fmt.Sprint(taskIndex)
frameworkAttemptIDStr := fmt.Sprint(f.FrameworkAttemptID())
frameworkAttemptInstanceUIDStr := string(*f.FrameworkAttemptInstanceUID())
configMapUIDStr := string(*f.ConfigMapUID())
taskAttemptIDStr := fmt.Sprint(taskStatus.TaskAttemptID())
taskAttemptInstanceUIDReferStr := string(*GetTaskAttemptInstanceUID(
taskStatus.TaskAttemptID(),
common.PtrUIDStr(common.ReferEnvVar(EnvNamePodUID))))
pod := &core.Pod{
ObjectMeta: taskPod.ObjectMeta,
Spec: taskPod.Spec,
}
// Rewrite Task.Pod
pod.Name = taskStatus.PodName()
pod.Namespace = f.Namespace
// Augment Task.Pod
// By setting the owner is also a controller, the Pod functions normally, except for
// its detail cannot be viewed from k8s dashboard, and the dashboard shows error:
// "Unknown reference kind ConfigMap".
// See https://github.com/kubernetes/dashboard/issues/3158
if pod.OwnerReferences == nil {
pod.OwnerReferences = []meta.OwnerReference{}
}
pod.OwnerReferences = append(pod.OwnerReferences, *meta.NewControllerRef(cm, ConfigMapGroupVersionKind))
if pod.Annotations == nil {
pod.Annotations = map[string]string{}
}
pod.Annotations[AnnotationKeyFrameworkName] = f.Name
pod.Annotations[AnnotationKeyTaskRoleName] = taskRoleName
pod.Annotations[AnnotationKeyTaskIndex] = taskIndexStr
pod.Annotations[AnnotationKeyConfigMapName] = f.ConfigMapName()
pod.Annotations[AnnotationKeyPodName] = pod.Name
pod.Annotations[AnnotationKeyPodNamespace] = pod.Namespace
pod.Annotations[AnnotationKeyFrameworkAttemptID] = frameworkAttemptIDStr
pod.Annotations[AnnotationKeyFrameworkAttemptInstanceUID] = frameworkAttemptInstanceUIDStr
pod.Annotations[AnnotationKeyConfigMapUID] = configMapUIDStr
pod.Annotations[AnnotationKeyTaskAttemptID] = taskAttemptIDStr
if pod.Labels == nil {
pod.Labels = map[string]string{}
}
pod.Labels[LabelKeyFrameworkName] = f.Name
pod.Labels[LabelKeyTaskRoleName] = taskRoleName
exEnvs := []core.EnvVar{
{Name: EnvNameFrameworkName, Value: f.Name},
{Name: EnvNameTaskRoleName, Value: taskRoleName},
{Name: EnvNameTaskIndex, Value: taskIndexStr},
{Name: EnvNameConfigMapName, Value: f.ConfigMapName()},
{Name: EnvNamePodName, Value: pod.Name},
{Name: EnvNamePodNamespace, Value: pod.Namespace},
{Name: EnvNameFrameworkAttemptID, Value: frameworkAttemptIDStr},
{Name: EnvNameFrameworkAttemptInstanceUID, Value: frameworkAttemptInstanceUIDStr},
{Name: EnvNameConfigMapUID, Value: configMapUIDStr},
{Name: EnvNameTaskAttemptID, Value: taskAttemptIDStr},
{Name: EnvNamePodUID, ValueFrom: ObjectUIDEnvVarSource},
{Name: EnvNameTaskAttemptInstanceUID, Value: taskAttemptInstanceUIDReferStr},
}
// Change the default TerminationMessagePolicy to TerminationMessageFallbackToLogsOnError
// in case the cluster-level logging has not been setup for the cluster.
// See https://kubernetes.io/docs/concepts/cluster-administration/logging
// It is safe to do so, since it will only fall back to the tail log if the container
// is failed and the termination message file specified by the terminationMessagePath
// is not found or empty.
for i := range pod.Spec.Containers {
for _, exEnv := range exEnvs {
pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, exEnv)
}
if len(pod.Spec.Containers[i].TerminationMessagePolicy) == 0 {
pod.Spec.Containers[i].TerminationMessagePolicy = core.TerminationMessageFallbackToLogsOnError
}
}
for i := range pod.Spec.InitContainers {
for _, exEnv := range exEnvs {
pod.Spec.InitContainers[i].Env = append(pod.Spec.InitContainers[i].Env, exEnv)
}
if len(pod.Spec.InitContainers[i].TerminationMessagePolicy) == 0 {
pod.Spec.InitContainers[i].TerminationMessagePolicy = core.TerminationMessageFallbackToLogsOnError
}
}
return pod
}
func (f *Framework) NewFrameworkStatus() *FrameworkStatus {
return &FrameworkStatus{
StartTime: meta.Now(),
CompletionTime: nil,
State: FrameworkAttemptCreationPending,
TransitionTime: meta.Now(),
RetryPolicyStatus: RetryPolicyStatus{
TotalRetriedCount: 0,
AccountableRetriedCount: 0,
RetryDelaySec: nil,
},
AttemptStatus: f.NewFrameworkAttemptStatus(0),
}
}
func (f *Framework) NewFrameworkAttemptStatus(
frameworkAttemptID int32) FrameworkAttemptStatus {
return FrameworkAttemptStatus{
ID: frameworkAttemptID,
StartTime: meta.Now(),
CompletionTime: nil,
InstanceUID: nil,
ConfigMapName: GetConfigMapName(f.Name),
ConfigMapUID: nil,
CompletionStatus: nil,
TaskRoleStatuses: f.NewTaskRoleStatuses(),
}
}
func (f *Framework) NewTaskRoleStatuses() []TaskRoleStatus {
trss := []TaskRoleStatus{}
for _, taskRole := range f.Spec.TaskRoles {
trs := TaskRoleStatus{Name: taskRole.Name, TaskStatuses: []TaskStatus{}}
for taskIndex := int32(0); taskIndex < taskRole.TaskNumber; taskIndex++ {
trs.TaskStatuses = append(trs.TaskStatuses, f.NewTaskStatus(taskRole.Name, taskIndex))
}
trss = append(trss, trs)
}
return trss
}
func (f *Framework) NewTaskStatus(taskRoleName string, taskIndex int32) TaskStatus {
return TaskStatus{
Index: taskIndex,
StartTime: meta.Now(),
CompletionTime: nil,
State: TaskAttemptCreationPending,
TransitionTime: meta.Now(),
RetryPolicyStatus: RetryPolicyStatus{
TotalRetriedCount: 0,
AccountableRetriedCount: 0,
RetryDelaySec: nil,
},
AttemptStatus: f.NewTaskAttemptStatus(taskRoleName, taskIndex, 0),
}
}
func (f *Framework) NewTaskAttemptStatus(
taskRoleName string, taskIndex int32, taskAttemptID int32) TaskAttemptStatus {
return TaskAttemptStatus{
ID: taskAttemptID,
StartTime: meta.Now(),
CompletionTime: nil,
InstanceUID: nil,
PodName: GetPodName(f.Name, taskRoleName, taskIndex),
PodUID: nil,
PodIP: nil,
PodHostIP: nil,
CompletionStatus: nil,
}
}
func (cc CompletionCode) NewCompletionStatus(diagnostics string) *CompletionStatus {
cci, exists := CompletionCodeInfos[cc]
if !exists {
cci = CompletionCodeInfoContainerFailedWithUnknownExitCode
}
return &CompletionStatus{
Code: cc,
Phrase: cci.Phrase,
Type: cci.Type,
Diagnostics: diagnostics,
}
}
func (cs *CompletionStatus) String() string {
return fmt.Sprintf(
"[Code: %v, Phrase: %v, Type: %v, Diagnostics: %v]",
cs.Code, cs.Phrase, cs.Type, cs.Diagnostics)
}
type RetryDecision struct {
ShouldRetry bool
// Whether the retry should be counted into AccountableRetriedCount
IsAccountable bool
// The retry should be executed after DelaySec.
DelaySec int64
Reason string
}
func (rd RetryDecision) String() string {
return fmt.Sprintf(
"[ShouldRetry: %v, IsAccountable: %v, DelaySec: %v, Reason: %v]",
rd.ShouldRetry, rd.IsAccountable, rd.DelaySec, rd.Reason)
}
func (rp RetryPolicySpec) ShouldRetry(
rps RetryPolicyStatus,
ct CompletionType,
minDelaySecForTransientConflictFailed int64,
maxDelaySecForTransientConflictFailed int64) RetryDecision {
// 1. FancyRetryPolicy
if rp.FancyRetryPolicy {
reason := fmt.Sprintf(
"FancyRetryPolicy is %v and CompletionType is %v",
rp.FancyRetryPolicy, ct)
if ct.IsFailed() {
if ct.ContainsAttribute(CompletionTypeAttributeTransient) {
if ct.ContainsAttribute(CompletionTypeAttributeConflict) {
return RetryDecision{true, false,
common.RandInt64(
minDelaySecForTransientConflictFailed,
maxDelaySecForTransientConflictFailed),
reason}
} else {
return RetryDecision{true, false, 0, reason}
}
} else if ct.ContainsAttribute(CompletionTypeAttributePermanent) {
return RetryDecision{false, true, 0, reason}
}
}
}
// 2. NormalRetryPolicy
if (rp.MaxRetryCount == ExtendedUnlimitedValue) ||
(ct.IsFailed() && rp.MaxRetryCount == UnlimitedValue) ||
(ct.IsFailed() && rps.AccountableRetriedCount < rp.MaxRetryCount) {
return RetryDecision{true, true, 0, fmt.Sprintf(
"AccountableRetriedCount %v has not reached MaxRetryCount %v",
rps.AccountableRetriedCount, rp.MaxRetryCount)}
} else {
if ct.IsSucceeded() {
return RetryDecision{false, true, 0, fmt.Sprintf(
"CompletionType is %v", ct)}
} else {
return RetryDecision{false, true, 0, fmt.Sprintf(
"AccountableRetriedCount %v has reached MaxRetryCount %v",
rps.AccountableRetriedCount, rp.MaxRetryCount)}
}
}
}
///////////////////////////////////////////////////////////////////////////////////////
// Status Write Methods
///////////////////////////////////////////////////////////////////////////////////////
// This is the only interface to modify FrameworkState
func (f *Framework) TransitionFrameworkState(dstState FrameworkState) {
srcState := f.Status.State
if srcState == dstState {
return
}
f.Status.State = dstState
f.Status.TransitionTime = meta.Now()
log.Infof(
"[%v]: Transitioned Framework from [%v] to [%v]",
f.Key(), srcState, dstState)
}
// This is the only interface to modify TaskState
func (f *Framework) TransitionTaskState(
taskRoleName string, taskIndex int32, dstState TaskState) {
taskStatus := f.TaskStatus(taskRoleName, taskIndex)
srcState := taskStatus.State
if srcState == dstState {
return
}
taskStatus.State = dstState
taskStatus.TransitionTime = meta.Now()
log.Infof(
"[%v][%v][%v]: Transitioned Task from [%v] to [%v]",
f.Key(), taskRoleName, taskIndex, srcState, dstState)
}

Просмотреть файл

@ -0,0 +1,55 @@
// MIT License
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// 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
package v1
import (
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
var SchemeGroupVersion = schema.GroupVersion{
Group: GroupName,
Version: Version,
}
var (
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
AddToScheme = SchemeBuilder.AddToScheme
)
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(
SchemeGroupVersion,
&Framework{},
&FrameworkList{},
)
// register the type in the scheme
meta.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}

Просмотреть файл

@ -0,0 +1,531 @@
// MIT License
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// 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
package v1
import (
core "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type FrameworkList struct {
meta.TypeMeta `json:",inline"`
meta.ListMeta `json:"metadata"`
Items []Framework `json:"items"`
}
// +genclient
// +genclient:noStatus
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
//////////////////////////////////////////////////////////////////////////////////////////////////
// A Framework represents an application with a set of Tasks:
// 1. Executed by Kubernetes Pod
// 2. Partitioned to different heterogeneous TaskRoles which share the same lifecycle
// 3. Ordered in the same homogeneous TaskRole by TaskIndex
// 4. With consistent identity {FrameworkName}-{TaskRoleName}-{TaskIndex} as PodName
// 5. With fine grained RetryPolicy for each Task and the whole Framework
// 6. With fine grained FrameworkAttemptCompletionPolicy for each TaskRole
// 7. Guarantees at most one instance of a specific Task is running at any point in time
// 8. Guarantees at most one instance of a specific Framework is running at any point in time
//
// Notes:
// 1. Status field should only be modified by FrameworkController, and
// other fields should not be modified by FrameworkController.
// TODO: Remove +genclient:noStatus after ApiServer has supported CRD Subresources.
// Leverage CRD status subresource to isolate Status field modification with other fields.
// This can help to avoid unintended modification, such as users may unintendedly modify
// the status when updating the spec.
// 2. To ensure at most one instance of a specific Task is running at any point in time:
// 1. Do not delete the managed Pod with 0 gracePeriodSeconds.
// For example, the default Pod deletion is acceptable.
// 2. Do not delete the Node which runs the managed Pod.
// For example, drain before delete the Node is acceptable.
// The instance can be universally located by its TaskAttemptInstanceUID or PodUID.
// See RetryPolicySpec and TaskAttemptStatus.
// 3. To ensure at most one instance of a specific Framework is running at any point in time:
// 1. Ensure ensure at most one instance of a specific Task is running at any point in time.
// 2. Do not delete the managed ConfigMap with Background propagationPolicy.
// For example, the default ConfigMap deletion is acceptable.
// 3. Must delete the Framework with Foreground propagationPolicy.
// For example, the default Framework deletion may not be acceptable, since the default
// propagationPolicy for Framework object may be Background.
// The instance can be universally located by its FrameworkAttemptInstanceUID or ConfigMapUID.
// See RetryPolicySpec and FrameworkAttemptStatus.
// 4. To ensure there is no orphan object previously managed by FrameworkController:
// 1. Do not delete the Framework or the managed ConfigMap with Orphan propagationPolicy.
// For example, the default Framework and ConfigMap deletion is acceptable.
// 2. Do not change the OwnerReferences of the managed ConfigMap and Pods.
//////////////////////////////////////////////////////////////////////////////////////////////////
type Framework struct {
meta.TypeMeta `json:",inline"`
meta.ObjectMeta `json:"metadata"`
Spec FrameworkSpec `json:"spec"`
Status *FrameworkStatus `json:"status"`
}
//////////////////////////////////////////////////////////////////////////////////////////////////
// Spec
//////////////////////////////////////////////////////////////////////////////////////////////////
type FrameworkSpec struct {
Description string `json:"description"`
ExecutionType ExecutionType `json:"executionType"`
RetryPolicy RetryPolicySpec `json:"retryPolicy"`
TaskRoles []TaskRoleSpec `json:"taskRoles"`
}
type TaskRoleSpec struct {
// TaskRoleName
Name string `json:"name"`
// Tasks with TaskIndex in range [0, TaskNumber)
TaskNumber int32 `json:"taskNumber"`
FrameworkAttemptCompletionPolicy CompletionPolicySpec `json:"frameworkAttemptCompletionPolicy"`
Task TaskSpec `json:"task"`
}
type TaskSpec struct {
RetryPolicy RetryPolicySpec `json:"retryPolicy"`
Pod core.PodTemplateSpec `json:"pod"`
}
type ExecutionType string
const (
ExecutionStart ExecutionType = "Start"
ExecutionStop ExecutionType = "Stop"
)
// RetryPolicySpec can be configured for the whole Framework and each TaskRole
// to control:
// 1. Framework RetryPolicy:
// The conditions to retry the whole Framework after the Framework's current
// FrameworkAttempt completed.
// It can also be considered as Framework CompletionPolicy, i.e. the conditions
// to complete the whole Framework.
// 2. Task RetryPolicy:
// The conditions to retry a single Task in the TaskRole after the Task's
// current TaskAttempt completed.
// It can also be considered as Task CompletionPolicy, i.e. the conditions to
// complete a single Task in the TaskRole.
//
// Usage:
// If the FancyRetryPolicy is enabled,
// will retry if the completion is due to Transient Failed CompletionType,
// will not retry if the completion is due to Permanent Failed CompletionType,
// will apply the NormalRetryPolicy defined below if all above conditions are
// not satisfied.
//
// If the FancyRetryPolicy is not enabled,
// will directly apply the NormalRetryPolicy for all kinds of completions.
//
// The NormalRetryPolicy is defined as,
// will retry and AccountableRetriedCount++ if MaxRetryCount == -2,
// will retry and AccountableRetriedCount++ if the completion is due to any
// failure and MaxRetryCount == -1,
// will retry and AccountableRetriedCount++ if the completion is due to any
// failure and AccountableRetriedCount < MaxRetryCount,
// will not retry if all above conditions are not satisfied.
//
// After the retry is exhausted, the final CompletionStatus is defined as,
// the CompletionStatus of the last attempt.
//
// Notes:
// 1. The existence of an attempt instance may not always be observed, such as
// create fails but succeeds on remote and then followed by an external delete.
// So, an attempt identified by its attempt id may be associated with multiple
// attempt instances over time, i.e. multiple instances may be run for the
// attempt over time, however, at most one instance is exposed into ApiServer
// over time and at most one instance is running at any point in time.
// So, the actual retried attempt instances maybe exceed the RetryPolicySpec
// in rare cases, however, the RetryPolicyStatus will never exceed the
// RetryPolicySpec.
type RetryPolicySpec struct {
FancyRetryPolicy bool `json:"fancyRetryPolicy"`
MaxRetryCount int32 `json:"maxRetryCount"`
}
// CompletionPolicySpec can be configured for each TaskRole to control:
// 1. FrameworkAttempt CompletionPolicy:
// 1. The conditions to complete a FrameworkAttempt.
// 2. The CompletionStatus of the completed FrameworkAttempt.
//
// Usage:
// 1. If MinFailedTaskCount != -1 and MinFailedTaskCount <= failed Task count of
// current TaskRole, immediately complete the FrameworkAttempt, regardless of
// any uncompleted Task, and the CompletionStatus is failed which is generated
// from the Task which triggers the completion.
// 2. If MinSucceededTaskCount != -1 and MinSucceededTaskCount <= succeeded Task
// count of current TaskRole, immediately complete the FrameworkAttempt, regardless
// of any uncompleted Task, and the CompletionStatus is succeeded which is
// generated from the Task which triggers the completion.
// 3. If multiple above 1. and 2. conditions of all TaskRoles are satisfied at the
// same time, the behavior can be any one of these satisfied conditions.
// 4. If none of above 1. and 2. conditions of all TaskRoles are satisfied until all
// Tasks of the Framework are completed, immediately complete the FrameworkAttempt
// and the CompletionStatus is succeeded which is not generated from any Task.
//
// Notes:
// 1. When the FrameworkAttempt is completed, the FrameworkState is transitioned to
// FrameworkAttemptCompleted, so the Framework may still be retried with another
// new FrameworkAttempt according to the Framework RetryPolicySpec.
type CompletionPolicySpec struct {
MinFailedTaskCount int32 `json:"minFailedTaskCount"`
MinSucceededTaskCount int32 `json:"minSucceededTaskCount"`
}
//////////////////////////////////////////////////////////////////////////////////////////////////
// Status
// It is used to:
// 1. Aggregate the ground truth from other related objects, such as Pod.Status.
// 2. Maintain the Framework owned ground truth, such as PodUID.
// 3. Retain the ground truth even if other related objects are deleted.
//
// Notes:
// 1. It should only contain current status, history status should be a different type
// and stored in a history database.
// 2. For field which is not the ground truth, such as the TaskState, it should be
// totally reconstructable from its ground truth, in case the Status is failed to
// persist due to FrameworkController restart.
// The ground truth may be other fields in Framework.Status or the fields in other
// related objects, such as the PodUID and Pod.Status.
// 3. For field which is the ground truth, such as the PodUID, it should be
// Monotonically Exposed which means it should only be changed to a future state in
// ApiServer. However, it does not mean other related objects are also Monotonically
// Exposed.
// For example, from the view of any ApiServer client, the PodUID should be changed
// from a not nil value to a different not nil value, if and only if its TaskAttemptID
// is also increased.
// 4. It is better to keep the ground truth in other related objects instead of in the
// Status here, so that the Framework can be more compatible with other k8s features,
// such as labels and selectors.
//////////////////////////////////////////////////////////////////////////////////////////////////
type FrameworkStatus struct {
StartTime meta.Time `json:"startTime"`
CompletionTime *meta.Time `json:"completionTime"`
State FrameworkState `json:"state"`
TransitionTime meta.Time `json:"transitionTime"`
RetryPolicyStatus RetryPolicyStatus `json:"retryPolicyStatus"`
AttemptStatus FrameworkAttemptStatus `json:"attemptStatus"`
}
type FrameworkAttemptStatus struct {
// FrameworkAttemptID = {FrameworkStatus.RetryPolicyStatus.TotalRetriedCount}
// It can only locate the FrameworkAttempt within a specific Framework, i.e.
// it cannot universally locate the FrameworkAttempt and cannot locate the
// FrameworkAttemptInstance even within a specific Framework.
ID int32 `json:"id"`
StartTime meta.Time `json:"startTime"`
CompletionTime *meta.Time `json:"completionTime"`
// Current associated FrameworkAttemptInstance:
// FrameworkAttemptInstanceUID = {FrameworkAttemptID}_{ConfigMapUID}
// It is ordered by FrameworkAttemptID and can universally locate the
// FrameworkAttemptInstance.
InstanceUID *types.UID `json:"instanceUID"`
// A FrameworkAttemptInstance is represented by a ConfigMap object:
// ConfigMapName = {FrameworkName}-attempt
// It will never be changed during the whole lifetime of a specific Framework.
ConfigMapName string `json:"configMapName"`
// ConfigMapUID can also universally locate the FrameworkAttemptInstance.
ConfigMapUID *types.UID `json:"configMapUID"`
CompletionStatus *CompletionStatus `json:"completionStatus"`
TaskRoleStatuses []TaskRoleStatus `json:"taskRoleStatuses"`
}
type TaskRoleStatus struct {
// TaskRoleName
Name string `json:"name"`
// Tasks with TaskIndex in range [0, TaskNumber)
TaskStatuses []TaskStatus `json:"taskStatuses"`
}
type TaskStatus struct {
// TaskIndex
Index int32 `json:"index"`
StartTime meta.Time `json:"startTime"`
CompletionTime *meta.Time `json:"completionTime"`
State TaskState `json:"state"`
TransitionTime meta.Time `json:"transitionTime"`
RetryPolicyStatus RetryPolicyStatus `json:"retryPolicyStatus"`
AttemptStatus TaskAttemptStatus `json:"attemptStatus"`
}
type TaskAttemptStatus struct {
// TaskAttemptID = {TaskStatus.RetryPolicyStatus.TotalRetriedCount}
// It can only locate the TaskAttempt within a specific Task, i.e. it cannot
// universally locate the TaskAttempt and cannot locate the TaskAttemptInstance
// even within a specific Task.
ID int32 `json:"id"`
StartTime meta.Time `json:"startTime"`
CompletionTime *meta.Time `json:"completionTime"`
// Current associated TaskAttemptInstance:
// TaskAttemptInstanceUID = {TaskAttemptID}_{PodUID}
// It is ordered by TaskAttemptID and can universally locate the
// TaskAttemptInstance.
InstanceUID *types.UID `json:"instanceUID"`
// A TaskAttemptInstance is represented by a Pod object:
// PodName = {FrameworkName}-{TaskRoleName}-{TaskIndex}
// It will never be changed during the whole lifetime of a specific Task.
PodName string `json:"podName"`
// PodUID can also universally locate the TaskAttemptInstance.
PodUID *types.UID `json:"podUID"`
PodIP *string `json:"podIP"`
PodHostIP *string `json:"podHostIP"`
CompletionStatus *CompletionStatus `json:"completionStatus"`
}
type RetryPolicyStatus struct {
// Used as the ground truth of current attempt id.
// If it is for Framework, TotalRetriedCount = FrameworkAttemptID
// If it is for Task, TotalRetriedCount = TaskAttemptID
TotalRetriedCount int32 `json:"totalRetriedCount"`
// Used to compare against MaxRetryCount.
// If the FancyRetryPolicy is not enabled,
// it is the same as the TotalRetriedCount.
// If the FancyRetryPolicy is enabled,
// it does not count into the retries for the completion which is due to
// Transient CompletionType, so only in this case, it may be less than the
// TotalRetriedCount.
AccountableRetriedCount int32 `json:"accountableRetriedCount"`
// Used to expose the ScheduledRetryTime after which current retry can be
// executed.
// ScheduledRetryTime = AttemptStatus.CompletionTime + RetryDelaySec
// It is available and meaningful if and only if current attempt is in
// AttemptCompleted state.
RetryDelaySec *int64 `json:"retryDelaySec"`
}
type CompletionStatus struct {
// CompletionCode Convention:
// 1. NonNegative:
// The CompletionCode is the ExitCode of the Framework's Container which
// triggers the completion.
// 2. Negative:
// -1XX: Framework Predefined Transient Error
// -2XX: Framework Predefined Permanent Error
// -3XX: Framework Predefined Unknown Error
// The CompletionCode is the ExitCode of the Framework's Predefined Error
// which triggers the completion.
Code CompletionCode `json:"code"`
// The textual phrase representation of the CompletionCode.
Phrase CompletionPhrase `json:"phrase"`
// CompletionType is determined by the CompletionCode and the Predefined
// CompletionCodeInfos.
// See CompletionCodeInfos.
Type CompletionType `json:"type"`
// The detailed diagnostic information of the completion.
Diagnostics string `json:"diagnostics"`
}
type CompletionCode int32
type CompletionPhrase string
type CompletionType struct {
Name CompletionTypeName `json:"name"`
Attributes []CompletionTypeAttribute `json:"attributes"`
}
type CompletionTypeName string
const (
CompletionTypeNameSucceeded CompletionTypeName = "Succeeded"
CompletionTypeNameFailed CompletionTypeName = "Failed"
)
type CompletionTypeAttribute string
const (
// CompletionTypeName must be different within a finite retry times:
// such as failed due to dependent components shutdown, machine error,
// network error, environment error, workload spike, etc.
CompletionTypeAttributeTransient CompletionTypeAttribute = "Transient"
// CompletionTypeName must be the same in every retry times:
// such as failed due to incorrect usage, incorrect configuration, etc.
CompletionTypeAttributePermanent CompletionTypeAttribute = "Permanent"
// The completion must be caused by External, i.e. the Platform.
CompletionTypeAttributeExternal CompletionTypeAttribute = "External"
// The completion must be caused by Internal, i.e. the Framework itself.
CompletionTypeAttributeInternal CompletionTypeAttribute = "Internal"
// The completion must be caused by Resource Conflict (Resource Contention):
// such as failed due to Gang Allocation timeout.
CompletionTypeAttributeConflict CompletionTypeAttribute = "Conflict"
)
// The ground truth of FrameworkState is the current associated FrameworkAttemptInstance
// which is represented by the ConfigMapUID and the corresponding ConfigMap object in
// the local cache.
//
// [AssociatedState]: ConfigMapUID is not nil
type FrameworkState string
const (
// ConfigMap does not exist and
// has not been creation requested.
// [StartState]
// [AttemptStartState]
// -> FrameworkAttemptCreationRequested
FrameworkAttemptCreationPending FrameworkState = "AttemptCreationPending"
// ConfigMap does not exist and
// has been creation requested and is expected to exist.
// [AssociatedState]
// -> FrameworkAttemptRunning
// -> FrameworkAttemptDeleting
// -> FrameworkAttemptCompleted
FrameworkAttemptCreationRequested FrameworkState = "AttemptCreationRequested"
// ConfigMap exists and is not deleting and
// has not been deletion requested and
// is not pending to be deletion requested.
// [AssociatedState]
// -> FrameworkAttemptDeletionPending
// -> FrameworkAttemptDeleting
// -> FrameworkAttemptCompleted
FrameworkAttemptRunning FrameworkState = "AttemptRunning"
// ConfigMap exists and is not deleting and
// has not been deletion requested and
// is pending to be deletion requested.
// [AssociatedState]
// -> FrameworkAttemptDeletionRequested
// -> FrameworkAttemptDeleting
// -> FrameworkAttemptCompleted
FrameworkAttemptDeletionPending FrameworkState = "AttemptDeletionPending"
// ConfigMap exists and is not deleting and
// has been deletion requested.
// [AssociatedState]
// -> FrameworkAttemptDeleting
// -> FrameworkAttemptCompleted
FrameworkAttemptDeletionRequested FrameworkState = "AttemptDeletionRequested"
// ConfigMap exists and is deleting.
// [AssociatedState]
// -> FrameworkAttemptCompleted
FrameworkAttemptDeleting FrameworkState = "AttemptDeleting"
// ConfigMap does not exist and
// has been creation requested and is not expected to exist.
// [AttemptFinalState]
// [AssociatedState]
// -> FrameworkAttemptCreationPending
// -> FrameworkCompleted
FrameworkAttemptCompleted FrameworkState = "AttemptCompleted"
// Last FrameworkAttempt is completed.
// [FinalState]
// [AssociatedState]
FrameworkCompleted FrameworkState = "Completed"
)
// The ground truth of TaskState is the current associated TaskAttemptInstance
// which is represented by the PodUID and the corresponding Pod object in the
// local cache.
//
// [AssociatedState]: PodUID is not nil
type TaskState string
const (
// Pod does not exist and
// has not been creation requested.
// [StartState]
// [AttemptStartState]
// -> TaskAttemptCreationRequested
TaskAttemptCreationPending TaskState = "AttemptCreationPending"
// Pod does not exist and
// has been creation requested and is expected to exist.
// [AssociatedState]
// -> TaskAttemptPreparing
// -> TaskAttemptRunning
// -> TaskAttemptDeleting
// -> TaskAttemptCompleted
TaskAttemptCreationRequested TaskState = "AttemptCreationRequested"
// Pod exists and is not deleting and
// has not been deletion requested and
// is PodPending or PodUnknown afterwards.
// [AssociatedState]
// -> TaskAttemptDeletionPending
// -> TaskAttemptRunning
// -> TaskAttemptDeleting
// -> TaskAttemptCompleted
TaskAttemptPreparing TaskState = "AttemptPreparing"
// Pod exists and is not deleting and
// has not been deletion requested and
// is PodRunning or PodUnknown afterwards.
// [AssociatedState]
// -> TaskAttemptDeletionPending
// -> TaskAttemptDeleting
// -> TaskAttemptCompleted
TaskAttemptRunning TaskState = "AttemptRunning"
// Pod exists and is not deleting and
// has not been deletion requested and
// is PodSucceeded or PodFailed.
// [AssociatedState]
// -> TaskAttemptDeletionRequested
// -> TaskAttemptDeleting
// -> TaskAttemptCompleted
TaskAttemptDeletionPending TaskState = "AttemptDeletionPending"
// Pod exists and is not deleting and
// has been deletion requested.
// [AssociatedState]
// -> TaskAttemptDeleting
// -> TaskAttemptCompleted
TaskAttemptDeletionRequested TaskState = "AttemptDeletionRequested"
// Pod exists and is deleting.
// [AssociatedState]
// -> TaskAttemptCompleted
TaskAttemptDeleting TaskState = "AttemptDeleting"
// Pod does not exist and
// has been creation requested and is not expected to exist.
// [AttemptFinalState]
// [AssociatedState]
// -> TaskAttemptCreationPending
// -> TaskCompleted
TaskAttemptCompleted TaskState = "AttemptCompleted"
// Last TaskAttempt is completed.
// [FinalState]
// [AssociatedState]
TaskCompleted TaskState = "Completed"
)

Просмотреть файл

@ -0,0 +1,491 @@
// +build !ignore_autogenerated
/*
Copyright 2018 The Kubernetes 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.
*/
// Code generated by deepcopy-gen. DO NOT EDIT.
package v1
import (
runtime "k8s.io/apimachinery/pkg/runtime"
types "k8s.io/apimachinery/pkg/types"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CompletionCodeInfo) DeepCopyInto(out *CompletionCodeInfo) {
*out = *in
in.Type.DeepCopyInto(&out.Type)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompletionCodeInfo.
func (in *CompletionCodeInfo) DeepCopy() *CompletionCodeInfo {
if in == nil {
return nil
}
out := new(CompletionCodeInfo)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CompletionPolicySpec) DeepCopyInto(out *CompletionPolicySpec) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompletionPolicySpec.
func (in *CompletionPolicySpec) DeepCopy() *CompletionPolicySpec {
if in == nil {
return nil
}
out := new(CompletionPolicySpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CompletionStatus) DeepCopyInto(out *CompletionStatus) {
*out = *in
in.Type.DeepCopyInto(&out.Type)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompletionStatus.
func (in *CompletionStatus) DeepCopy() *CompletionStatus {
if in == nil {
return nil
}
out := new(CompletionStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CompletionType) DeepCopyInto(out *CompletionType) {
*out = *in
if in.Attributes != nil {
in, out := &in.Attributes, &out.Attributes
*out = make([]CompletionTypeAttribute, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompletionType.
func (in *CompletionType) DeepCopy() *CompletionType {
if in == nil {
return nil
}
out := new(CompletionType)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ControllerConfig) DeepCopyInto(out *ControllerConfig) {
*out = *in
if in.KubeApiServerAddress != nil {
in, out := &in.KubeApiServerAddress, &out.KubeApiServerAddress
*out = new(string)
**out = **in
}
if in.KubeConfigFilePath != nil {
in, out := &in.KubeConfigFilePath, &out.KubeConfigFilePath
*out = new(string)
**out = **in
}
if in.WorkerNumber != nil {
in, out := &in.WorkerNumber, &out.WorkerNumber
*out = new(int32)
**out = **in
}
if in.CRDEstablishedCheckIntervalSec != nil {
in, out := &in.CRDEstablishedCheckIntervalSec, &out.CRDEstablishedCheckIntervalSec
*out = new(int64)
**out = **in
}
if in.CRDEstablishedCheckTimeoutSec != nil {
in, out := &in.CRDEstablishedCheckTimeoutSec, &out.CRDEstablishedCheckTimeoutSec
*out = new(int64)
**out = **in
}
if in.ObjectLocalCacheCreationTimeoutSec != nil {
in, out := &in.ObjectLocalCacheCreationTimeoutSec, &out.ObjectLocalCacheCreationTimeoutSec
*out = new(int64)
**out = **in
}
if in.FrameworkMinRetryDelaySecForTransientConflictFailed != nil {
in, out := &in.FrameworkMinRetryDelaySecForTransientConflictFailed, &out.FrameworkMinRetryDelaySecForTransientConflictFailed
*out = new(int64)
**out = **in
}
if in.FrameworkMaxRetryDelaySecForTransientConflictFailed != nil {
in, out := &in.FrameworkMaxRetryDelaySecForTransientConflictFailed, &out.FrameworkMaxRetryDelaySecForTransientConflictFailed
*out = new(int64)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControllerConfig.
func (in *ControllerConfig) DeepCopy() *ControllerConfig {
if in == nil {
return nil
}
out := new(ControllerConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Framework) DeepCopyInto(out *Framework) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
if in.Status != nil {
in, out := &in.Status, &out.Status
*out = new(FrameworkStatus)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Framework.
func (in *Framework) DeepCopy() *Framework {
if in == nil {
return nil
}
out := new(Framework)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Framework) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *FrameworkAttemptStatus) DeepCopyInto(out *FrameworkAttemptStatus) {
*out = *in
in.StartTime.DeepCopyInto(&out.StartTime)
if in.CompletionTime != nil {
in, out := &in.CompletionTime, &out.CompletionTime
*out = (*in).DeepCopy()
}
if in.InstanceUID != nil {
in, out := &in.InstanceUID, &out.InstanceUID
*out = new(types.UID)
**out = **in
}
if in.ConfigMapUID != nil {
in, out := &in.ConfigMapUID, &out.ConfigMapUID
*out = new(types.UID)
**out = **in
}
if in.CompletionStatus != nil {
in, out := &in.CompletionStatus, &out.CompletionStatus
*out = new(CompletionStatus)
(*in).DeepCopyInto(*out)
}
if in.TaskRoleStatuses != nil {
in, out := &in.TaskRoleStatuses, &out.TaskRoleStatuses
*out = make([]TaskRoleStatus, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FrameworkAttemptStatus.
func (in *FrameworkAttemptStatus) DeepCopy() *FrameworkAttemptStatus {
if in == nil {
return nil
}
out := new(FrameworkAttemptStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *FrameworkList) DeepCopyInto(out *FrameworkList) {
*out = *in
out.TypeMeta = in.TypeMeta
out.ListMeta = in.ListMeta
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Framework, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FrameworkList.
func (in *FrameworkList) DeepCopy() *FrameworkList {
if in == nil {
return nil
}
out := new(FrameworkList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *FrameworkList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *FrameworkSpec) DeepCopyInto(out *FrameworkSpec) {
*out = *in
out.RetryPolicy = in.RetryPolicy
if in.TaskRoles != nil {
in, out := &in.TaskRoles, &out.TaskRoles
*out = make([]TaskRoleSpec, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FrameworkSpec.
func (in *FrameworkSpec) DeepCopy() *FrameworkSpec {
if in == nil {
return nil
}
out := new(FrameworkSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *FrameworkStatus) DeepCopyInto(out *FrameworkStatus) {
*out = *in
in.StartTime.DeepCopyInto(&out.StartTime)
if in.CompletionTime != nil {
in, out := &in.CompletionTime, &out.CompletionTime
*out = (*in).DeepCopy()
}
in.TransitionTime.DeepCopyInto(&out.TransitionTime)
in.RetryPolicyStatus.DeepCopyInto(&out.RetryPolicyStatus)
in.AttemptStatus.DeepCopyInto(&out.AttemptStatus)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FrameworkStatus.
func (in *FrameworkStatus) DeepCopy() *FrameworkStatus {
if in == nil {
return nil
}
out := new(FrameworkStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RetryDecision) DeepCopyInto(out *RetryDecision) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RetryDecision.
func (in *RetryDecision) DeepCopy() *RetryDecision {
if in == nil {
return nil
}
out := new(RetryDecision)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RetryPolicySpec) DeepCopyInto(out *RetryPolicySpec) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RetryPolicySpec.
func (in *RetryPolicySpec) DeepCopy() *RetryPolicySpec {
if in == nil {
return nil
}
out := new(RetryPolicySpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RetryPolicyStatus) DeepCopyInto(out *RetryPolicyStatus) {
*out = *in
if in.RetryDelaySec != nil {
in, out := &in.RetryDelaySec, &out.RetryDelaySec
*out = new(int64)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RetryPolicyStatus.
func (in *RetryPolicyStatus) DeepCopy() *RetryPolicyStatus {
if in == nil {
return nil
}
out := new(RetryPolicyStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TaskAttemptStatus) DeepCopyInto(out *TaskAttemptStatus) {
*out = *in
in.StartTime.DeepCopyInto(&out.StartTime)
if in.CompletionTime != nil {
in, out := &in.CompletionTime, &out.CompletionTime
*out = (*in).DeepCopy()
}
if in.InstanceUID != nil {
in, out := &in.InstanceUID, &out.InstanceUID
*out = new(types.UID)
**out = **in
}
if in.PodUID != nil {
in, out := &in.PodUID, &out.PodUID
*out = new(types.UID)
**out = **in
}
if in.PodIP != nil {
in, out := &in.PodIP, &out.PodIP
*out = new(string)
**out = **in
}
if in.PodHostIP != nil {
in, out := &in.PodHostIP, &out.PodHostIP
*out = new(string)
**out = **in
}
if in.CompletionStatus != nil {
in, out := &in.CompletionStatus, &out.CompletionStatus
*out = new(CompletionStatus)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskAttemptStatus.
func (in *TaskAttemptStatus) DeepCopy() *TaskAttemptStatus {
if in == nil {
return nil
}
out := new(TaskAttemptStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TaskRoleSpec) DeepCopyInto(out *TaskRoleSpec) {
*out = *in
out.FrameworkAttemptCompletionPolicy = in.FrameworkAttemptCompletionPolicy
in.Task.DeepCopyInto(&out.Task)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskRoleSpec.
func (in *TaskRoleSpec) DeepCopy() *TaskRoleSpec {
if in == nil {
return nil
}
out := new(TaskRoleSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TaskRoleStatus) DeepCopyInto(out *TaskRoleStatus) {
*out = *in
if in.TaskStatuses != nil {
in, out := &in.TaskStatuses, &out.TaskStatuses
*out = make([]TaskStatus, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskRoleStatus.
func (in *TaskRoleStatus) DeepCopy() *TaskRoleStatus {
if in == nil {
return nil
}
out := new(TaskRoleStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TaskSpec) DeepCopyInto(out *TaskSpec) {
*out = *in
out.RetryPolicy = in.RetryPolicy
in.Pod.DeepCopyInto(&out.Pod)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskSpec.
func (in *TaskSpec) DeepCopy() *TaskSpec {
if in == nil {
return nil
}
out := new(TaskSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TaskStatus) DeepCopyInto(out *TaskStatus) {
*out = *in
in.StartTime.DeepCopyInto(&out.StartTime)
if in.CompletionTime != nil {
in, out := &in.CompletionTime, &out.CompletionTime
*out = (*in).DeepCopy()
}
in.TransitionTime.DeepCopyInto(&out.TransitionTime)
in.RetryPolicyStatus.DeepCopyInto(&out.RetryPolicyStatus)
in.AttemptStatus.DeepCopyInto(&out.AttemptStatus)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskStatus.
func (in *TaskStatus) DeepCopy() *TaskStatus {
if in == nil {
return nil
}
out := new(TaskStatus)
in.DeepCopyInto(out)
return out
}

Просмотреть файл

@ -0,0 +1,100 @@
/*
Copyright 2018 The Kubernetes 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.
*/
// Code generated by client-gen. DO NOT EDIT.
package versioned
import (
glog "github.com/golang/glog"
frameworkcontrollerv1 "github.com/microsoft/frameworkcontroller/pkg/client/clientset/versioned/typed/frameworkcontroller/v1"
discovery "k8s.io/client-go/discovery"
rest "k8s.io/client-go/rest"
flowcontrol "k8s.io/client-go/util/flowcontrol"
)
type Interface interface {
Discovery() discovery.DiscoveryInterface
FrameworkcontrollerV1() frameworkcontrollerv1.FrameworkcontrollerV1Interface
// Deprecated: please explicitly pick a version if possible.
Frameworkcontroller() frameworkcontrollerv1.FrameworkcontrollerV1Interface
}
// Clientset contains the clients for groups. Each group has exactly one
// version included in a Clientset.
type Clientset struct {
*discovery.DiscoveryClient
frameworkcontrollerV1 *frameworkcontrollerv1.FrameworkcontrollerV1Client
}
// FrameworkcontrollerV1 retrieves the FrameworkcontrollerV1Client
func (c *Clientset) FrameworkcontrollerV1() frameworkcontrollerv1.FrameworkcontrollerV1Interface {
return c.frameworkcontrollerV1
}
// Deprecated: Frameworkcontroller retrieves the default version of FrameworkcontrollerClient.
// Please explicitly pick a version.
func (c *Clientset) Frameworkcontroller() frameworkcontrollerv1.FrameworkcontrollerV1Interface {
return c.frameworkcontrollerV1
}
// Discovery retrieves the DiscoveryClient
func (c *Clientset) Discovery() discovery.DiscoveryInterface {
if c == nil {
return nil
}
return c.DiscoveryClient
}
// NewForConfig creates a new Clientset for the given config.
func NewForConfig(c *rest.Config) (*Clientset, error) {
configShallowCopy := *c
if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 {
configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst)
}
var cs Clientset
var err error
cs.frameworkcontrollerV1, err = frameworkcontrollerv1.NewForConfig(&configShallowCopy)
if err != nil {
return nil, err
}
cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy)
if err != nil {
glog.Errorf("failed to create the DiscoveryClient: %v", err)
return nil, err
}
return &cs, nil
}
// NewForConfigOrDie creates a new Clientset for the given config and
// panics if there is an error in the config.
func NewForConfigOrDie(c *rest.Config) *Clientset {
var cs Clientset
cs.frameworkcontrollerV1 = frameworkcontrollerv1.NewForConfigOrDie(c)
cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c)
return &cs
}
// New creates a new Clientset for the given RESTClient.
func New(c rest.Interface) *Clientset {
var cs Clientset
cs.frameworkcontrollerV1 = frameworkcontrollerv1.New(c)
cs.DiscoveryClient = discovery.NewDiscoveryClient(c)
return &cs
}

Просмотреть файл

@ -0,0 +1,20 @@
/*
Copyright 2018 The Kubernetes 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.
*/
// Code generated by client-gen. DO NOT EDIT.
// This package has the automatically generated clientset.
package versioned

Просмотреть файл

@ -0,0 +1,81 @@
/*
Copyright 2018 The Kubernetes 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.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
clientset "github.com/microsoft/frameworkcontroller/pkg/client/clientset/versioned"
frameworkcontrollerv1 "github.com/microsoft/frameworkcontroller/pkg/client/clientset/versioned/typed/frameworkcontroller/v1"
fakeframeworkcontrollerv1 "github.com/microsoft/frameworkcontroller/pkg/client/clientset/versioned/typed/frameworkcontroller/v1/fake"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/discovery"
fakediscovery "k8s.io/client-go/discovery/fake"
"k8s.io/client-go/testing"
)
// NewSimpleClientset returns a clientset that will respond with the provided objects.
// It's backed by a very simple object tracker that processes creates, updates and deletions as-is,
// without applying any validations and/or defaults. It shouldn't be considered a replacement
// for a real clientset and is mostly useful in simple unit tests.
func NewSimpleClientset(objects ...runtime.Object) *Clientset {
o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder())
for _, obj := range objects {
if err := o.Add(obj); err != nil {
panic(err)
}
}
fakePtr := testing.Fake{}
fakePtr.AddReactor("*", "*", testing.ObjectReaction(o))
fakePtr.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) {
gvr := action.GetResource()
ns := action.GetNamespace()
watch, err := o.Watch(gvr, ns)
if err != nil {
return false, nil, err
}
return true, watch, nil
})
return &Clientset{fakePtr, &fakediscovery.FakeDiscovery{Fake: &fakePtr}}
}
// Clientset implements clientset.Interface. Meant to be embedded into a
// struct to get a default implementation. This makes faking out just the method
// you want to test easier.
type Clientset struct {
testing.Fake
discovery *fakediscovery.FakeDiscovery
}
func (c *Clientset) Discovery() discovery.DiscoveryInterface {
return c.discovery
}
var _ clientset.Interface = &Clientset{}
// FrameworkcontrollerV1 retrieves the FrameworkcontrollerV1Client
func (c *Clientset) FrameworkcontrollerV1() frameworkcontrollerv1.FrameworkcontrollerV1Interface {
return &fakeframeworkcontrollerv1.FakeFrameworkcontrollerV1{Fake: &c.Fake}
}
// Frameworkcontroller retrieves the FrameworkcontrollerV1Client
func (c *Clientset) Frameworkcontroller() frameworkcontrollerv1.FrameworkcontrollerV1Interface {
return &fakeframeworkcontrollerv1.FakeFrameworkcontrollerV1{Fake: &c.Fake}
}

Просмотреть файл

@ -0,0 +1,20 @@
/*
Copyright 2018 The Kubernetes 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.
*/
// Code generated by client-gen. DO NOT EDIT.
// This package has the automatically generated fake clientset.
package fake

Просмотреть файл

@ -0,0 +1,54 @@
/*
Copyright 2018 The Kubernetes 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.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
frameworkcontrollerv1 "github.com/microsoft/frameworkcontroller/pkg/apis/frameworkcontroller/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
schema "k8s.io/apimachinery/pkg/runtime/schema"
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
)
var scheme = runtime.NewScheme()
var codecs = serializer.NewCodecFactory(scheme)
var parameterCodec = runtime.NewParameterCodec(scheme)
func init() {
v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"})
AddToScheme(scheme)
}
// AddToScheme adds all types of this clientset into the given scheme. This allows composition
// of clientsets, like in:
//
// import (
// "k8s.io/client-go/kubernetes"
// clientsetscheme "k8s.io/client-go/kubernetes/scheme"
// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
// )
//
// kclientset, _ := kubernetes.NewForConfig(c)
// aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
//
// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
// correctly.
func AddToScheme(scheme *runtime.Scheme) {
frameworkcontrollerv1.AddToScheme(scheme)
}

Просмотреть файл

@ -0,0 +1,20 @@
/*
Copyright 2018 The Kubernetes 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.
*/
// Code generated by client-gen. DO NOT EDIT.
// This package contains the scheme of the automatically generated clientset.
package scheme

Просмотреть файл

@ -0,0 +1,54 @@
/*
Copyright 2018 The Kubernetes 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.
*/
// Code generated by client-gen. DO NOT EDIT.
package scheme
import (
frameworkcontrollerv1 "github.com/microsoft/frameworkcontroller/pkg/apis/frameworkcontroller/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
schema "k8s.io/apimachinery/pkg/runtime/schema"
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
)
var Scheme = runtime.NewScheme()
var Codecs = serializer.NewCodecFactory(Scheme)
var ParameterCodec = runtime.NewParameterCodec(Scheme)
func init() {
v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"})
AddToScheme(Scheme)
}
// AddToScheme adds all types of this clientset into the given scheme. This allows composition
// of clientsets, like in:
//
// import (
// "k8s.io/client-go/kubernetes"
// clientsetscheme "k8s.io/client-go/kubernetes/scheme"
// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
// )
//
// kclientset, _ := kubernetes.NewForConfig(c)
// aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
//
// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
// correctly.
func AddToScheme(scheme *runtime.Scheme) {
frameworkcontrollerv1.AddToScheme(scheme)
}

Просмотреть файл

@ -0,0 +1,20 @@
/*
Copyright 2018 The Kubernetes 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.
*/
// Code generated by client-gen. DO NOT EDIT.
// This package has the automatically generated typed clients.
package v1

Просмотреть файл

@ -0,0 +1,20 @@
/*
Copyright 2018 The Kubernetes 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.
*/
// Code generated by client-gen. DO NOT EDIT.
// Package fake has the automatically generated clients.
package fake

Просмотреть файл

@ -0,0 +1,128 @@
/*
Copyright 2018 The Kubernetes 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.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
frameworkcontrollerv1 "github.com/microsoft/frameworkcontroller/pkg/apis/frameworkcontroller/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
)
// FakeFrameworks implements FrameworkInterface
type FakeFrameworks struct {
Fake *FakeFrameworkcontrollerV1
ns string
}
var frameworksResource = schema.GroupVersionResource{Group: "frameworkcontroller.microsoft.com", Version: "v1", Resource: "frameworks"}
var frameworksKind = schema.GroupVersionKind{Group: "frameworkcontroller.microsoft.com", Version: "v1", Kind: "Framework"}
// Get takes name of the framework, and returns the corresponding framework object, and an error if there is any.
func (c *FakeFrameworks) Get(name string, options v1.GetOptions) (result *frameworkcontrollerv1.Framework, err error) {
obj, err := c.Fake.
Invokes(testing.NewGetAction(frameworksResource, c.ns, name), &frameworkcontrollerv1.Framework{})
if obj == nil {
return nil, err
}
return obj.(*frameworkcontrollerv1.Framework), err
}
// List takes label and field selectors, and returns the list of Frameworks that match those selectors.
func (c *FakeFrameworks) List(opts v1.ListOptions) (result *frameworkcontrollerv1.FrameworkList, err error) {
obj, err := c.Fake.
Invokes(testing.NewListAction(frameworksResource, frameworksKind, c.ns, opts), &frameworkcontrollerv1.FrameworkList{})
if obj == nil {
return nil, err
}
label, _, _ := testing.ExtractFromListOptions(opts)
if label == nil {
label = labels.Everything()
}
list := &frameworkcontrollerv1.FrameworkList{}
for _, item := range obj.(*frameworkcontrollerv1.FrameworkList).Items {
if label.Matches(labels.Set(item.Labels)) {
list.Items = append(list.Items, item)
}
}
return list, err
}
// Watch returns a watch.Interface that watches the requested frameworks.
func (c *FakeFrameworks) Watch(opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchAction(frameworksResource, c.ns, opts))
}
// Create takes the representation of a framework and creates it. Returns the server's representation of the framework, and an error, if there is any.
func (c *FakeFrameworks) Create(framework *frameworkcontrollerv1.Framework) (result *frameworkcontrollerv1.Framework, err error) {
obj, err := c.Fake.
Invokes(testing.NewCreateAction(frameworksResource, c.ns, framework), &frameworkcontrollerv1.Framework{})
if obj == nil {
return nil, err
}
return obj.(*frameworkcontrollerv1.Framework), err
}
// Update takes the representation of a framework and updates it. Returns the server's representation of the framework, and an error, if there is any.
func (c *FakeFrameworks) Update(framework *frameworkcontrollerv1.Framework) (result *frameworkcontrollerv1.Framework, err error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateAction(frameworksResource, c.ns, framework), &frameworkcontrollerv1.Framework{})
if obj == nil {
return nil, err
}
return obj.(*frameworkcontrollerv1.Framework), err
}
// Delete takes name of the framework and deletes it. Returns an error if one occurs.
func (c *FakeFrameworks) Delete(name string, options *v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(frameworksResource, c.ns, name), &frameworkcontrollerv1.Framework{})
return err
}
// DeleteCollection deletes a collection of objects.
func (c *FakeFrameworks) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
action := testing.NewDeleteCollectionAction(frameworksResource, c.ns, listOptions)
_, err := c.Fake.Invokes(action, &frameworkcontrollerv1.FrameworkList{})
return err
}
// Patch applies the patch and returns the patched framework.
func (c *FakeFrameworks) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *frameworkcontrollerv1.Framework, err error) {
obj, err := c.Fake.
Invokes(testing.NewPatchSubresourceAction(frameworksResource, c.ns, name, data, subresources...), &frameworkcontrollerv1.Framework{})
if obj == nil {
return nil, err
}
return obj.(*frameworkcontrollerv1.Framework), err
}

Просмотреть файл

@ -0,0 +1,40 @@
/*
Copyright 2018 The Kubernetes 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.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
v1 "github.com/microsoft/frameworkcontroller/pkg/client/clientset/versioned/typed/frameworkcontroller/v1"
rest "k8s.io/client-go/rest"
testing "k8s.io/client-go/testing"
)
type FakeFrameworkcontrollerV1 struct {
*testing.Fake
}
func (c *FakeFrameworkcontrollerV1) Frameworks(namespace string) v1.FrameworkInterface {
return &FakeFrameworks{c, namespace}
}
// RESTClient returns a RESTClient that is used to communicate
// with API server by this client implementation.
func (c *FakeFrameworkcontrollerV1) RESTClient() rest.Interface {
var ret *rest.RESTClient
return ret
}

Просмотреть файл

@ -0,0 +1,157 @@
/*
Copyright 2018 The Kubernetes 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.
*/
// Code generated by client-gen. DO NOT EDIT.
package v1
import (
v1 "github.com/microsoft/frameworkcontroller/pkg/apis/frameworkcontroller/v1"
scheme "github.com/microsoft/frameworkcontroller/pkg/client/clientset/versioned/scheme"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
rest "k8s.io/client-go/rest"
)
// FrameworksGetter has a method to return a FrameworkInterface.
// A group's client should implement this interface.
type FrameworksGetter interface {
Frameworks(namespace string) FrameworkInterface
}
// FrameworkInterface has methods to work with Framework resources.
type FrameworkInterface interface {
Create(*v1.Framework) (*v1.Framework, error)
Update(*v1.Framework) (*v1.Framework, error)
Delete(name string, options *metav1.DeleteOptions) error
DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error
Get(name string, options metav1.GetOptions) (*v1.Framework, error)
List(opts metav1.ListOptions) (*v1.FrameworkList, error)
Watch(opts metav1.ListOptions) (watch.Interface, error)
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.Framework, err error)
FrameworkExpansion
}
// frameworks implements FrameworkInterface
type frameworks struct {
client rest.Interface
ns string
}
// newFrameworks returns a Frameworks
func newFrameworks(c *FrameworkcontrollerV1Client, namespace string) *frameworks {
return &frameworks{
client: c.RESTClient(),
ns: namespace,
}
}
// Get takes name of the framework, and returns the corresponding framework object, and an error if there is any.
func (c *frameworks) Get(name string, options metav1.GetOptions) (result *v1.Framework, err error) {
result = &v1.Framework{}
err = c.client.Get().
Namespace(c.ns).
Resource("frameworks").
Name(name).
VersionedParams(&options, scheme.ParameterCodec).
Do().
Into(result)
return
}
// List takes label and field selectors, and returns the list of Frameworks that match those selectors.
func (c *frameworks) List(opts metav1.ListOptions) (result *v1.FrameworkList, err error) {
result = &v1.FrameworkList{}
err = c.client.Get().
Namespace(c.ns).
Resource("frameworks").
VersionedParams(&opts, scheme.ParameterCodec).
Do().
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested frameworks.
func (c *frameworks) Watch(opts metav1.ListOptions) (watch.Interface, error) {
opts.Watch = true
return c.client.Get().
Namespace(c.ns).
Resource("frameworks").
VersionedParams(&opts, scheme.ParameterCodec).
Watch()
}
// Create takes the representation of a framework and creates it. Returns the server's representation of the framework, and an error, if there is any.
func (c *frameworks) Create(framework *v1.Framework) (result *v1.Framework, err error) {
result = &v1.Framework{}
err = c.client.Post().
Namespace(c.ns).
Resource("frameworks").
Body(framework).
Do().
Into(result)
return
}
// Update takes the representation of a framework and updates it. Returns the server's representation of the framework, and an error, if there is any.
func (c *frameworks) Update(framework *v1.Framework) (result *v1.Framework, err error) {
result = &v1.Framework{}
err = c.client.Put().
Namespace(c.ns).
Resource("frameworks").
Name(framework.Name).
Body(framework).
Do().
Into(result)
return
}
// Delete takes name of the framework and deletes it. Returns an error if one occurs.
func (c *frameworks) Delete(name string, options *metav1.DeleteOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("frameworks").
Name(name).
Body(options).
Do().
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *frameworks) DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("frameworks").
VersionedParams(&listOptions, scheme.ParameterCodec).
Body(options).
Do().
Error()
}
// Patch applies the patch and returns the patched framework.
func (c *frameworks) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.Framework, err error) {
result = &v1.Framework{}
err = c.client.Patch(pt).
Namespace(c.ns).
Resource("frameworks").
SubResource(subresources...).
Name(name).
Body(data).
Do().
Into(result)
return
}

Просмотреть файл

@ -0,0 +1,90 @@
/*
Copyright 2018 The Kubernetes 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.
*/
// Code generated by client-gen. DO NOT EDIT.
package v1
import (
v1 "github.com/microsoft/frameworkcontroller/pkg/apis/frameworkcontroller/v1"
"github.com/microsoft/frameworkcontroller/pkg/client/clientset/versioned/scheme"
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
rest "k8s.io/client-go/rest"
)
type FrameworkcontrollerV1Interface interface {
RESTClient() rest.Interface
FrameworksGetter
}
// FrameworkcontrollerV1Client is used to interact with features provided by the frameworkcontroller.microsoft.com group.
type FrameworkcontrollerV1Client struct {
restClient rest.Interface
}
func (c *FrameworkcontrollerV1Client) Frameworks(namespace string) FrameworkInterface {
return newFrameworks(c, namespace)
}
// NewForConfig creates a new FrameworkcontrollerV1Client for the given config.
func NewForConfig(c *rest.Config) (*FrameworkcontrollerV1Client, error) {
config := *c
if err := setConfigDefaults(&config); err != nil {
return nil, err
}
client, err := rest.RESTClientFor(&config)
if err != nil {
return nil, err
}
return &FrameworkcontrollerV1Client{client}, nil
}
// NewForConfigOrDie creates a new FrameworkcontrollerV1Client for the given config and
// panics if there is an error in the config.
func NewForConfigOrDie(c *rest.Config) *FrameworkcontrollerV1Client {
client, err := NewForConfig(c)
if err != nil {
panic(err)
}
return client
}
// New creates a new FrameworkcontrollerV1Client for the given RESTClient.
func New(c rest.Interface) *FrameworkcontrollerV1Client {
return &FrameworkcontrollerV1Client{c}
}
func setConfigDefaults(config *rest.Config) error {
gv := v1.SchemeGroupVersion
config.GroupVersion = &gv
config.APIPath = "/apis"
config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
if config.UserAgent == "" {
config.UserAgent = rest.DefaultKubernetesUserAgent()
}
return nil
}
// RESTClient returns a RESTClient that is used to communicate
// with API server by this client implementation.
func (c *FrameworkcontrollerV1Client) RESTClient() rest.Interface {
if c == nil {
return nil
}
return c.restClient
}

Просмотреть файл

@ -0,0 +1,21 @@
/*
Copyright 2018 The Kubernetes 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.
*/
// Code generated by client-gen. DO NOT EDIT.
package v1
type FrameworkExpansion interface{}

Просмотреть файл

@ -0,0 +1,131 @@
/*
Copyright 2018 The Kubernetes 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.
*/
// Code generated by informer-gen. DO NOT EDIT.
package externalversions
import (
reflect "reflect"
sync "sync"
time "time"
versioned "github.com/microsoft/frameworkcontroller/pkg/client/clientset/versioned"
frameworkcontroller "github.com/microsoft/frameworkcontroller/pkg/client/informers/externalversions/frameworkcontroller"
internalinterfaces "github.com/microsoft/frameworkcontroller/pkg/client/informers/externalversions/internalinterfaces"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
schema "k8s.io/apimachinery/pkg/runtime/schema"
cache "k8s.io/client-go/tools/cache"
)
type sharedInformerFactory struct {
client versioned.Interface
namespace string
tweakListOptions internalinterfaces.TweakListOptionsFunc
lock sync.Mutex
defaultResync time.Duration
informers map[reflect.Type]cache.SharedIndexInformer
// startedInformers is used for tracking which informers have been started.
// This allows Start() to be called multiple times safely.
startedInformers map[reflect.Type]bool
}
// NewSharedInformerFactory constructs a new instance of sharedInformerFactory
func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory {
return NewFilteredSharedInformerFactory(client, defaultResync, v1.NamespaceAll, nil)
}
// NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory.
// Listers obtained via this SharedInformerFactory will be subject to the same filters
// as specified here.
func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory {
return &sharedInformerFactory{
client: client,
namespace: namespace,
tweakListOptions: tweakListOptions,
defaultResync: defaultResync,
informers: make(map[reflect.Type]cache.SharedIndexInformer),
startedInformers: make(map[reflect.Type]bool),
}
}
// Start initializes all requested informers.
func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) {
f.lock.Lock()
defer f.lock.Unlock()
for informerType, informer := range f.informers {
if !f.startedInformers[informerType] {
go informer.Run(stopCh)
f.startedInformers[informerType] = true
}
}
}
// WaitForCacheSync waits for all started informers' cache were synced.
func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool {
informers := func() map[reflect.Type]cache.SharedIndexInformer {
f.lock.Lock()
defer f.lock.Unlock()
informers := map[reflect.Type]cache.SharedIndexInformer{}
for informerType, informer := range f.informers {
if f.startedInformers[informerType] {
informers[informerType] = informer
}
}
return informers
}()
res := map[reflect.Type]bool{}
for informType, informer := range informers {
res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced)
}
return res
}
// InternalInformerFor returns the SharedIndexInformer for obj using an internal
// client.
func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer {
f.lock.Lock()
defer f.lock.Unlock()
informerType := reflect.TypeOf(obj)
informer, exists := f.informers[informerType]
if exists {
return informer
}
informer = newFunc(f.client, f.defaultResync)
f.informers[informerType] = informer
return informer
}
// SharedInformerFactory provides shared informers for resources in all known
// API group versions.
type SharedInformerFactory interface {
internalinterfaces.SharedInformerFactory
ForResource(resource schema.GroupVersionResource) (GenericInformer, error)
WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool
Frameworkcontroller() frameworkcontroller.Interface
}
func (f *sharedInformerFactory) Frameworkcontroller() frameworkcontroller.Interface {
return frameworkcontroller.New(f, f.namespace, f.tweakListOptions)
}

Просмотреть файл

@ -0,0 +1,46 @@
/*
Copyright 2018 The Kubernetes 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.
*/
// Code generated by informer-gen. DO NOT EDIT.
package frameworkcontroller
import (
v1 "github.com/microsoft/frameworkcontroller/pkg/client/informers/externalversions/frameworkcontroller/v1"
internalinterfaces "github.com/microsoft/frameworkcontroller/pkg/client/informers/externalversions/internalinterfaces"
)
// Interface provides access to each of this group's versions.
type Interface interface {
// V1 provides access to shared informers for resources in V1.
V1() v1.Interface
}
type group struct {
factory internalinterfaces.SharedInformerFactory
namespace string
tweakListOptions internalinterfaces.TweakListOptionsFunc
}
// New returns a new Interface.
func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {
return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
}
// V1 returns a new v1.Interface.
func (g *group) V1() v1.Interface {
return v1.New(g.factory, g.namespace, g.tweakListOptions)
}

Просмотреть файл

@ -0,0 +1,89 @@
/*
Copyright 2018 The Kubernetes 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.
*/
// Code generated by informer-gen. DO NOT EDIT.
package v1
import (
time "time"
frameworkcontrollerv1 "github.com/microsoft/frameworkcontroller/pkg/apis/frameworkcontroller/v1"
versioned "github.com/microsoft/frameworkcontroller/pkg/client/clientset/versioned"
internalinterfaces "github.com/microsoft/frameworkcontroller/pkg/client/informers/externalversions/internalinterfaces"
v1 "github.com/microsoft/frameworkcontroller/pkg/client/listers/frameworkcontroller/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
watch "k8s.io/apimachinery/pkg/watch"
cache "k8s.io/client-go/tools/cache"
)
// FrameworkInformer provides access to a shared informer and lister for
// Frameworks.
type FrameworkInformer interface {
Informer() cache.SharedIndexInformer
Lister() v1.FrameworkLister
}
type frameworkInformer struct {
factory internalinterfaces.SharedInformerFactory
tweakListOptions internalinterfaces.TweakListOptionsFunc
namespace string
}
// NewFrameworkInformer constructs a new informer for Framework type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewFrameworkInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
return NewFilteredFrameworkInformer(client, namespace, resyncPeriod, indexers, nil)
}
// NewFilteredFrameworkInformer constructs a new informer for Framework type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewFilteredFrameworkInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
return cache.NewSharedIndexInformer(
&cache.ListWatch{
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.FrameworkcontrollerV1().Frameworks(namespace).List(options)
},
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.FrameworkcontrollerV1().Frameworks(namespace).Watch(options)
},
},
&frameworkcontrollerv1.Framework{},
resyncPeriod,
indexers,
)
}
func (f *frameworkInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
return NewFilteredFrameworkInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
}
func (f *frameworkInformer) Informer() cache.SharedIndexInformer {
return f.factory.InformerFor(&frameworkcontrollerv1.Framework{}, f.defaultInformer)
}
func (f *frameworkInformer) Lister() v1.FrameworkLister {
return v1.NewFrameworkLister(f.Informer().GetIndexer())
}

Просмотреть файл

@ -0,0 +1,45 @@
/*
Copyright 2018 The Kubernetes 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.
*/
// Code generated by informer-gen. DO NOT EDIT.
package v1
import (
internalinterfaces "github.com/microsoft/frameworkcontroller/pkg/client/informers/externalversions/internalinterfaces"
)
// Interface provides access to all the informers in this group version.
type Interface interface {
// Frameworks returns a FrameworkInformer.
Frameworks() FrameworkInformer
}
type version struct {
factory internalinterfaces.SharedInformerFactory
namespace string
tweakListOptions internalinterfaces.TweakListOptionsFunc
}
// New returns a new Interface.
func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {
return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
}
// Frameworks returns a FrameworkInformer.
func (v *version) Frameworks() FrameworkInformer {
return &frameworkInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
}

Просмотреть файл

@ -0,0 +1,62 @@
/*
Copyright 2018 The Kubernetes 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.
*/
// Code generated by informer-gen. DO NOT EDIT.
package externalversions
import (
"fmt"
v1 "github.com/microsoft/frameworkcontroller/pkg/apis/frameworkcontroller/v1"
schema "k8s.io/apimachinery/pkg/runtime/schema"
cache "k8s.io/client-go/tools/cache"
)
// GenericInformer is type of SharedIndexInformer which will locate and delegate to other
// sharedInformers based on type
type GenericInformer interface {
Informer() cache.SharedIndexInformer
Lister() cache.GenericLister
}
type genericInformer struct {
informer cache.SharedIndexInformer
resource schema.GroupResource
}
// Informer returns the SharedIndexInformer.
func (f *genericInformer) Informer() cache.SharedIndexInformer {
return f.informer
}
// Lister returns the GenericLister.
func (f *genericInformer) Lister() cache.GenericLister {
return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource)
}
// ForResource gives generic access to a shared informer of the matching type
// TODO extend this to unknown resources with a client pool
func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) {
switch resource {
// Group=frameworkcontroller.microsoft.com, Version=v1
case v1.SchemeGroupVersion.WithResource("frameworks"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Frameworkcontroller().V1().Frameworks().Informer()}, nil
}
return nil, fmt.Errorf("no informer found for %v", resource)
}

Просмотреть файл

@ -0,0 +1,38 @@
/*
Copyright 2018 The Kubernetes 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.
*/
// Code generated by informer-gen. DO NOT EDIT.
package internalinterfaces
import (
time "time"
versioned "github.com/microsoft/frameworkcontroller/pkg/client/clientset/versioned"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
cache "k8s.io/client-go/tools/cache"
)
type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer
// SharedInformerFactory a small interface to allow for adding an informer without an import cycle
type SharedInformerFactory interface {
Start(stopCh <-chan struct{})
InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer
}
type TweakListOptionsFunc func(*v1.ListOptions)

Просмотреть файл

@ -0,0 +1,27 @@
/*
Copyright 2018 The Kubernetes 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.
*/
// Code generated by lister-gen. DO NOT EDIT.
package v1
// FrameworkListerExpansion allows custom methods to be added to
// FrameworkLister.
type FrameworkListerExpansion interface{}
// FrameworkNamespaceListerExpansion allows custom methods to be added to
// FrameworkNamespaceLister.
type FrameworkNamespaceListerExpansion interface{}

Просмотреть файл

@ -0,0 +1,94 @@
/*
Copyright 2018 The Kubernetes 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.
*/
// Code generated by lister-gen. DO NOT EDIT.
package v1
import (
v1 "github.com/microsoft/frameworkcontroller/pkg/apis/frameworkcontroller/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/cache"
)
// FrameworkLister helps list Frameworks.
type FrameworkLister interface {
// List lists all Frameworks in the indexer.
List(selector labels.Selector) (ret []*v1.Framework, err error)
// Frameworks returns an object that can list and get Frameworks.
Frameworks(namespace string) FrameworkNamespaceLister
FrameworkListerExpansion
}
// frameworkLister implements the FrameworkLister interface.
type frameworkLister struct {
indexer cache.Indexer
}
// NewFrameworkLister returns a new FrameworkLister.
func NewFrameworkLister(indexer cache.Indexer) FrameworkLister {
return &frameworkLister{indexer: indexer}
}
// List lists all Frameworks in the indexer.
func (s *frameworkLister) List(selector labels.Selector) (ret []*v1.Framework, err error) {
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*v1.Framework))
})
return ret, err
}
// Frameworks returns an object that can list and get Frameworks.
func (s *frameworkLister) Frameworks(namespace string) FrameworkNamespaceLister {
return frameworkNamespaceLister{indexer: s.indexer, namespace: namespace}
}
// FrameworkNamespaceLister helps list and get Frameworks.
type FrameworkNamespaceLister interface {
// List lists all Frameworks in the indexer for a given namespace.
List(selector labels.Selector) (ret []*v1.Framework, err error)
// Get retrieves the Framework from the indexer for a given namespace and name.
Get(name string) (*v1.Framework, error)
FrameworkNamespaceListerExpansion
}
// frameworkNamespaceLister implements the FrameworkNamespaceLister
// interface.
type frameworkNamespaceLister struct {
indexer cache.Indexer
namespace string
}
// List lists all Frameworks in the indexer for a given namespace.
func (s frameworkNamespaceLister) List(selector labels.Selector) (ret []*v1.Framework, err error) {
err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
ret = append(ret, m.(*v1.Framework))
})
return ret, err
}
// Get retrieves the Framework from the indexer for a given namespace and name.
func (s frameworkNamespaceLister) Get(name string) (*v1.Framework, error) {
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(v1.Resource("framework"), name)
}
return obj.(*v1.Framework), nil
}

88
pkg/common/common_type.go Normal file
Просмотреть файл

@ -0,0 +1,88 @@
// MIT License
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// 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
package common
import (
"io"
log "github.com/sirupsen/logrus"
)
type Empty struct{}
type T interface{}
type Set struct {
items map[T]Empty
}
func NewSet(items ...T) Set {
s := Set{items: map[T]Empty{}}
for _, item := range items {
s.Add(item)
}
return s
}
func (s Set) Contains(item T) bool {
_, exists := s.items[item]
return exists
}
func (s Set) Add(item T) Set {
s.items[item] = Empty{}
return s
}
func (s Set) Delete(item T) Set {
delete(s.items, item)
return s
}
type ImmutableSet struct {
set Set
}
func NewImmutableSet(items ...T) ImmutableSet {
return ImmutableSet{set: NewSet(items...)}
}
func (s ImmutableSet) Contains(item T) bool {
return s.set.Contains(item)
}
type LogHook struct {
LogLevels []log.Level
LogWriter io.Writer
}
func (lh *LogHook) Levels() []log.Level {
return lh.LogLevels
}
func (lh *LogHook) Fire(entry *log.Entry) error {
str, err := entry.String()
if err != nil {
return err
}
_, err = lh.LogWriter.Write([]byte(str))
return err
}

150
pkg/common/common_util.go Normal file
Просмотреть файл

@ -0,0 +1,150 @@
// MIT License
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// 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
package common
import (
"fmt"
"strings"
"time"
"os"
"gopkg.in/yaml.v2"
log "github.com/sirupsen/logrus"
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"math/rand"
)
func Quote(s string) string {
return `"` + s + `"`
}
func ReferEnvVar(name string) string {
return "$(" + name + ")"
}
func PtrString(o string) *string {
return &o
}
func PtrInt32(o int32) *int32 {
return &o
}
func NilInt32() *int32 {
return nil
}
func PtrInt64(o int64) *int64 {
return &o
}
func PtrFloat64(o float64) *float64 {
return &o
}
func PtrBool(o bool) *bool {
return &o
}
func NilBool() *bool {
return nil
}
func PtrUID(o types.UID) *types.UID {
return &o
}
func PtrUIDStr(s string) *types.UID {
return PtrUID(types.UID(s))
}
func PtrNow() *meta.Time {
now := meta.Now()
return &now
}
func SecToDuration(sec *int64) time.Duration {
return time.Duration(*sec) * time.Second
}
func IsTimeout(leftDuration time.Duration) bool {
// Align with the AddAfter method of the workqueue
return leftDuration <= 0
}
func CurrentLeftDuration(startTime meta.Time, timeoutSec *int64) time.Duration {
currentDuration := time.Since(startTime.Time)
timeoutDuration := SecToDuration(timeoutSec)
leftDuration := timeoutDuration - currentDuration
return leftDuration
}
func InitAll() {
InitLogger()
InitRandSeed()
}
func InitLogger() {
log.SetFormatter(&log.TextFormatter{
DisableColors: true,
// Always log with full timestamp, regardless of whether TTY is attached
DisableTimestamp: false,
FullTimestamp: true,
// Align with k8s.io/apimachinery/pkg/apis/meta/v1.Time
TimestampFormat: time.RFC3339,
})
log.SetLevel(log.DebugLevel)
log.SetOutput(os.Stdout)
}
func LogLines(format string, args ...interface{}) {
lines := strings.Split(fmt.Sprintf(format, args), "\n")
for _, line := range lines {
log.Infof(line)
}
}
func InitRandSeed() {
rand.Seed(time.Now().UTC().UnixNano())
}
// Rand in range [min, max]
func RandInt64(min int64, max int64) int64 {
return min + rand.Int63n(max-min+1)
}
func ToYaml(obj interface{}) string {
yamlBytes, err := yaml.Marshal(obj)
if err != nil {
panic(fmt.Errorf("Failed to marshal Object %#v to YAML: %v", obj, err))
}
return string(yamlBytes)
}
func FromYaml(yamlStr string, objAddr interface{}) {
err := yaml.Unmarshal([]byte(yamlStr), objAddr)
if err != nil {
panic(fmt.Errorf("Failed to unmarshal YAML %#v to Object: %v", yamlStr, err))
}
}

1477
pkg/controller/controller.go Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

119
pkg/util/crd_util.go Normal file
Просмотреть файл

@ -0,0 +1,119 @@
// MIT License
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// 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
package util
import (
"fmt"
"reflect"
log "github.com/sirupsen/logrus"
apiExtensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
apiClient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
apiErrors "k8s.io/apimachinery/pkg/api/errors"
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/rest"
"github.com/microsoft/frameworkcontroller/pkg/common"
)
func PutCRD(
config *rest.Config, crd *apiExtensions.CustomResourceDefinition,
establishedCheckIntervalSec *int64, establishedCheckTimeoutSec *int64) {
client := createCRDClient(config)
err := putCRDInternal(client, crd, establishedCheckIntervalSec, establishedCheckTimeoutSec)
if err != nil {
panic(fmt.Errorf("Failed to put CRD: %v", err))
} else {
log.Infof("Succeeded to put CRD")
}
}
func DeleteCRD(config *rest.Config, name string) {
client := createCRDClient(config)
err := client.ApiextensionsV1beta1().CustomResourceDefinitions().Delete(name, meta.NewDeleteOptions(0))
if err != nil && !apiErrors.IsNotFound(err) {
panic(fmt.Errorf("Failed to delete CRD: %v", err))
} else {
log.Infof("Succeeded to delete CRD")
}
}
func createCRDClient(config *rest.Config) apiClient.Interface {
client, err := apiClient.NewForConfig(config)
if err != nil {
panic(fmt.Errorf("Failed to create CRDClient: %v", err))
}
return client
}
func putCRDInternal(
client apiClient.Interface, newCRD *apiExtensions.CustomResourceDefinition,
establishedCheckIntervalSec *int64, establishedCheckTimeoutSec *int64) error {
remoteCRD, err := client.ApiextensionsV1beta1().CustomResourceDefinitions().Get(newCRD.Name, meta.GetOptions{})
if err == nil {
log.Infof("Update CRD %v", newCRD.Name)
if !reflect.DeepEqual(remoteCRD.Spec, newCRD.Spec) {
updateCRD := remoteCRD
updateCRD.Spec = newCRD.Spec
remoteCRD, err = client.ApiextensionsV1beta1().CustomResourceDefinitions().Update(updateCRD)
if err != nil {
return err
}
}
} else if apiErrors.IsNotFound(err) {
log.Infof("Create CRD %v", newCRD.Name)
remoteCRD, err = client.ApiextensionsV1beta1().CustomResourceDefinitions().Create(newCRD)
if err != nil {
return err
}
} else {
return err
}
if isCRDEstablished(remoteCRD) {
return nil
}
return wait.Poll(
common.SecToDuration(establishedCheckIntervalSec),
common.SecToDuration(establishedCheckTimeoutSec),
func() (bool, error) {
remoteCRD, err = client.ApiextensionsV1beta1().CustomResourceDefinitions().Get(newCRD.Name, meta.GetOptions{})
if err != nil {
return false, err
}
return isCRDEstablished(remoteCRD), nil
})
}
func isCRDEstablished(crd *apiExtensions.CustomResourceDefinition) bool {
for _, cond := range crd.Status.Conditions {
if cond.Status == apiExtensions.ConditionTrue &&
cond.Type == apiExtensions.Established {
return true
}
}
return false
}

122
pkg/util/kube_util.go Normal file
Просмотреть файл

@ -0,0 +1,122 @@
// MIT License
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// 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
package util
import (
"fmt"
log "github.com/sirupsen/logrus"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/rest"
core "k8s.io/api/core/v1"
kubeClient "k8s.io/client-go/kubernetes"
frameworkClient "github.com/microsoft/frameworkcontroller/pkg/client/clientset/versioned"
ci "github.com/microsoft/frameworkcontroller/pkg/apis/frameworkcontroller/v1"
)
func BuildKubeConfig(cConfig *ci.ControllerConfig) (*rest.Config) {
kConfig, err := clientcmd.BuildConfigFromFlags(
*cConfig.KubeApiServerAddress, *cConfig.KubeConfigFilePath)
if err != nil {
panic(fmt.Errorf("Failed to build KubeConfig, please ensure "+
"config kubeApiServerAddress or config kubeConfigFilePath or "+
"${KUBE_APISERVER_ADDRESS} or ${KUBECONFIG} or ${HOME}/.kube/config or "+
"${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT} is valid: "+
"Error: %v",
err))
}
return kConfig
}
func CreateClients(kConfig *rest.Config) (
kubeClient.Interface, frameworkClient.Interface) {
kClient, err := kubeClient.NewForConfig(kConfig)
if err != nil {
panic(fmt.Errorf("Failed to create KubeClient: %v", err))
}
fClient, err := frameworkClient.NewForConfig(kConfig)
if err != nil {
panic(fmt.Errorf("Failed to create FrameworkClient: %v", err))
}
return kClient, fClient
}
func GetKey(obj interface{}) (string, error) {
return cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
}
func SplitKey(key string) (namespace, name string, err error) {
return cache.SplitMetaNamespaceKey(key)
}
// obj could be *core.ConfigMap or cache.DeletedFinalStateUnknown.
func ToConfigMap(obj interface{}) *core.ConfigMap {
cm, ok := obj.(*core.ConfigMap)
if !ok {
deletedFinalStateUnknown, ok := obj.(cache.DeletedFinalStateUnknown)
if !ok {
log.Errorf(
"Failed to convert obj to ConfigMap or DeletedFinalStateUnknown: %#v",
obj)
return nil
}
cm, ok = deletedFinalStateUnknown.Obj.(*core.ConfigMap)
if !ok {
log.Errorf(
"Failed to convert DeletedFinalStateUnknown.Obj to ConfigMap: %#v",
deletedFinalStateUnknown)
return nil
}
}
return cm
}
// obj could be *core.Pod or cache.DeletedFinalStateUnknown.
func ToPod(obj interface{}) *core.Pod {
pod, ok := obj.(*core.Pod)
if !ok {
deletedFinalStateUnknown, ok := obj.(cache.DeletedFinalStateUnknown)
if !ok {
log.Errorf(
"Failed to convert obj to Pod or DeletedFinalStateUnknown: %#v",
obj)
return nil
}
pod, ok = deletedFinalStateUnknown.Obj.(*core.Pod)
if !ok {
log.Errorf(
"Failed to convert DeletedFinalStateUnknown.Obj to Pod: %#v",
deletedFinalStateUnknown)
return nil
}
}
return pod
}

12
vendor/github.com/PuerkitoBio/purell/LICENSE сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,12 @@
Copyright (c) 2012, Martin Angers
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

379
vendor/github.com/PuerkitoBio/purell/purell.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,379 @@
/*
Package purell offers URL normalization as described on the wikipedia page:
http://en.wikipedia.org/wiki/URL_normalization
*/
package purell
import (
"bytes"
"fmt"
"net/url"
"regexp"
"sort"
"strconv"
"strings"
"github.com/PuerkitoBio/urlesc"
"golang.org/x/net/idna"
"golang.org/x/text/unicode/norm"
"golang.org/x/text/width"
)
// A set of normalization flags determines how a URL will
// be normalized.
type NormalizationFlags uint
const (
// Safe normalizations
FlagLowercaseScheme NormalizationFlags = 1 << iota // HTTP://host -> http://host, applied by default in Go1.1
FlagLowercaseHost // http://HOST -> http://host
FlagUppercaseEscapes // http://host/t%ef -> http://host/t%EF
FlagDecodeUnnecessaryEscapes // http://host/t%41 -> http://host/tA
FlagEncodeNecessaryEscapes // http://host/!"#$ -> http://host/%21%22#$
FlagRemoveDefaultPort // http://host:80 -> http://host
FlagRemoveEmptyQuerySeparator // http://host/path? -> http://host/path
// Usually safe normalizations
FlagRemoveTrailingSlash // http://host/path/ -> http://host/path
FlagAddTrailingSlash // http://host/path -> http://host/path/ (should choose only one of these add/remove trailing slash flags)
FlagRemoveDotSegments // http://host/path/./a/b/../c -> http://host/path/a/c
// Unsafe normalizations
FlagRemoveDirectoryIndex // http://host/path/index.html -> http://host/path/
FlagRemoveFragment // http://host/path#fragment -> http://host/path
FlagForceHTTP // https://host -> http://host
FlagRemoveDuplicateSlashes // http://host/path//a///b -> http://host/path/a/b
FlagRemoveWWW // http://www.host/ -> http://host/
FlagAddWWW // http://host/ -> http://www.host/ (should choose only one of these add/remove WWW flags)
FlagSortQuery // http://host/path?c=3&b=2&a=1&b=1 -> http://host/path?a=1&b=1&b=2&c=3
// Normalizations not in the wikipedia article, required to cover tests cases
// submitted by jehiah
FlagDecodeDWORDHost // http://1113982867 -> http://66.102.7.147
FlagDecodeOctalHost // http://0102.0146.07.0223 -> http://66.102.7.147
FlagDecodeHexHost // http://0x42660793 -> http://66.102.7.147
FlagRemoveUnnecessaryHostDots // http://.host../path -> http://host/path
FlagRemoveEmptyPortSeparator // http://host:/path -> http://host/path
// Convenience set of safe normalizations
FlagsSafe NormalizationFlags = FlagLowercaseHost | FlagLowercaseScheme | FlagUppercaseEscapes | FlagDecodeUnnecessaryEscapes | FlagEncodeNecessaryEscapes | FlagRemoveDefaultPort | FlagRemoveEmptyQuerySeparator
// For convenience sets, "greedy" uses the "remove trailing slash" and "remove www. prefix" flags,
// while "non-greedy" uses the "add (or keep) the trailing slash" and "add www. prefix".
// Convenience set of usually safe normalizations (includes FlagsSafe)
FlagsUsuallySafeGreedy NormalizationFlags = FlagsSafe | FlagRemoveTrailingSlash | FlagRemoveDotSegments
FlagsUsuallySafeNonGreedy NormalizationFlags = FlagsSafe | FlagAddTrailingSlash | FlagRemoveDotSegments
// Convenience set of unsafe normalizations (includes FlagsUsuallySafe)
FlagsUnsafeGreedy NormalizationFlags = FlagsUsuallySafeGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagRemoveWWW | FlagSortQuery
FlagsUnsafeNonGreedy NormalizationFlags = FlagsUsuallySafeNonGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagAddWWW | FlagSortQuery
// Convenience set of all available flags
FlagsAllGreedy = FlagsUnsafeGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator
FlagsAllNonGreedy = FlagsUnsafeNonGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator
)
const (
defaultHttpPort = ":80"
defaultHttpsPort = ":443"
)
// Regular expressions used by the normalizations
var rxPort = regexp.MustCompile(`(:\d+)/?$`)
var rxDirIndex = regexp.MustCompile(`(^|/)((?:default|index)\.\w{1,4})$`)
var rxDupSlashes = regexp.MustCompile(`/{2,}`)
var rxDWORDHost = regexp.MustCompile(`^(\d+)((?:\.+)?(?:\:\d*)?)$`)
var rxOctalHost = regexp.MustCompile(`^(0\d*)\.(0\d*)\.(0\d*)\.(0\d*)((?:\.+)?(?:\:\d*)?)$`)
var rxHexHost = regexp.MustCompile(`^0x([0-9A-Fa-f]+)((?:\.+)?(?:\:\d*)?)$`)
var rxHostDots = regexp.MustCompile(`^(.+?)(:\d+)?$`)
var rxEmptyPort = regexp.MustCompile(`:+$`)
// Map of flags to implementation function.
// FlagDecodeUnnecessaryEscapes has no action, since it is done automatically
// by parsing the string as an URL. Same for FlagUppercaseEscapes and FlagRemoveEmptyQuerySeparator.
// Since maps have undefined traversing order, make a slice of ordered keys
var flagsOrder = []NormalizationFlags{
FlagLowercaseScheme,
FlagLowercaseHost,
FlagRemoveDefaultPort,
FlagRemoveDirectoryIndex,
FlagRemoveDotSegments,
FlagRemoveFragment,
FlagForceHTTP, // Must be after remove default port (because https=443/http=80)
FlagRemoveDuplicateSlashes,
FlagRemoveWWW,
FlagAddWWW,
FlagSortQuery,
FlagDecodeDWORDHost,
FlagDecodeOctalHost,
FlagDecodeHexHost,
FlagRemoveUnnecessaryHostDots,
FlagRemoveEmptyPortSeparator,
FlagRemoveTrailingSlash, // These two (add/remove trailing slash) must be last
FlagAddTrailingSlash,
}
// ... and then the map, where order is unimportant
var flags = map[NormalizationFlags]func(*url.URL){
FlagLowercaseScheme: lowercaseScheme,
FlagLowercaseHost: lowercaseHost,
FlagRemoveDefaultPort: removeDefaultPort,
FlagRemoveDirectoryIndex: removeDirectoryIndex,
FlagRemoveDotSegments: removeDotSegments,
FlagRemoveFragment: removeFragment,
FlagForceHTTP: forceHTTP,
FlagRemoveDuplicateSlashes: removeDuplicateSlashes,
FlagRemoveWWW: removeWWW,
FlagAddWWW: addWWW,
FlagSortQuery: sortQuery,
FlagDecodeDWORDHost: decodeDWORDHost,
FlagDecodeOctalHost: decodeOctalHost,
FlagDecodeHexHost: decodeHexHost,
FlagRemoveUnnecessaryHostDots: removeUnncessaryHostDots,
FlagRemoveEmptyPortSeparator: removeEmptyPortSeparator,
FlagRemoveTrailingSlash: removeTrailingSlash,
FlagAddTrailingSlash: addTrailingSlash,
}
// MustNormalizeURLString returns the normalized string, and panics if an error occurs.
// It takes an URL string as input, as well as the normalization flags.
func MustNormalizeURLString(u string, f NormalizationFlags) string {
result, e := NormalizeURLString(u, f)
if e != nil {
panic(e)
}
return result
}
// NormalizeURLString returns the normalized string, or an error if it can't be parsed into an URL object.
// It takes an URL string as input, as well as the normalization flags.
func NormalizeURLString(u string, f NormalizationFlags) (string, error) {
parsed, err := url.Parse(u)
if err != nil {
return "", err
}
if f&FlagLowercaseHost == FlagLowercaseHost {
parsed.Host = strings.ToLower(parsed.Host)
}
// The idna package doesn't fully conform to RFC 5895
// (https://tools.ietf.org/html/rfc5895), so we do it here.
// Taken from Go 1.8 cycle source, courtesy of bradfitz.
// TODO: Remove when (if?) idna package conforms to RFC 5895.
parsed.Host = width.Fold.String(parsed.Host)
parsed.Host = norm.NFC.String(parsed.Host)
if parsed.Host, err = idna.ToASCII(parsed.Host); err != nil {
return "", err
}
return NormalizeURL(parsed, f), nil
}
// NormalizeURL returns the normalized string.
// It takes a parsed URL object as input, as well as the normalization flags.
func NormalizeURL(u *url.URL, f NormalizationFlags) string {
for _, k := range flagsOrder {
if f&k == k {
flags[k](u)
}
}
return urlesc.Escape(u)
}
func lowercaseScheme(u *url.URL) {
if len(u.Scheme) > 0 {
u.Scheme = strings.ToLower(u.Scheme)
}
}
func lowercaseHost(u *url.URL) {
if len(u.Host) > 0 {
u.Host = strings.ToLower(u.Host)
}
}
func removeDefaultPort(u *url.URL) {
if len(u.Host) > 0 {
scheme := strings.ToLower(u.Scheme)
u.Host = rxPort.ReplaceAllStringFunc(u.Host, func(val string) string {
if (scheme == "http" && val == defaultHttpPort) || (scheme == "https" && val == defaultHttpsPort) {
return ""
}
return val
})
}
}
func removeTrailingSlash(u *url.URL) {
if l := len(u.Path); l > 0 {
if strings.HasSuffix(u.Path, "/") {
u.Path = u.Path[:l-1]
}
} else if l = len(u.Host); l > 0 {
if strings.HasSuffix(u.Host, "/") {
u.Host = u.Host[:l-1]
}
}
}
func addTrailingSlash(u *url.URL) {
if l := len(u.Path); l > 0 {
if !strings.HasSuffix(u.Path, "/") {
u.Path += "/"
}
} else if l = len(u.Host); l > 0 {
if !strings.HasSuffix(u.Host, "/") {
u.Host += "/"
}
}
}
func removeDotSegments(u *url.URL) {
if len(u.Path) > 0 {
var dotFree []string
var lastIsDot bool
sections := strings.Split(u.Path, "/")
for _, s := range sections {
if s == ".." {
if len(dotFree) > 0 {
dotFree = dotFree[:len(dotFree)-1]
}
} else if s != "." {
dotFree = append(dotFree, s)
}
lastIsDot = (s == "." || s == "..")
}
// Special case if host does not end with / and new path does not begin with /
u.Path = strings.Join(dotFree, "/")
if u.Host != "" && !strings.HasSuffix(u.Host, "/") && !strings.HasPrefix(u.Path, "/") {
u.Path = "/" + u.Path
}
// Special case if the last segment was a dot, make sure the path ends with a slash
if lastIsDot && !strings.HasSuffix(u.Path, "/") {
u.Path += "/"
}
}
}
func removeDirectoryIndex(u *url.URL) {
if len(u.Path) > 0 {
u.Path = rxDirIndex.ReplaceAllString(u.Path, "$1")
}
}
func removeFragment(u *url.URL) {
u.Fragment = ""
}
func forceHTTP(u *url.URL) {
if strings.ToLower(u.Scheme) == "https" {
u.Scheme = "http"
}
}
func removeDuplicateSlashes(u *url.URL) {
if len(u.Path) > 0 {
u.Path = rxDupSlashes.ReplaceAllString(u.Path, "/")
}
}
func removeWWW(u *url.URL) {
if len(u.Host) > 0 && strings.HasPrefix(strings.ToLower(u.Host), "www.") {
u.Host = u.Host[4:]
}
}
func addWWW(u *url.URL) {
if len(u.Host) > 0 && !strings.HasPrefix(strings.ToLower(u.Host), "www.") {
u.Host = "www." + u.Host
}
}
func sortQuery(u *url.URL) {
q := u.Query()
if len(q) > 0 {
arKeys := make([]string, len(q))
i := 0
for k, _ := range q {
arKeys[i] = k
i++
}
sort.Strings(arKeys)
buf := new(bytes.Buffer)
for _, k := range arKeys {
sort.Strings(q[k])
for _, v := range q[k] {
if buf.Len() > 0 {
buf.WriteRune('&')
}
buf.WriteString(fmt.Sprintf("%s=%s", k, urlesc.QueryEscape(v)))
}
}
// Rebuild the raw query string
u.RawQuery = buf.String()
}
}
func decodeDWORDHost(u *url.URL) {
if len(u.Host) > 0 {
if matches := rxDWORDHost.FindStringSubmatch(u.Host); len(matches) > 2 {
var parts [4]int64
dword, _ := strconv.ParseInt(matches[1], 10, 0)
for i, shift := range []uint{24, 16, 8, 0} {
parts[i] = dword >> shift & 0xFF
}
u.Host = fmt.Sprintf("%d.%d.%d.%d%s", parts[0], parts[1], parts[2], parts[3], matches[2])
}
}
}
func decodeOctalHost(u *url.URL) {
if len(u.Host) > 0 {
if matches := rxOctalHost.FindStringSubmatch(u.Host); len(matches) > 5 {
var parts [4]int64
for i := 1; i <= 4; i++ {
parts[i-1], _ = strconv.ParseInt(matches[i], 8, 0)
}
u.Host = fmt.Sprintf("%d.%d.%d.%d%s", parts[0], parts[1], parts[2], parts[3], matches[5])
}
}
}
func decodeHexHost(u *url.URL) {
if len(u.Host) > 0 {
if matches := rxHexHost.FindStringSubmatch(u.Host); len(matches) > 2 {
// Conversion is safe because of regex validation
parsed, _ := strconv.ParseInt(matches[1], 16, 0)
// Set host as DWORD (base 10) encoded host
u.Host = fmt.Sprintf("%d%s", parsed, matches[2])
// The rest is the same as decoding a DWORD host
decodeDWORDHost(u)
}
}
}
func removeUnncessaryHostDots(u *url.URL) {
if len(u.Host) > 0 {
if matches := rxHostDots.FindStringSubmatch(u.Host); len(matches) > 1 {
// Trim the leading and trailing dots
u.Host = strings.Trim(matches[1], ".")
if len(matches) > 2 {
u.Host += matches[2]
}
}
}
}
func removeEmptyPortSeparator(u *url.URL) {
if len(u.Host) > 0 {
u.Host = rxEmptyPort.ReplaceAllString(u.Host, "")
}
}

27
vendor/github.com/PuerkitoBio/urlesc/LICENSE сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,27 @@
Copyright (c) 2012 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

180
vendor/github.com/PuerkitoBio/urlesc/urlesc.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,180 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package urlesc implements query escaping as per RFC 3986.
// It contains some parts of the net/url package, modified so as to allow
// some reserved characters incorrectly escaped by net/url.
// See https://github.com/golang/go/issues/5684
package urlesc
import (
"bytes"
"net/url"
"strings"
)
type encoding int
const (
encodePath encoding = 1 + iota
encodeUserPassword
encodeQueryComponent
encodeFragment
)
// Return true if the specified character should be escaped when
// appearing in a URL string, according to RFC 3986.
func shouldEscape(c byte, mode encoding) bool {
// §2.3 Unreserved characters (alphanum)
if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' {
return false
}
switch c {
case '-', '.', '_', '~': // §2.3 Unreserved characters (mark)
return false
// §2.2 Reserved characters (reserved)
case ':', '/', '?', '#', '[', ']', '@', // gen-delims
'!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=': // sub-delims
// Different sections of the URL allow a few of
// the reserved characters to appear unescaped.
switch mode {
case encodePath: // §3.3
// The RFC allows sub-delims and : @.
// '/', '[' and ']' can be used to assign meaning to individual path
// segments. This package only manipulates the path as a whole,
// so we allow those as well. That leaves only ? and # to escape.
return c == '?' || c == '#'
case encodeUserPassword: // §3.2.1
// The RFC allows : and sub-delims in
// userinfo. The parsing of userinfo treats ':' as special so we must escape
// all the gen-delims.
return c == ':' || c == '/' || c == '?' || c == '#' || c == '[' || c == ']' || c == '@'
case encodeQueryComponent: // §3.4
// The RFC allows / and ?.
return c != '/' && c != '?'
case encodeFragment: // §4.1
// The RFC text is silent but the grammar allows
// everything, so escape nothing but #
return c == '#'
}
}
// Everything else must be escaped.
return true
}
// QueryEscape escapes the string so it can be safely placed
// inside a URL query.
func QueryEscape(s string) string {
return escape(s, encodeQueryComponent)
}
func escape(s string, mode encoding) string {
spaceCount, hexCount := 0, 0
for i := 0; i < len(s); i++ {
c := s[i]
if shouldEscape(c, mode) {
if c == ' ' && mode == encodeQueryComponent {
spaceCount++
} else {
hexCount++
}
}
}
if spaceCount == 0 && hexCount == 0 {
return s
}
t := make([]byte, len(s)+2*hexCount)
j := 0
for i := 0; i < len(s); i++ {
switch c := s[i]; {
case c == ' ' && mode == encodeQueryComponent:
t[j] = '+'
j++
case shouldEscape(c, mode):
t[j] = '%'
t[j+1] = "0123456789ABCDEF"[c>>4]
t[j+2] = "0123456789ABCDEF"[c&15]
j += 3
default:
t[j] = s[i]
j++
}
}
return string(t)
}
var uiReplacer = strings.NewReplacer(
"%21", "!",
"%27", "'",
"%28", "(",
"%29", ")",
"%2A", "*",
)
// unescapeUserinfo unescapes some characters that need not to be escaped as per RFC3986.
func unescapeUserinfo(s string) string {
return uiReplacer.Replace(s)
}
// Escape reassembles the URL into a valid URL string.
// The general form of the result is one of:
//
// scheme:opaque
// scheme://userinfo@host/path?query#fragment
//
// If u.Opaque is non-empty, String uses the first form;
// otherwise it uses the second form.
//
// In the second form, the following rules apply:
// - if u.Scheme is empty, scheme: is omitted.
// - if u.User is nil, userinfo@ is omitted.
// - if u.Host is empty, host/ is omitted.
// - if u.Scheme and u.Host are empty and u.User is nil,
// the entire scheme://userinfo@host/ is omitted.
// - if u.Host is non-empty and u.Path begins with a /,
// the form host/path does not add its own /.
// - if u.RawQuery is empty, ?query is omitted.
// - if u.Fragment is empty, #fragment is omitted.
func Escape(u *url.URL) string {
var buf bytes.Buffer
if u.Scheme != "" {
buf.WriteString(u.Scheme)
buf.WriteByte(':')
}
if u.Opaque != "" {
buf.WriteString(u.Opaque)
} else {
if u.Scheme != "" || u.Host != "" || u.User != nil {
buf.WriteString("//")
if ui := u.User; ui != nil {
buf.WriteString(unescapeUserinfo(ui.String()))
buf.WriteByte('@')
}
if h := u.Host; h != "" {
buf.WriteString(h)
}
}
if u.Path != "" && u.Path[0] != '/' && u.Host != "" {
buf.WriteByte('/')
}
buf.WriteString(escape(u.Path, encodePath))
}
if u.RawQuery != "" {
buf.WriteByte('?')
buf.WriteString(u.RawQuery)
}
if u.Fragment != "" {
buf.WriteByte('#')
buf.WriteString(escape(u.Fragment, encodeFragment))
}
return buf.String()
}

15
vendor/github.com/davecgh/go-spew/LICENSE сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,15 @@
ISC License
Copyright (c) 2012-2016 Dave Collins <dave@davec.name>
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

152
vendor/github.com/davecgh/go-spew/spew/bypass.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,152 @@
// Copyright (c) 2015-2016 Dave Collins <dave@davec.name>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// NOTE: Due to the following build constraints, this file will only be compiled
// when the code is not running on Google App Engine, compiled by GopherJS, and
// "-tags safe" is not added to the go build command line. The "disableunsafe"
// tag is deprecated and thus should not be used.
// +build !js,!appengine,!safe,!disableunsafe
package spew
import (
"reflect"
"unsafe"
)
const (
// UnsafeDisabled is a build-time constant which specifies whether or
// not access to the unsafe package is available.
UnsafeDisabled = false
// ptrSize is the size of a pointer on the current arch.
ptrSize = unsafe.Sizeof((*byte)(nil))
)
var (
// offsetPtr, offsetScalar, and offsetFlag are the offsets for the
// internal reflect.Value fields. These values are valid before golang
// commit ecccf07e7f9d which changed the format. The are also valid
// after commit 82f48826c6c7 which changed the format again to mirror
// the original format. Code in the init function updates these offsets
// as necessary.
offsetPtr = uintptr(ptrSize)
offsetScalar = uintptr(0)
offsetFlag = uintptr(ptrSize * 2)
// flagKindWidth and flagKindShift indicate various bits that the
// reflect package uses internally to track kind information.
//
// flagRO indicates whether or not the value field of a reflect.Value is
// read-only.
//
// flagIndir indicates whether the value field of a reflect.Value is
// the actual data or a pointer to the data.
//
// These values are valid before golang commit 90a7c3c86944 which
// changed their positions. Code in the init function updates these
// flags as necessary.
flagKindWidth = uintptr(5)
flagKindShift = uintptr(flagKindWidth - 1)
flagRO = uintptr(1 << 0)
flagIndir = uintptr(1 << 1)
)
func init() {
// Older versions of reflect.Value stored small integers directly in the
// ptr field (which is named val in the older versions). Versions
// between commits ecccf07e7f9d and 82f48826c6c7 added a new field named
// scalar for this purpose which unfortunately came before the flag
// field, so the offset of the flag field is different for those
// versions.
//
// This code constructs a new reflect.Value from a known small integer
// and checks if the size of the reflect.Value struct indicates it has
// the scalar field. When it does, the offsets are updated accordingly.
vv := reflect.ValueOf(0xf00)
if unsafe.Sizeof(vv) == (ptrSize * 4) {
offsetScalar = ptrSize * 2
offsetFlag = ptrSize * 3
}
// Commit 90a7c3c86944 changed the flag positions such that the low
// order bits are the kind. This code extracts the kind from the flags
// field and ensures it's the correct type. When it's not, the flag
// order has been changed to the newer format, so the flags are updated
// accordingly.
upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag)
upfv := *(*uintptr)(upf)
flagKindMask := uintptr((1<<flagKindWidth - 1) << flagKindShift)
if (upfv&flagKindMask)>>flagKindShift != uintptr(reflect.Int) {
flagKindShift = 0
flagRO = 1 << 5
flagIndir = 1 << 6
// Commit adf9b30e5594 modified the flags to separate the
// flagRO flag into two bits which specifies whether or not the
// field is embedded. This causes flagIndir to move over a bit
// and means that flagRO is the combination of either of the
// original flagRO bit and the new bit.
//
// This code detects the change by extracting what used to be
// the indirect bit to ensure it's set. When it's not, the flag
// order has been changed to the newer format, so the flags are
// updated accordingly.
if upfv&flagIndir == 0 {
flagRO = 3 << 5
flagIndir = 1 << 7
}
}
}
// unsafeReflectValue converts the passed reflect.Value into a one that bypasses
// the typical safety restrictions preventing access to unaddressable and
// unexported data. It works by digging the raw pointer to the underlying
// value out of the protected value and generating a new unprotected (unsafe)
// reflect.Value to it.
//
// This allows us to check for implementations of the Stringer and error
// interfaces to be used for pretty printing ordinarily unaddressable and
// inaccessible values such as unexported struct fields.
func unsafeReflectValue(v reflect.Value) (rv reflect.Value) {
indirects := 1
vt := v.Type()
upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr)
rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag))
if rvf&flagIndir != 0 {
vt = reflect.PtrTo(v.Type())
indirects++
} else if offsetScalar != 0 {
// The value is in the scalar field when it's not one of the
// reference types.
switch vt.Kind() {
case reflect.Uintptr:
case reflect.Chan:
case reflect.Func:
case reflect.Map:
case reflect.Ptr:
case reflect.UnsafePointer:
default:
upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) +
offsetScalar)
}
}
pv := reflect.NewAt(vt, upv)
rv = pv
for i := 0; i < indirects; i++ {
rv = rv.Elem()
}
return rv
}

38
vendor/github.com/davecgh/go-spew/spew/bypasssafe.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,38 @@
// Copyright (c) 2015-2016 Dave Collins <dave@davec.name>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// NOTE: Due to the following build constraints, this file will only be compiled
// when the code is running on Google App Engine, compiled by GopherJS, or
// "-tags safe" is added to the go build command line. The "disableunsafe"
// tag is deprecated and thus should not be used.
// +build js appengine safe disableunsafe
package spew
import "reflect"
const (
// UnsafeDisabled is a build-time constant which specifies whether or
// not access to the unsafe package is available.
UnsafeDisabled = true
)
// unsafeReflectValue typically converts the passed reflect.Value into a one
// that bypasses the typical safety restrictions preventing access to
// unaddressable and unexported data. However, doing this relies on access to
// the unsafe package. This is a stub version which simply returns the passed
// reflect.Value when the unsafe package is not available.
func unsafeReflectValue(v reflect.Value) reflect.Value {
return v
}

341
vendor/github.com/davecgh/go-spew/spew/common.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,341 @@
/*
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package spew
import (
"bytes"
"fmt"
"io"
"reflect"
"sort"
"strconv"
)
// Some constants in the form of bytes to avoid string overhead. This mirrors
// the technique used in the fmt package.
var (
panicBytes = []byte("(PANIC=")
plusBytes = []byte("+")
iBytes = []byte("i")
trueBytes = []byte("true")
falseBytes = []byte("false")
interfaceBytes = []byte("(interface {})")
commaNewlineBytes = []byte(",\n")
newlineBytes = []byte("\n")
openBraceBytes = []byte("{")
openBraceNewlineBytes = []byte("{\n")
closeBraceBytes = []byte("}")
asteriskBytes = []byte("*")
colonBytes = []byte(":")
colonSpaceBytes = []byte(": ")
openParenBytes = []byte("(")
closeParenBytes = []byte(")")
spaceBytes = []byte(" ")
pointerChainBytes = []byte("->")
nilAngleBytes = []byte("<nil>")
maxNewlineBytes = []byte("<max depth reached>\n")
maxShortBytes = []byte("<max>")
circularBytes = []byte("<already shown>")
circularShortBytes = []byte("<shown>")
invalidAngleBytes = []byte("<invalid>")
openBracketBytes = []byte("[")
closeBracketBytes = []byte("]")
percentBytes = []byte("%")
precisionBytes = []byte(".")
openAngleBytes = []byte("<")
closeAngleBytes = []byte(">")
openMapBytes = []byte("map[")
closeMapBytes = []byte("]")
lenEqualsBytes = []byte("len=")
capEqualsBytes = []byte("cap=")
)
// hexDigits is used to map a decimal value to a hex digit.
var hexDigits = "0123456789abcdef"
// catchPanic handles any panics that might occur during the handleMethods
// calls.
func catchPanic(w io.Writer, v reflect.Value) {
if err := recover(); err != nil {
w.Write(panicBytes)
fmt.Fprintf(w, "%v", err)
w.Write(closeParenBytes)
}
}
// handleMethods attempts to call the Error and String methods on the underlying
// type the passed reflect.Value represents and outputes the result to Writer w.
//
// It handles panics in any called methods by catching and displaying the error
// as the formatted value.
func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) {
// We need an interface to check if the type implements the error or
// Stringer interface. However, the reflect package won't give us an
// interface on certain things like unexported struct fields in order
// to enforce visibility rules. We use unsafe, when it's available,
// to bypass these restrictions since this package does not mutate the
// values.
if !v.CanInterface() {
if UnsafeDisabled {
return false
}
v = unsafeReflectValue(v)
}
// Choose whether or not to do error and Stringer interface lookups against
// the base type or a pointer to the base type depending on settings.
// Technically calling one of these methods with a pointer receiver can
// mutate the value, however, types which choose to satisify an error or
// Stringer interface with a pointer receiver should not be mutating their
// state inside these interface methods.
if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() {
v = unsafeReflectValue(v)
}
if v.CanAddr() {
v = v.Addr()
}
// Is it an error or Stringer?
switch iface := v.Interface().(type) {
case error:
defer catchPanic(w, v)
if cs.ContinueOnMethod {
w.Write(openParenBytes)
w.Write([]byte(iface.Error()))
w.Write(closeParenBytes)
w.Write(spaceBytes)
return false
}
w.Write([]byte(iface.Error()))
return true
case fmt.Stringer:
defer catchPanic(w, v)
if cs.ContinueOnMethod {
w.Write(openParenBytes)
w.Write([]byte(iface.String()))
w.Write(closeParenBytes)
w.Write(spaceBytes)
return false
}
w.Write([]byte(iface.String()))
return true
}
return false
}
// printBool outputs a boolean value as true or false to Writer w.
func printBool(w io.Writer, val bool) {
if val {
w.Write(trueBytes)
} else {
w.Write(falseBytes)
}
}
// printInt outputs a signed integer value to Writer w.
func printInt(w io.Writer, val int64, base int) {
w.Write([]byte(strconv.FormatInt(val, base)))
}
// printUint outputs an unsigned integer value to Writer w.
func printUint(w io.Writer, val uint64, base int) {
w.Write([]byte(strconv.FormatUint(val, base)))
}
// printFloat outputs a floating point value using the specified precision,
// which is expected to be 32 or 64bit, to Writer w.
func printFloat(w io.Writer, val float64, precision int) {
w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision)))
}
// printComplex outputs a complex value using the specified float precision
// for the real and imaginary parts to Writer w.
func printComplex(w io.Writer, c complex128, floatPrecision int) {
r := real(c)
w.Write(openParenBytes)
w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision)))
i := imag(c)
if i >= 0 {
w.Write(plusBytes)
}
w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision)))
w.Write(iBytes)
w.Write(closeParenBytes)
}
// printHexPtr outputs a uintptr formatted as hexidecimal with a leading '0x'
// prefix to Writer w.
func printHexPtr(w io.Writer, p uintptr) {
// Null pointer.
num := uint64(p)
if num == 0 {
w.Write(nilAngleBytes)
return
}
// Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix
buf := make([]byte, 18)
// It's simpler to construct the hex string right to left.
base := uint64(16)
i := len(buf) - 1
for num >= base {
buf[i] = hexDigits[num%base]
num /= base
i--
}
buf[i] = hexDigits[num]
// Add '0x' prefix.
i--
buf[i] = 'x'
i--
buf[i] = '0'
// Strip unused leading bytes.
buf = buf[i:]
w.Write(buf)
}
// valuesSorter implements sort.Interface to allow a slice of reflect.Value
// elements to be sorted.
type valuesSorter struct {
values []reflect.Value
strings []string // either nil or same len and values
cs *ConfigState
}
// newValuesSorter initializes a valuesSorter instance, which holds a set of
// surrogate keys on which the data should be sorted. It uses flags in
// ConfigState to decide if and how to populate those surrogate keys.
func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface {
vs := &valuesSorter{values: values, cs: cs}
if canSortSimply(vs.values[0].Kind()) {
return vs
}
if !cs.DisableMethods {
vs.strings = make([]string, len(values))
for i := range vs.values {
b := bytes.Buffer{}
if !handleMethods(cs, &b, vs.values[i]) {
vs.strings = nil
break
}
vs.strings[i] = b.String()
}
}
if vs.strings == nil && cs.SpewKeys {
vs.strings = make([]string, len(values))
for i := range vs.values {
vs.strings[i] = Sprintf("%#v", vs.values[i].Interface())
}
}
return vs
}
// canSortSimply tests whether a reflect.Kind is a primitive that can be sorted
// directly, or whether it should be considered for sorting by surrogate keys
// (if the ConfigState allows it).
func canSortSimply(kind reflect.Kind) bool {
// This switch parallels valueSortLess, except for the default case.
switch kind {
case reflect.Bool:
return true
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
return true
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
return true
case reflect.Float32, reflect.Float64:
return true
case reflect.String:
return true
case reflect.Uintptr:
return true
case reflect.Array:
return true
}
return false
}
// Len returns the number of values in the slice. It is part of the
// sort.Interface implementation.
func (s *valuesSorter) Len() int {
return len(s.values)
}
// Swap swaps the values at the passed indices. It is part of the
// sort.Interface implementation.
func (s *valuesSorter) Swap(i, j int) {
s.values[i], s.values[j] = s.values[j], s.values[i]
if s.strings != nil {
s.strings[i], s.strings[j] = s.strings[j], s.strings[i]
}
}
// valueSortLess returns whether the first value should sort before the second
// value. It is used by valueSorter.Less as part of the sort.Interface
// implementation.
func valueSortLess(a, b reflect.Value) bool {
switch a.Kind() {
case reflect.Bool:
return !a.Bool() && b.Bool()
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
return a.Int() < b.Int()
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
return a.Uint() < b.Uint()
case reflect.Float32, reflect.Float64:
return a.Float() < b.Float()
case reflect.String:
return a.String() < b.String()
case reflect.Uintptr:
return a.Uint() < b.Uint()
case reflect.Array:
// Compare the contents of both arrays.
l := a.Len()
for i := 0; i < l; i++ {
av := a.Index(i)
bv := b.Index(i)
if av.Interface() == bv.Interface() {
continue
}
return valueSortLess(av, bv)
}
}
return a.String() < b.String()
}
// Less returns whether the value at index i should sort before the
// value at index j. It is part of the sort.Interface implementation.
func (s *valuesSorter) Less(i, j int) bool {
if s.strings == nil {
return valueSortLess(s.values[i], s.values[j])
}
return s.strings[i] < s.strings[j]
}
// sortValues is a sort function that handles both native types and any type that
// can be converted to error or Stringer. Other inputs are sorted according to
// their Value.String() value to ensure display stability.
func sortValues(values []reflect.Value, cs *ConfigState) {
if len(values) == 0 {
return
}
sort.Sort(newValuesSorter(values, cs))
}

306
vendor/github.com/davecgh/go-spew/spew/config.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,306 @@
/*
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package spew
import (
"bytes"
"fmt"
"io"
"os"
)
// ConfigState houses the configuration options used by spew to format and
// display values. There is a global instance, Config, that is used to control
// all top-level Formatter and Dump functionality. Each ConfigState instance
// provides methods equivalent to the top-level functions.
//
// The zero value for ConfigState provides no indentation. You would typically
// want to set it to a space or a tab.
//
// Alternatively, you can use NewDefaultConfig to get a ConfigState instance
// with default settings. See the documentation of NewDefaultConfig for default
// values.
type ConfigState struct {
// Indent specifies the string to use for each indentation level. The
// global config instance that all top-level functions use set this to a
// single space by default. If you would like more indentation, you might
// set this to a tab with "\t" or perhaps two spaces with " ".
Indent string
// MaxDepth controls the maximum number of levels to descend into nested
// data structures. The default, 0, means there is no limit.
//
// NOTE: Circular data structures are properly detected, so it is not
// necessary to set this value unless you specifically want to limit deeply
// nested data structures.
MaxDepth int
// DisableMethods specifies whether or not error and Stringer interfaces are
// invoked for types that implement them.
DisableMethods bool
// DisablePointerMethods specifies whether or not to check for and invoke
// error and Stringer interfaces on types which only accept a pointer
// receiver when the current type is not a pointer.
//
// NOTE: This might be an unsafe action since calling one of these methods
// with a pointer receiver could technically mutate the value, however,
// in practice, types which choose to satisify an error or Stringer
// interface with a pointer receiver should not be mutating their state
// inside these interface methods. As a result, this option relies on
// access to the unsafe package, so it will not have any effect when
// running in environments without access to the unsafe package such as
// Google App Engine or with the "safe" build tag specified.
DisablePointerMethods bool
// DisablePointerAddresses specifies whether to disable the printing of
// pointer addresses. This is useful when diffing data structures in tests.
DisablePointerAddresses bool
// DisableCapacities specifies whether to disable the printing of capacities
// for arrays, slices, maps and channels. This is useful when diffing
// data structures in tests.
DisableCapacities bool
// ContinueOnMethod specifies whether or not recursion should continue once
// a custom error or Stringer interface is invoked. The default, false,
// means it will print the results of invoking the custom error or Stringer
// interface and return immediately instead of continuing to recurse into
// the internals of the data type.
//
// NOTE: This flag does not have any effect if method invocation is disabled
// via the DisableMethods or DisablePointerMethods options.
ContinueOnMethod bool
// SortKeys specifies map keys should be sorted before being printed. Use
// this to have a more deterministic, diffable output. Note that only
// native types (bool, int, uint, floats, uintptr and string) and types
// that support the error or Stringer interfaces (if methods are
// enabled) are supported, with other types sorted according to the
// reflect.Value.String() output which guarantees display stability.
SortKeys bool
// SpewKeys specifies that, as a last resort attempt, map keys should
// be spewed to strings and sorted by those strings. This is only
// considered if SortKeys is true.
SpewKeys bool
}
// Config is the active configuration of the top-level functions.
// The configuration can be changed by modifying the contents of spew.Config.
var Config = ConfigState{Indent: " "}
// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
// passed with a Formatter interface returned by c.NewFormatter. It returns
// the formatted string as a value that satisfies error. See NewFormatter
// for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Errorf(format, c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Errorf(format string, a ...interface{}) (err error) {
return fmt.Errorf(format, c.convertArgs(a)...)
}
// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
// passed with a Formatter interface returned by c.NewFormatter. It returns
// the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Fprint(w, c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
return fmt.Fprint(w, c.convertArgs(a)...)
}
// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
// passed with a Formatter interface returned by c.NewFormatter. It returns
// the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Fprintf(w, format, c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
return fmt.Fprintf(w, format, c.convertArgs(a)...)
}
// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
// passed with a Formatter interface returned by c.NewFormatter. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Fprintln(w, c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
return fmt.Fprintln(w, c.convertArgs(a)...)
}
// Print is a wrapper for fmt.Print that treats each argument as if it were
// passed with a Formatter interface returned by c.NewFormatter. It returns
// the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Print(c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Print(a ...interface{}) (n int, err error) {
return fmt.Print(c.convertArgs(a)...)
}
// Printf is a wrapper for fmt.Printf that treats each argument as if it were
// passed with a Formatter interface returned by c.NewFormatter. It returns
// the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Printf(format, c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Printf(format string, a ...interface{}) (n int, err error) {
return fmt.Printf(format, c.convertArgs(a)...)
}
// Println is a wrapper for fmt.Println that treats each argument as if it were
// passed with a Formatter interface returned by c.NewFormatter. It returns
// the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Println(c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Println(a ...interface{}) (n int, err error) {
return fmt.Println(c.convertArgs(a)...)
}
// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
// passed with a Formatter interface returned by c.NewFormatter. It returns
// the resulting string. See NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Sprint(c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Sprint(a ...interface{}) string {
return fmt.Sprint(c.convertArgs(a)...)
}
// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
// passed with a Formatter interface returned by c.NewFormatter. It returns
// the resulting string. See NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Sprintf(format, c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Sprintf(format string, a ...interface{}) string {
return fmt.Sprintf(format, c.convertArgs(a)...)
}
// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
// were passed with a Formatter interface returned by c.NewFormatter. It
// returns the resulting string. See NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Sprintln(c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Sprintln(a ...interface{}) string {
return fmt.Sprintln(c.convertArgs(a)...)
}
/*
NewFormatter returns a custom formatter that satisfies the fmt.Formatter
interface. As a result, it integrates cleanly with standard fmt package
printing functions. The formatter is useful for inline printing of smaller data
types similar to the standard %v format specifier.
The custom formatter only responds to the %v (most compact), %+v (adds pointer
addresses), %#v (adds types), and %#+v (adds types and pointer addresses) verb
combinations. Any other verbs such as %x and %q will be sent to the the
standard fmt package for formatting. In addition, the custom formatter ignores
the width and precision arguments (however they will still work on the format
specifiers not handled by the custom formatter).
Typically this function shouldn't be called directly. It is much easier to make
use of the custom formatter by calling one of the convenience functions such as
c.Printf, c.Println, or c.Printf.
*/
func (c *ConfigState) NewFormatter(v interface{}) fmt.Formatter {
return newFormatter(c, v)
}
// Fdump formats and displays the passed arguments to io.Writer w. It formats
// exactly the same as Dump.
func (c *ConfigState) Fdump(w io.Writer, a ...interface{}) {
fdump(c, w, a...)
}
/*
Dump displays the passed parameters to standard out with newlines, customizable
indentation, and additional debug information such as complete types and all
pointer addresses used to indirect to the final value. It provides the
following features over the built-in printing facilities provided by the fmt
package:
* Pointers are dereferenced and followed
* Circular data structures are detected and handled properly
* Custom Stringer/error interfaces are optionally invoked, including
on unexported types
* Custom types which only implement the Stringer/error interfaces via
a pointer receiver are optionally invoked when passing non-pointer
variables
* Byte arrays and slices are dumped like the hexdump -C command which
includes offsets, byte values in hex, and ASCII output
The configuration options are controlled by modifying the public members
of c. See ConfigState for options documentation.
See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
get the formatted result as a string.
*/
func (c *ConfigState) Dump(a ...interface{}) {
fdump(c, os.Stdout, a...)
}
// Sdump returns a string with the passed arguments formatted exactly the same
// as Dump.
func (c *ConfigState) Sdump(a ...interface{}) string {
var buf bytes.Buffer
fdump(c, &buf, a...)
return buf.String()
}
// convertArgs accepts a slice of arguments and returns a slice of the same
// length with each argument converted to a spew Formatter interface using
// the ConfigState associated with s.
func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{}) {
formatters = make([]interface{}, len(args))
for index, arg := range args {
formatters[index] = newFormatter(c, arg)
}
return formatters
}
// NewDefaultConfig returns a ConfigState with the following default settings.
//
// Indent: " "
// MaxDepth: 0
// DisableMethods: false
// DisablePointerMethods: false
// ContinueOnMethod: false
// SortKeys: false
func NewDefaultConfig() *ConfigState {
return &ConfigState{Indent: " "}
}

211
vendor/github.com/davecgh/go-spew/spew/doc.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,211 @@
/*
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
Package spew implements a deep pretty printer for Go data structures to aid in
debugging.
A quick overview of the additional features spew provides over the built-in
printing facilities for Go data types are as follows:
* Pointers are dereferenced and followed
* Circular data structures are detected and handled properly
* Custom Stringer/error interfaces are optionally invoked, including
on unexported types
* Custom types which only implement the Stringer/error interfaces via
a pointer receiver are optionally invoked when passing non-pointer
variables
* Byte arrays and slices are dumped like the hexdump -C command which
includes offsets, byte values in hex, and ASCII output (only when using
Dump style)
There are two different approaches spew allows for dumping Go data structures:
* Dump style which prints with newlines, customizable indentation,
and additional debug information such as types and all pointer addresses
used to indirect to the final value
* A custom Formatter interface that integrates cleanly with the standard fmt
package and replaces %v, %+v, %#v, and %#+v to provide inline printing
similar to the default %v while providing the additional functionality
outlined above and passing unsupported format verbs such as %x and %q
along to fmt
Quick Start
This section demonstrates how to quickly get started with spew. See the
sections below for further details on formatting and configuration options.
To dump a variable with full newlines, indentation, type, and pointer
information use Dump, Fdump, or Sdump:
spew.Dump(myVar1, myVar2, ...)
spew.Fdump(someWriter, myVar1, myVar2, ...)
str := spew.Sdump(myVar1, myVar2, ...)
Alternatively, if you would prefer to use format strings with a compacted inline
printing style, use the convenience wrappers Printf, Fprintf, etc with
%v (most compact), %+v (adds pointer addresses), %#v (adds types), or
%#+v (adds types and pointer addresses):
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
Configuration Options
Configuration of spew is handled by fields in the ConfigState type. For
convenience, all of the top-level functions use a global state available
via the spew.Config global.
It is also possible to create a ConfigState instance that provides methods
equivalent to the top-level functions. This allows concurrent configuration
options. See the ConfigState documentation for more details.
The following configuration options are available:
* Indent
String to use for each indentation level for Dump functions.
It is a single space by default. A popular alternative is "\t".
* MaxDepth
Maximum number of levels to descend into nested data structures.
There is no limit by default.
* DisableMethods
Disables invocation of error and Stringer interface methods.
Method invocation is enabled by default.
* DisablePointerMethods
Disables invocation of error and Stringer interface methods on types
which only accept pointer receivers from non-pointer variables.
Pointer method invocation is enabled by default.
* DisablePointerAddresses
DisablePointerAddresses specifies whether to disable the printing of
pointer addresses. This is useful when diffing data structures in tests.
* DisableCapacities
DisableCapacities specifies whether to disable the printing of
capacities for arrays, slices, maps and channels. This is useful when
diffing data structures in tests.
* ContinueOnMethod
Enables recursion into types after invoking error and Stringer interface
methods. Recursion after method invocation is disabled by default.
* SortKeys
Specifies map keys should be sorted before being printed. Use
this to have a more deterministic, diffable output. Note that
only native types (bool, int, uint, floats, uintptr and string)
and types which implement error or Stringer interfaces are
supported with other types sorted according to the
reflect.Value.String() output which guarantees display
stability. Natural map order is used by default.
* SpewKeys
Specifies that, as a last resort attempt, map keys should be
spewed to strings and sorted by those strings. This is only
considered if SortKeys is true.
Dump Usage
Simply call spew.Dump with a list of variables you want to dump:
spew.Dump(myVar1, myVar2, ...)
You may also call spew.Fdump if you would prefer to output to an arbitrary
io.Writer. For example, to dump to standard error:
spew.Fdump(os.Stderr, myVar1, myVar2, ...)
A third option is to call spew.Sdump to get the formatted output as a string:
str := spew.Sdump(myVar1, myVar2, ...)
Sample Dump Output
See the Dump example for details on the setup of the types and variables being
shown here.
(main.Foo) {
unexportedField: (*main.Bar)(0xf84002e210)({
flag: (main.Flag) flagTwo,
data: (uintptr) <nil>
}),
ExportedField: (map[interface {}]interface {}) (len=1) {
(string) (len=3) "one": (bool) true
}
}
Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C
command as shown.
([]uint8) (len=32 cap=32) {
00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
00000020 31 32 |12|
}
Custom Formatter
Spew provides a custom formatter that implements the fmt.Formatter interface
so that it integrates cleanly with standard fmt package printing functions. The
formatter is useful for inline printing of smaller data types similar to the
standard %v format specifier.
The custom formatter only responds to the %v (most compact), %+v (adds pointer
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
combinations. Any other verbs such as %x and %q will be sent to the the
standard fmt package for formatting. In addition, the custom formatter ignores
the width and precision arguments (however they will still work on the format
specifiers not handled by the custom formatter).
Custom Formatter Usage
The simplest way to make use of the spew custom formatter is to call one of the
convenience functions such as spew.Printf, spew.Println, or spew.Printf. The
functions have syntax you are most likely already familiar with:
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
spew.Println(myVar, myVar2)
spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
See the Index for the full list convenience functions.
Sample Formatter Output
Double pointer to a uint8:
%v: <**>5
%+v: <**>(0xf8400420d0->0xf8400420c8)5
%#v: (**uint8)5
%#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5
Pointer to circular struct with a uint8 field and a pointer to itself:
%v: <*>{1 <*><shown>}
%+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)<shown>}
%#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)<shown>}
%#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)<shown>}
See the Printf example for details on the setup of variables being shown
here.
Errors
Since it is possible for custom Stringer/error interfaces to panic, spew
detects them and handles them internally by printing the panic information
inline with the output. Since spew is intended to provide deep pretty printing
capabilities on structures, it intentionally does not return any errors.
*/
package spew

509
vendor/github.com/davecgh/go-spew/spew/dump.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,509 @@
/*
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package spew
import (
"bytes"
"encoding/hex"
"fmt"
"io"
"os"
"reflect"
"regexp"
"strconv"
"strings"
)
var (
// uint8Type is a reflect.Type representing a uint8. It is used to
// convert cgo types to uint8 slices for hexdumping.
uint8Type = reflect.TypeOf(uint8(0))
// cCharRE is a regular expression that matches a cgo char.
// It is used to detect character arrays to hexdump them.
cCharRE = regexp.MustCompile("^.*\\._Ctype_char$")
// cUnsignedCharRE is a regular expression that matches a cgo unsigned
// char. It is used to detect unsigned character arrays to hexdump
// them.
cUnsignedCharRE = regexp.MustCompile("^.*\\._Ctype_unsignedchar$")
// cUint8tCharRE is a regular expression that matches a cgo uint8_t.
// It is used to detect uint8_t arrays to hexdump them.
cUint8tCharRE = regexp.MustCompile("^.*\\._Ctype_uint8_t$")
)
// dumpState contains information about the state of a dump operation.
type dumpState struct {
w io.Writer
depth int
pointers map[uintptr]int
ignoreNextType bool
ignoreNextIndent bool
cs *ConfigState
}
// indent performs indentation according to the depth level and cs.Indent
// option.
func (d *dumpState) indent() {
if d.ignoreNextIndent {
d.ignoreNextIndent = false
return
}
d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth))
}
// unpackValue returns values inside of non-nil interfaces when possible.
// This is useful for data types like structs, arrays, slices, and maps which
// can contain varying types packed inside an interface.
func (d *dumpState) unpackValue(v reflect.Value) reflect.Value {
if v.Kind() == reflect.Interface && !v.IsNil() {
v = v.Elem()
}
return v
}
// dumpPtr handles formatting of pointers by indirecting them as necessary.
func (d *dumpState) dumpPtr(v reflect.Value) {
// Remove pointers at or below the current depth from map used to detect
// circular refs.
for k, depth := range d.pointers {
if depth >= d.depth {
delete(d.pointers, k)
}
}
// Keep list of all dereferenced pointers to show later.
pointerChain := make([]uintptr, 0)
// Figure out how many levels of indirection there are by dereferencing
// pointers and unpacking interfaces down the chain while detecting circular
// references.
nilFound := false
cycleFound := false
indirects := 0
ve := v
for ve.Kind() == reflect.Ptr {
if ve.IsNil() {
nilFound = true
break
}
indirects++
addr := ve.Pointer()
pointerChain = append(pointerChain, addr)
if pd, ok := d.pointers[addr]; ok && pd < d.depth {
cycleFound = true
indirects--
break
}
d.pointers[addr] = d.depth
ve = ve.Elem()
if ve.Kind() == reflect.Interface {
if ve.IsNil() {
nilFound = true
break
}
ve = ve.Elem()
}
}
// Display type information.
d.w.Write(openParenBytes)
d.w.Write(bytes.Repeat(asteriskBytes, indirects))
d.w.Write([]byte(ve.Type().String()))
d.w.Write(closeParenBytes)
// Display pointer information.
if !d.cs.DisablePointerAddresses && len(pointerChain) > 0 {
d.w.Write(openParenBytes)
for i, addr := range pointerChain {
if i > 0 {
d.w.Write(pointerChainBytes)
}
printHexPtr(d.w, addr)
}
d.w.Write(closeParenBytes)
}
// Display dereferenced value.
d.w.Write(openParenBytes)
switch {
case nilFound == true:
d.w.Write(nilAngleBytes)
case cycleFound == true:
d.w.Write(circularBytes)
default:
d.ignoreNextType = true
d.dump(ve)
}
d.w.Write(closeParenBytes)
}
// dumpSlice handles formatting of arrays and slices. Byte (uint8 under
// reflection) arrays and slices are dumped in hexdump -C fashion.
func (d *dumpState) dumpSlice(v reflect.Value) {
// Determine whether this type should be hex dumped or not. Also,
// for types which should be hexdumped, try to use the underlying data
// first, then fall back to trying to convert them to a uint8 slice.
var buf []uint8
doConvert := false
doHexDump := false
numEntries := v.Len()
if numEntries > 0 {
vt := v.Index(0).Type()
vts := vt.String()
switch {
// C types that need to be converted.
case cCharRE.MatchString(vts):
fallthrough
case cUnsignedCharRE.MatchString(vts):
fallthrough
case cUint8tCharRE.MatchString(vts):
doConvert = true
// Try to use existing uint8 slices and fall back to converting
// and copying if that fails.
case vt.Kind() == reflect.Uint8:
// We need an addressable interface to convert the type
// to a byte slice. However, the reflect package won't
// give us an interface on certain things like
// unexported struct fields in order to enforce
// visibility rules. We use unsafe, when available, to
// bypass these restrictions since this package does not
// mutate the values.
vs := v
if !vs.CanInterface() || !vs.CanAddr() {
vs = unsafeReflectValue(vs)
}
if !UnsafeDisabled {
vs = vs.Slice(0, numEntries)
// Use the existing uint8 slice if it can be
// type asserted.
iface := vs.Interface()
if slice, ok := iface.([]uint8); ok {
buf = slice
doHexDump = true
break
}
}
// The underlying data needs to be converted if it can't
// be type asserted to a uint8 slice.
doConvert = true
}
// Copy and convert the underlying type if needed.
if doConvert && vt.ConvertibleTo(uint8Type) {
// Convert and copy each element into a uint8 byte
// slice.
buf = make([]uint8, numEntries)
for i := 0; i < numEntries; i++ {
vv := v.Index(i)
buf[i] = uint8(vv.Convert(uint8Type).Uint())
}
doHexDump = true
}
}
// Hexdump the entire slice as needed.
if doHexDump {
indent := strings.Repeat(d.cs.Indent, d.depth)
str := indent + hex.Dump(buf)
str = strings.Replace(str, "\n", "\n"+indent, -1)
str = strings.TrimRight(str, d.cs.Indent)
d.w.Write([]byte(str))
return
}
// Recursively call dump for each item.
for i := 0; i < numEntries; i++ {
d.dump(d.unpackValue(v.Index(i)))
if i < (numEntries - 1) {
d.w.Write(commaNewlineBytes)
} else {
d.w.Write(newlineBytes)
}
}
}
// dump is the main workhorse for dumping a value. It uses the passed reflect
// value to figure out what kind of object we are dealing with and formats it
// appropriately. It is a recursive function, however circular data structures
// are detected and handled properly.
func (d *dumpState) dump(v reflect.Value) {
// Handle invalid reflect values immediately.
kind := v.Kind()
if kind == reflect.Invalid {
d.w.Write(invalidAngleBytes)
return
}
// Handle pointers specially.
if kind == reflect.Ptr {
d.indent()
d.dumpPtr(v)
return
}
// Print type information unless already handled elsewhere.
if !d.ignoreNextType {
d.indent()
d.w.Write(openParenBytes)
d.w.Write([]byte(v.Type().String()))
d.w.Write(closeParenBytes)
d.w.Write(spaceBytes)
}
d.ignoreNextType = false
// Display length and capacity if the built-in len and cap functions
// work with the value's kind and the len/cap itself is non-zero.
valueLen, valueCap := 0, 0
switch v.Kind() {
case reflect.Array, reflect.Slice, reflect.Chan:
valueLen, valueCap = v.Len(), v.Cap()
case reflect.Map, reflect.String:
valueLen = v.Len()
}
if valueLen != 0 || !d.cs.DisableCapacities && valueCap != 0 {
d.w.Write(openParenBytes)
if valueLen != 0 {
d.w.Write(lenEqualsBytes)
printInt(d.w, int64(valueLen), 10)
}
if !d.cs.DisableCapacities && valueCap != 0 {
if valueLen != 0 {
d.w.Write(spaceBytes)
}
d.w.Write(capEqualsBytes)
printInt(d.w, int64(valueCap), 10)
}
d.w.Write(closeParenBytes)
d.w.Write(spaceBytes)
}
// Call Stringer/error interfaces if they exist and the handle methods flag
// is enabled
if !d.cs.DisableMethods {
if (kind != reflect.Invalid) && (kind != reflect.Interface) {
if handled := handleMethods(d.cs, d.w, v); handled {
return
}
}
}
switch kind {
case reflect.Invalid:
// Do nothing. We should never get here since invalid has already
// been handled above.
case reflect.Bool:
printBool(d.w, v.Bool())
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
printInt(d.w, v.Int(), 10)
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
printUint(d.w, v.Uint(), 10)
case reflect.Float32:
printFloat(d.w, v.Float(), 32)
case reflect.Float64:
printFloat(d.w, v.Float(), 64)
case reflect.Complex64:
printComplex(d.w, v.Complex(), 32)
case reflect.Complex128:
printComplex(d.w, v.Complex(), 64)
case reflect.Slice:
if v.IsNil() {
d.w.Write(nilAngleBytes)
break
}
fallthrough
case reflect.Array:
d.w.Write(openBraceNewlineBytes)
d.depth++
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
d.indent()
d.w.Write(maxNewlineBytes)
} else {
d.dumpSlice(v)
}
d.depth--
d.indent()
d.w.Write(closeBraceBytes)
case reflect.String:
d.w.Write([]byte(strconv.Quote(v.String())))
case reflect.Interface:
// The only time we should get here is for nil interfaces due to
// unpackValue calls.
if v.IsNil() {
d.w.Write(nilAngleBytes)
}
case reflect.Ptr:
// Do nothing. We should never get here since pointers have already
// been handled above.
case reflect.Map:
// nil maps should be indicated as different than empty maps
if v.IsNil() {
d.w.Write(nilAngleBytes)
break
}
d.w.Write(openBraceNewlineBytes)
d.depth++
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
d.indent()
d.w.Write(maxNewlineBytes)
} else {
numEntries := v.Len()
keys := v.MapKeys()
if d.cs.SortKeys {
sortValues(keys, d.cs)
}
for i, key := range keys {
d.dump(d.unpackValue(key))
d.w.Write(colonSpaceBytes)
d.ignoreNextIndent = true
d.dump(d.unpackValue(v.MapIndex(key)))
if i < (numEntries - 1) {
d.w.Write(commaNewlineBytes)
} else {
d.w.Write(newlineBytes)
}
}
}
d.depth--
d.indent()
d.w.Write(closeBraceBytes)
case reflect.Struct:
d.w.Write(openBraceNewlineBytes)
d.depth++
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
d.indent()
d.w.Write(maxNewlineBytes)
} else {
vt := v.Type()
numFields := v.NumField()
for i := 0; i < numFields; i++ {
d.indent()
vtf := vt.Field(i)
d.w.Write([]byte(vtf.Name))
d.w.Write(colonSpaceBytes)
d.ignoreNextIndent = true
d.dump(d.unpackValue(v.Field(i)))
if i < (numFields - 1) {
d.w.Write(commaNewlineBytes)
} else {
d.w.Write(newlineBytes)
}
}
}
d.depth--
d.indent()
d.w.Write(closeBraceBytes)
case reflect.Uintptr:
printHexPtr(d.w, uintptr(v.Uint()))
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
printHexPtr(d.w, v.Pointer())
// There were not any other types at the time this code was written, but
// fall back to letting the default fmt package handle it in case any new
// types are added.
default:
if v.CanInterface() {
fmt.Fprintf(d.w, "%v", v.Interface())
} else {
fmt.Fprintf(d.w, "%v", v.String())
}
}
}
// fdump is a helper function to consolidate the logic from the various public
// methods which take varying writers and config states.
func fdump(cs *ConfigState, w io.Writer, a ...interface{}) {
for _, arg := range a {
if arg == nil {
w.Write(interfaceBytes)
w.Write(spaceBytes)
w.Write(nilAngleBytes)
w.Write(newlineBytes)
continue
}
d := dumpState{w: w, cs: cs}
d.pointers = make(map[uintptr]int)
d.dump(reflect.ValueOf(arg))
d.w.Write(newlineBytes)
}
}
// Fdump formats and displays the passed arguments to io.Writer w. It formats
// exactly the same as Dump.
func Fdump(w io.Writer, a ...interface{}) {
fdump(&Config, w, a...)
}
// Sdump returns a string with the passed arguments formatted exactly the same
// as Dump.
func Sdump(a ...interface{}) string {
var buf bytes.Buffer
fdump(&Config, &buf, a...)
return buf.String()
}
/*
Dump displays the passed parameters to standard out with newlines, customizable
indentation, and additional debug information such as complete types and all
pointer addresses used to indirect to the final value. It provides the
following features over the built-in printing facilities provided by the fmt
package:
* Pointers are dereferenced and followed
* Circular data structures are detected and handled properly
* Custom Stringer/error interfaces are optionally invoked, including
on unexported types
* Custom types which only implement the Stringer/error interfaces via
a pointer receiver are optionally invoked when passing non-pointer
variables
* Byte arrays and slices are dumped like the hexdump -C command which
includes offsets, byte values in hex, and ASCII output
The configuration options are controlled by an exported package global,
spew.Config. See ConfigState for options documentation.
See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
get the formatted result as a string.
*/
func Dump(a ...interface{}) {
fdump(&Config, os.Stdout, a...)
}

419
vendor/github.com/davecgh/go-spew/spew/format.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,419 @@
/*
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package spew
import (
"bytes"
"fmt"
"reflect"
"strconv"
"strings"
)
// supportedFlags is a list of all the character flags supported by fmt package.
const supportedFlags = "0-+# "
// formatState implements the fmt.Formatter interface and contains information
// about the state of a formatting operation. The NewFormatter function can
// be used to get a new Formatter which can be used directly as arguments
// in standard fmt package printing calls.
type formatState struct {
value interface{}
fs fmt.State
depth int
pointers map[uintptr]int
ignoreNextType bool
cs *ConfigState
}
// buildDefaultFormat recreates the original format string without precision
// and width information to pass in to fmt.Sprintf in the case of an
// unrecognized type. Unless new types are added to the language, this
// function won't ever be called.
func (f *formatState) buildDefaultFormat() (format string) {
buf := bytes.NewBuffer(percentBytes)
for _, flag := range supportedFlags {
if f.fs.Flag(int(flag)) {
buf.WriteRune(flag)
}
}
buf.WriteRune('v')
format = buf.String()
return format
}
// constructOrigFormat recreates the original format string including precision
// and width information to pass along to the standard fmt package. This allows
// automatic deferral of all format strings this package doesn't support.
func (f *formatState) constructOrigFormat(verb rune) (format string) {
buf := bytes.NewBuffer(percentBytes)
for _, flag := range supportedFlags {
if f.fs.Flag(int(flag)) {
buf.WriteRune(flag)
}
}
if width, ok := f.fs.Width(); ok {
buf.WriteString(strconv.Itoa(width))
}
if precision, ok := f.fs.Precision(); ok {
buf.Write(precisionBytes)
buf.WriteString(strconv.Itoa(precision))
}
buf.WriteRune(verb)
format = buf.String()
return format
}
// unpackValue returns values inside of non-nil interfaces when possible and
// ensures that types for values which have been unpacked from an interface
// are displayed when the show types flag is also set.
// This is useful for data types like structs, arrays, slices, and maps which
// can contain varying types packed inside an interface.
func (f *formatState) unpackValue(v reflect.Value) reflect.Value {
if v.Kind() == reflect.Interface {
f.ignoreNextType = false
if !v.IsNil() {
v = v.Elem()
}
}
return v
}
// formatPtr handles formatting of pointers by indirecting them as necessary.
func (f *formatState) formatPtr(v reflect.Value) {
// Display nil if top level pointer is nil.
showTypes := f.fs.Flag('#')
if v.IsNil() && (!showTypes || f.ignoreNextType) {
f.fs.Write(nilAngleBytes)
return
}
// Remove pointers at or below the current depth from map used to detect
// circular refs.
for k, depth := range f.pointers {
if depth >= f.depth {
delete(f.pointers, k)
}
}
// Keep list of all dereferenced pointers to possibly show later.
pointerChain := make([]uintptr, 0)
// Figure out how many levels of indirection there are by derferencing
// pointers and unpacking interfaces down the chain while detecting circular
// references.
nilFound := false
cycleFound := false
indirects := 0
ve := v
for ve.Kind() == reflect.Ptr {
if ve.IsNil() {
nilFound = true
break
}
indirects++
addr := ve.Pointer()
pointerChain = append(pointerChain, addr)
if pd, ok := f.pointers[addr]; ok && pd < f.depth {
cycleFound = true
indirects--
break
}
f.pointers[addr] = f.depth
ve = ve.Elem()
if ve.Kind() == reflect.Interface {
if ve.IsNil() {
nilFound = true
break
}
ve = ve.Elem()
}
}
// Display type or indirection level depending on flags.
if showTypes && !f.ignoreNextType {
f.fs.Write(openParenBytes)
f.fs.Write(bytes.Repeat(asteriskBytes, indirects))
f.fs.Write([]byte(ve.Type().String()))
f.fs.Write(closeParenBytes)
} else {
if nilFound || cycleFound {
indirects += strings.Count(ve.Type().String(), "*")
}
f.fs.Write(openAngleBytes)
f.fs.Write([]byte(strings.Repeat("*", indirects)))
f.fs.Write(closeAngleBytes)
}
// Display pointer information depending on flags.
if f.fs.Flag('+') && (len(pointerChain) > 0) {
f.fs.Write(openParenBytes)
for i, addr := range pointerChain {
if i > 0 {
f.fs.Write(pointerChainBytes)
}
printHexPtr(f.fs, addr)
}
f.fs.Write(closeParenBytes)
}
// Display dereferenced value.
switch {
case nilFound == true:
f.fs.Write(nilAngleBytes)
case cycleFound == true:
f.fs.Write(circularShortBytes)
default:
f.ignoreNextType = true
f.format(ve)
}
}
// format is the main workhorse for providing the Formatter interface. It
// uses the passed reflect value to figure out what kind of object we are
// dealing with and formats it appropriately. It is a recursive function,
// however circular data structures are detected and handled properly.
func (f *formatState) format(v reflect.Value) {
// Handle invalid reflect values immediately.
kind := v.Kind()
if kind == reflect.Invalid {
f.fs.Write(invalidAngleBytes)
return
}
// Handle pointers specially.
if kind == reflect.Ptr {
f.formatPtr(v)
return
}
// Print type information unless already handled elsewhere.
if !f.ignoreNextType && f.fs.Flag('#') {
f.fs.Write(openParenBytes)
f.fs.Write([]byte(v.Type().String()))
f.fs.Write(closeParenBytes)
}
f.ignoreNextType = false
// Call Stringer/error interfaces if they exist and the handle methods
// flag is enabled.
if !f.cs.DisableMethods {
if (kind != reflect.Invalid) && (kind != reflect.Interface) {
if handled := handleMethods(f.cs, f.fs, v); handled {
return
}
}
}
switch kind {
case reflect.Invalid:
// Do nothing. We should never get here since invalid has already
// been handled above.
case reflect.Bool:
printBool(f.fs, v.Bool())
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
printInt(f.fs, v.Int(), 10)
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
printUint(f.fs, v.Uint(), 10)
case reflect.Float32:
printFloat(f.fs, v.Float(), 32)
case reflect.Float64:
printFloat(f.fs, v.Float(), 64)
case reflect.Complex64:
printComplex(f.fs, v.Complex(), 32)
case reflect.Complex128:
printComplex(f.fs, v.Complex(), 64)
case reflect.Slice:
if v.IsNil() {
f.fs.Write(nilAngleBytes)
break
}
fallthrough
case reflect.Array:
f.fs.Write(openBracketBytes)
f.depth++
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
f.fs.Write(maxShortBytes)
} else {
numEntries := v.Len()
for i := 0; i < numEntries; i++ {
if i > 0 {
f.fs.Write(spaceBytes)
}
f.ignoreNextType = true
f.format(f.unpackValue(v.Index(i)))
}
}
f.depth--
f.fs.Write(closeBracketBytes)
case reflect.String:
f.fs.Write([]byte(v.String()))
case reflect.Interface:
// The only time we should get here is for nil interfaces due to
// unpackValue calls.
if v.IsNil() {
f.fs.Write(nilAngleBytes)
}
case reflect.Ptr:
// Do nothing. We should never get here since pointers have already
// been handled above.
case reflect.Map:
// nil maps should be indicated as different than empty maps
if v.IsNil() {
f.fs.Write(nilAngleBytes)
break
}
f.fs.Write(openMapBytes)
f.depth++
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
f.fs.Write(maxShortBytes)
} else {
keys := v.MapKeys()
if f.cs.SortKeys {
sortValues(keys, f.cs)
}
for i, key := range keys {
if i > 0 {
f.fs.Write(spaceBytes)
}
f.ignoreNextType = true
f.format(f.unpackValue(key))
f.fs.Write(colonBytes)
f.ignoreNextType = true
f.format(f.unpackValue(v.MapIndex(key)))
}
}
f.depth--
f.fs.Write(closeMapBytes)
case reflect.Struct:
numFields := v.NumField()
f.fs.Write(openBraceBytes)
f.depth++
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
f.fs.Write(maxShortBytes)
} else {
vt := v.Type()
for i := 0; i < numFields; i++ {
if i > 0 {
f.fs.Write(spaceBytes)
}
vtf := vt.Field(i)
if f.fs.Flag('+') || f.fs.Flag('#') {
f.fs.Write([]byte(vtf.Name))
f.fs.Write(colonBytes)
}
f.format(f.unpackValue(v.Field(i)))
}
}
f.depth--
f.fs.Write(closeBraceBytes)
case reflect.Uintptr:
printHexPtr(f.fs, uintptr(v.Uint()))
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
printHexPtr(f.fs, v.Pointer())
// There were not any other types at the time this code was written, but
// fall back to letting the default fmt package handle it if any get added.
default:
format := f.buildDefaultFormat()
if v.CanInterface() {
fmt.Fprintf(f.fs, format, v.Interface())
} else {
fmt.Fprintf(f.fs, format, v.String())
}
}
}
// Format satisfies the fmt.Formatter interface. See NewFormatter for usage
// details.
func (f *formatState) Format(fs fmt.State, verb rune) {
f.fs = fs
// Use standard formatting for verbs that are not v.
if verb != 'v' {
format := f.constructOrigFormat(verb)
fmt.Fprintf(fs, format, f.value)
return
}
if f.value == nil {
if fs.Flag('#') {
fs.Write(interfaceBytes)
}
fs.Write(nilAngleBytes)
return
}
f.format(reflect.ValueOf(f.value))
}
// newFormatter is a helper function to consolidate the logic from the various
// public methods which take varying config states.
func newFormatter(cs *ConfigState, v interface{}) fmt.Formatter {
fs := &formatState{value: v, cs: cs}
fs.pointers = make(map[uintptr]int)
return fs
}
/*
NewFormatter returns a custom formatter that satisfies the fmt.Formatter
interface. As a result, it integrates cleanly with standard fmt package
printing functions. The formatter is useful for inline printing of smaller data
types similar to the standard %v format specifier.
The custom formatter only responds to the %v (most compact), %+v (adds pointer
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
combinations. Any other verbs such as %x and %q will be sent to the the
standard fmt package for formatting. In addition, the custom formatter ignores
the width and precision arguments (however they will still work on the format
specifiers not handled by the custom formatter).
Typically this function shouldn't be called directly. It is much easier to make
use of the custom formatter by calling one of the convenience functions such as
Printf, Println, or Fprintf.
*/
func NewFormatter(v interface{}) fmt.Formatter {
return newFormatter(&Config, v)
}

148
vendor/github.com/davecgh/go-spew/spew/spew.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,148 @@
/*
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package spew
import (
"fmt"
"io"
)
// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the formatted string as a value that satisfies error. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b))
func Errorf(format string, a ...interface{}) (err error) {
return fmt.Errorf(format, convertArgs(a)...)
}
// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b))
func Fprint(w io.Writer, a ...interface{}) (n int, err error) {
return fmt.Fprint(w, convertArgs(a)...)
}
// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b))
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
return fmt.Fprintf(w, format, convertArgs(a)...)
}
// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
// passed with a default Formatter interface returned by NewFormatter. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b))
func Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
return fmt.Fprintln(w, convertArgs(a)...)
}
// Print is a wrapper for fmt.Print that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b))
func Print(a ...interface{}) (n int, err error) {
return fmt.Print(convertArgs(a)...)
}
// Printf is a wrapper for fmt.Printf that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b))
func Printf(format string, a ...interface{}) (n int, err error) {
return fmt.Printf(format, convertArgs(a)...)
}
// Println is a wrapper for fmt.Println that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b))
func Println(a ...interface{}) (n int, err error) {
return fmt.Println(convertArgs(a)...)
}
// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the resulting string. See NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b))
func Sprint(a ...interface{}) string {
return fmt.Sprint(convertArgs(a)...)
}
// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the resulting string. See NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b))
func Sprintf(format string, a ...interface{}) string {
return fmt.Sprintf(format, convertArgs(a)...)
}
// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
// were passed with a default Formatter interface returned by NewFormatter. It
// returns the resulting string. See NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b))
func Sprintln(a ...interface{}) string {
return fmt.Sprintln(convertArgs(a)...)
}
// convertArgs accepts a slice of arguments and returns a slice of the same
// length with each argument converted to a default spew Formatter interface.
func convertArgs(args []interface{}) (formatters []interface{}) {
formatters = make([]interface{}, len(args))
for index, arg := range args {
formatters[index] = NewFormatter(arg)
}
return formatters
}

22
vendor/github.com/emicklei/go-restful/LICENSE сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,22 @@
Copyright (c) 2012,2013 Ernest Micklei
MIT License
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.

123
vendor/github.com/emicklei/go-restful/compress.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,123 @@
package restful
// Copyright 2013 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.
import (
"bufio"
"compress/gzip"
"compress/zlib"
"errors"
"io"
"net"
"net/http"
"strings"
)
// OBSOLETE : use restful.DefaultContainer.EnableContentEncoding(true) to change this setting.
var EnableContentEncoding = false
// CompressingResponseWriter is a http.ResponseWriter that can perform content encoding (gzip and zlib)
type CompressingResponseWriter struct {
writer http.ResponseWriter
compressor io.WriteCloser
encoding string
}
// Header is part of http.ResponseWriter interface
func (c *CompressingResponseWriter) Header() http.Header {
return c.writer.Header()
}
// WriteHeader is part of http.ResponseWriter interface
func (c *CompressingResponseWriter) WriteHeader(status int) {
c.writer.WriteHeader(status)
}
// Write is part of http.ResponseWriter interface
// It is passed through the compressor
func (c *CompressingResponseWriter) Write(bytes []byte) (int, error) {
if c.isCompressorClosed() {
return -1, errors.New("Compressing error: tried to write data using closed compressor")
}
return c.compressor.Write(bytes)
}
// CloseNotify is part of http.CloseNotifier interface
func (c *CompressingResponseWriter) CloseNotify() <-chan bool {
return c.writer.(http.CloseNotifier).CloseNotify()
}
// Close the underlying compressor
func (c *CompressingResponseWriter) Close() error {
if c.isCompressorClosed() {
return errors.New("Compressing error: tried to close already closed compressor")
}
c.compressor.Close()
if ENCODING_GZIP == c.encoding {
currentCompressorProvider.ReleaseGzipWriter(c.compressor.(*gzip.Writer))
}
if ENCODING_DEFLATE == c.encoding {
currentCompressorProvider.ReleaseZlibWriter(c.compressor.(*zlib.Writer))
}
// gc hint needed?
c.compressor = nil
return nil
}
func (c *CompressingResponseWriter) isCompressorClosed() bool {
return nil == c.compressor
}
// Hijack implements the Hijacker interface
// This is especially useful when combining Container.EnabledContentEncoding
// in combination with websockets (for instance gorilla/websocket)
func (c *CompressingResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
hijacker, ok := c.writer.(http.Hijacker)
if !ok {
return nil, nil, errors.New("ResponseWriter doesn't support Hijacker interface")
}
return hijacker.Hijack()
}
// WantsCompressedResponse reads the Accept-Encoding header to see if and which encoding is requested.
func wantsCompressedResponse(httpRequest *http.Request) (bool, string) {
header := httpRequest.Header.Get(HEADER_AcceptEncoding)
gi := strings.Index(header, ENCODING_GZIP)
zi := strings.Index(header, ENCODING_DEFLATE)
// use in order of appearance
if gi == -1 {
return zi != -1, ENCODING_DEFLATE
} else if zi == -1 {
return gi != -1, ENCODING_GZIP
} else {
if gi < zi {
return true, ENCODING_GZIP
}
return true, ENCODING_DEFLATE
}
}
// NewCompressingResponseWriter create a CompressingResponseWriter for a known encoding = {gzip,deflate}
func NewCompressingResponseWriter(httpWriter http.ResponseWriter, encoding string) (*CompressingResponseWriter, error) {
httpWriter.Header().Set(HEADER_ContentEncoding, encoding)
c := new(CompressingResponseWriter)
c.writer = httpWriter
var err error
if ENCODING_GZIP == encoding {
w := currentCompressorProvider.AcquireGzipWriter()
w.Reset(httpWriter)
c.compressor = w
c.encoding = ENCODING_GZIP
} else if ENCODING_DEFLATE == encoding {
w := currentCompressorProvider.AcquireZlibWriter()
w.Reset(httpWriter)
c.compressor = w
c.encoding = ENCODING_DEFLATE
} else {
return nil, errors.New("Unknown encoding:" + encoding)
}
return c, err
}

103
vendor/github.com/emicklei/go-restful/compressor_cache.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,103 @@
package restful
// Copyright 2015 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.
import (
"compress/gzip"
"compress/zlib"
)
// BoundedCachedCompressors is a CompressorProvider that uses a cache with a fixed amount
// of writers and readers (resources).
// If a new resource is acquired and all are in use, it will return a new unmanaged resource.
type BoundedCachedCompressors struct {
gzipWriters chan *gzip.Writer
gzipReaders chan *gzip.Reader
zlibWriters chan *zlib.Writer
writersCapacity int
readersCapacity int
}
// NewBoundedCachedCompressors returns a new, with filled cache, BoundedCachedCompressors.
func NewBoundedCachedCompressors(writersCapacity, readersCapacity int) *BoundedCachedCompressors {
b := &BoundedCachedCompressors{
gzipWriters: make(chan *gzip.Writer, writersCapacity),
gzipReaders: make(chan *gzip.Reader, readersCapacity),
zlibWriters: make(chan *zlib.Writer, writersCapacity),
writersCapacity: writersCapacity,
readersCapacity: readersCapacity,
}
for ix := 0; ix < writersCapacity; ix++ {
b.gzipWriters <- newGzipWriter()
b.zlibWriters <- newZlibWriter()
}
for ix := 0; ix < readersCapacity; ix++ {
b.gzipReaders <- newGzipReader()
}
return b
}
// AcquireGzipWriter returns an resettable *gzip.Writer. Needs to be released.
func (b *BoundedCachedCompressors) AcquireGzipWriter() *gzip.Writer {
var writer *gzip.Writer
select {
case writer, _ = <-b.gzipWriters:
default:
// return a new unmanaged one
writer = newGzipWriter()
}
return writer
}
// ReleaseGzipWriter accepts a writer (does not have to be one that was cached)
// only when the cache has room for it. It will ignore it otherwise.
func (b *BoundedCachedCompressors) ReleaseGzipWriter(w *gzip.Writer) {
// forget the unmanaged ones
if len(b.gzipWriters) < b.writersCapacity {
b.gzipWriters <- w
}
}
// AcquireGzipReader returns a *gzip.Reader. Needs to be released.
func (b *BoundedCachedCompressors) AcquireGzipReader() *gzip.Reader {
var reader *gzip.Reader
select {
case reader, _ = <-b.gzipReaders:
default:
// return a new unmanaged one
reader = newGzipReader()
}
return reader
}
// ReleaseGzipReader accepts a reader (does not have to be one that was cached)
// only when the cache has room for it. It will ignore it otherwise.
func (b *BoundedCachedCompressors) ReleaseGzipReader(r *gzip.Reader) {
// forget the unmanaged ones
if len(b.gzipReaders) < b.readersCapacity {
b.gzipReaders <- r
}
}
// AcquireZlibWriter returns an resettable *zlib.Writer. Needs to be released.
func (b *BoundedCachedCompressors) AcquireZlibWriter() *zlib.Writer {
var writer *zlib.Writer
select {
case writer, _ = <-b.zlibWriters:
default:
// return a new unmanaged one
writer = newZlibWriter()
}
return writer
}
// ReleaseZlibWriter accepts a writer (does not have to be one that was cached)
// only when the cache has room for it. It will ignore it otherwise.
func (b *BoundedCachedCompressors) ReleaseZlibWriter(w *zlib.Writer) {
// forget the unmanaged ones
if len(b.zlibWriters) < b.writersCapacity {
b.zlibWriters <- w
}
}

91
vendor/github.com/emicklei/go-restful/compressor_pools.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,91 @@
package restful
// Copyright 2015 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.
import (
"bytes"
"compress/gzip"
"compress/zlib"
"sync"
)
// SyncPoolCompessors is a CompressorProvider that use the standard sync.Pool.
type SyncPoolCompessors struct {
GzipWriterPool *sync.Pool
GzipReaderPool *sync.Pool
ZlibWriterPool *sync.Pool
}
// NewSyncPoolCompessors returns a new ("empty") SyncPoolCompessors.
func NewSyncPoolCompessors() *SyncPoolCompessors {
return &SyncPoolCompessors{
GzipWriterPool: &sync.Pool{
New: func() interface{} { return newGzipWriter() },
},
GzipReaderPool: &sync.Pool{
New: func() interface{} { return newGzipReader() },
},
ZlibWriterPool: &sync.Pool{
New: func() interface{} { return newZlibWriter() },
},
}
}
func (s *SyncPoolCompessors) AcquireGzipWriter() *gzip.Writer {
return s.GzipWriterPool.Get().(*gzip.Writer)
}
func (s *SyncPoolCompessors) ReleaseGzipWriter(w *gzip.Writer) {
s.GzipWriterPool.Put(w)
}
func (s *SyncPoolCompessors) AcquireGzipReader() *gzip.Reader {
return s.GzipReaderPool.Get().(*gzip.Reader)
}
func (s *SyncPoolCompessors) ReleaseGzipReader(r *gzip.Reader) {
s.GzipReaderPool.Put(r)
}
func (s *SyncPoolCompessors) AcquireZlibWriter() *zlib.Writer {
return s.ZlibWriterPool.Get().(*zlib.Writer)
}
func (s *SyncPoolCompessors) ReleaseZlibWriter(w *zlib.Writer) {
s.ZlibWriterPool.Put(w)
}
func newGzipWriter() *gzip.Writer {
// create with an empty bytes writer; it will be replaced before using the gzipWriter
writer, err := gzip.NewWriterLevel(new(bytes.Buffer), gzip.BestSpeed)
if err != nil {
panic(err.Error())
}
return writer
}
func newGzipReader() *gzip.Reader {
// create with an empty reader (but with GZIP header); it will be replaced before using the gzipReader
// we can safely use currentCompressProvider because it is set on package initialization.
w := currentCompressorProvider.AcquireGzipWriter()
defer currentCompressorProvider.ReleaseGzipWriter(w)
b := new(bytes.Buffer)
w.Reset(b)
w.Flush()
w.Close()
reader, err := gzip.NewReader(bytes.NewReader(b.Bytes()))
if err != nil {
panic(err.Error())
}
return reader
}
func newZlibWriter() *zlib.Writer {
writer, err := zlib.NewWriterLevel(new(bytes.Buffer), gzip.BestSpeed)
if err != nil {
panic(err.Error())
}
return writer
}

54
vendor/github.com/emicklei/go-restful/compressors.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,54 @@
package restful
// Copyright 2015 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.
import (
"compress/gzip"
"compress/zlib"
)
// CompressorProvider describes a component that can provider compressors for the std methods.
type CompressorProvider interface {
// Returns a *gzip.Writer which needs to be released later.
// Before using it, call Reset().
AcquireGzipWriter() *gzip.Writer
// Releases an acquired *gzip.Writer.
ReleaseGzipWriter(w *gzip.Writer)
// Returns a *gzip.Reader which needs to be released later.
AcquireGzipReader() *gzip.Reader
// Releases an acquired *gzip.Reader.
ReleaseGzipReader(w *gzip.Reader)
// Returns a *zlib.Writer which needs to be released later.
// Before using it, call Reset().
AcquireZlibWriter() *zlib.Writer
// Releases an acquired *zlib.Writer.
ReleaseZlibWriter(w *zlib.Writer)
}
// DefaultCompressorProvider is the actual provider of compressors (zlib or gzip).
var currentCompressorProvider CompressorProvider
func init() {
currentCompressorProvider = NewSyncPoolCompessors()
}
// CurrentCompressorProvider returns the current CompressorProvider.
// It is initialized using a SyncPoolCompessors.
func CurrentCompressorProvider() CompressorProvider {
return currentCompressorProvider
}
// SetCompressorProvider sets the actual provider of compressors (zlib or gzip).
func SetCompressorProvider(p CompressorProvider) {
if p == nil {
panic("cannot set compressor provider to nil")
}
currentCompressorProvider = p
}

30
vendor/github.com/emicklei/go-restful/constants.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,30 @@
package restful
// Copyright 2013 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.
const (
MIME_XML = "application/xml" // Accept or Content-Type used in Consumes() and/or Produces()
MIME_JSON = "application/json" // Accept or Content-Type used in Consumes() and/or Produces()
MIME_OCTET = "application/octet-stream" // If Content-Type is not present in request, use the default
HEADER_Allow = "Allow"
HEADER_Accept = "Accept"
HEADER_Origin = "Origin"
HEADER_ContentType = "Content-Type"
HEADER_LastModified = "Last-Modified"
HEADER_AcceptEncoding = "Accept-Encoding"
HEADER_ContentEncoding = "Content-Encoding"
HEADER_AccessControlExposeHeaders = "Access-Control-Expose-Headers"
HEADER_AccessControlRequestMethod = "Access-Control-Request-Method"
HEADER_AccessControlRequestHeaders = "Access-Control-Request-Headers"
HEADER_AccessControlAllowMethods = "Access-Control-Allow-Methods"
HEADER_AccessControlAllowOrigin = "Access-Control-Allow-Origin"
HEADER_AccessControlAllowCredentials = "Access-Control-Allow-Credentials"
HEADER_AccessControlAllowHeaders = "Access-Control-Allow-Headers"
HEADER_AccessControlMaxAge = "Access-Control-Max-Age"
ENCODING_GZIP = "gzip"
ENCODING_DEFLATE = "deflate"
)

371
vendor/github.com/emicklei/go-restful/container.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,371 @@
package restful
// Copyright 2013 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.
import (
"bytes"
"errors"
"fmt"
"net/http"
"os"
"runtime"
"strings"
"sync"
"github.com/emicklei/go-restful/log"
)
// Container holds a collection of WebServices and a http.ServeMux to dispatch http requests.
// The requests are further dispatched to routes of WebServices using a RouteSelector
type Container struct {
webServicesLock sync.RWMutex
webServices []*WebService
ServeMux *http.ServeMux
isRegisteredOnRoot bool
containerFilters []FilterFunction
doNotRecover bool // default is true
recoverHandleFunc RecoverHandleFunction
serviceErrorHandleFunc ServiceErrorHandleFunction
router RouteSelector // default is a CurlyRouter (RouterJSR311 is a slower alternative)
contentEncodingEnabled bool // default is false
}
// NewContainer creates a new Container using a new ServeMux and default router (CurlyRouter)
func NewContainer() *Container {
return &Container{
webServices: []*WebService{},
ServeMux: http.NewServeMux(),
isRegisteredOnRoot: false,
containerFilters: []FilterFunction{},
doNotRecover: true,
recoverHandleFunc: logStackOnRecover,
serviceErrorHandleFunc: writeServiceError,
router: CurlyRouter{},
contentEncodingEnabled: false}
}
// RecoverHandleFunction declares functions that can be used to handle a panic situation.
// The first argument is what recover() returns. The second must be used to communicate an error response.
type RecoverHandleFunction func(interface{}, http.ResponseWriter)
// RecoverHandler changes the default function (logStackOnRecover) to be called
// when a panic is detected. DoNotRecover must be have its default value (=false).
func (c *Container) RecoverHandler(handler RecoverHandleFunction) {
c.recoverHandleFunc = handler
}
// ServiceErrorHandleFunction declares functions that can be used to handle a service error situation.
// The first argument is the service error, the second is the request that resulted in the error and
// the third must be used to communicate an error response.
type ServiceErrorHandleFunction func(ServiceError, *Request, *Response)
// ServiceErrorHandler changes the default function (writeServiceError) to be called
// when a ServiceError is detected.
func (c *Container) ServiceErrorHandler(handler ServiceErrorHandleFunction) {
c.serviceErrorHandleFunc = handler
}
// DoNotRecover controls whether panics will be caught to return HTTP 500.
// If set to true, Route functions are responsible for handling any error situation.
// Default value is true.
func (c *Container) DoNotRecover(doNot bool) {
c.doNotRecover = doNot
}
// Router changes the default Router (currently CurlyRouter)
func (c *Container) Router(aRouter RouteSelector) {
c.router = aRouter
}
// EnableContentEncoding (default=false) allows for GZIP or DEFLATE encoding of responses.
func (c *Container) EnableContentEncoding(enabled bool) {
c.contentEncodingEnabled = enabled
}
// Add a WebService to the Container. It will detect duplicate root paths and exit in that case.
func (c *Container) Add(service *WebService) *Container {
c.webServicesLock.Lock()
defer c.webServicesLock.Unlock()
// if rootPath was not set then lazy initialize it
if len(service.rootPath) == 0 {
service.Path("/")
}
// cannot have duplicate root paths
for _, each := range c.webServices {
if each.RootPath() == service.RootPath() {
log.Printf("[restful] WebService with duplicate root path detected:['%v']", each)
os.Exit(1)
}
}
// If not registered on root then add specific mapping
if !c.isRegisteredOnRoot {
c.isRegisteredOnRoot = c.addHandler(service, c.ServeMux)
}
c.webServices = append(c.webServices, service)
return c
}
// addHandler may set a new HandleFunc for the serveMux
// this function must run inside the critical region protected by the webServicesLock.
// returns true if the function was registered on root ("/")
func (c *Container) addHandler(service *WebService, serveMux *http.ServeMux) bool {
pattern := fixedPrefixPath(service.RootPath())
// check if root path registration is needed
if "/" == pattern || "" == pattern {
serveMux.HandleFunc("/", c.dispatch)
return true
}
// detect if registration already exists
alreadyMapped := false
for _, each := range c.webServices {
if each.RootPath() == service.RootPath() {
alreadyMapped = true
break
}
}
if !alreadyMapped {
serveMux.HandleFunc(pattern, c.dispatch)
if !strings.HasSuffix(pattern, "/") {
serveMux.HandleFunc(pattern+"/", c.dispatch)
}
}
return false
}
func (c *Container) Remove(ws *WebService) error {
if c.ServeMux == http.DefaultServeMux {
errMsg := fmt.Sprintf("[restful] cannot remove a WebService from a Container using the DefaultServeMux: ['%v']", ws)
log.Print(errMsg)
return errors.New(errMsg)
}
c.webServicesLock.Lock()
defer c.webServicesLock.Unlock()
// build a new ServeMux and re-register all WebServices
newServeMux := http.NewServeMux()
newServices := []*WebService{}
newIsRegisteredOnRoot := false
for _, each := range c.webServices {
if each.rootPath != ws.rootPath {
// If not registered on root then add specific mapping
if !newIsRegisteredOnRoot {
newIsRegisteredOnRoot = c.addHandler(each, newServeMux)
}
newServices = append(newServices, each)
}
}
c.webServices, c.ServeMux, c.isRegisteredOnRoot = newServices, newServeMux, newIsRegisteredOnRoot
return nil
}
// logStackOnRecover is the default RecoverHandleFunction and is called
// when DoNotRecover is false and the recoverHandleFunc is not set for the container.
// Default implementation logs the stacktrace and writes the stacktrace on the response.
// This may be a security issue as it exposes sourcecode information.
func logStackOnRecover(panicReason interface{}, httpWriter http.ResponseWriter) {
var buffer bytes.Buffer
buffer.WriteString(fmt.Sprintf("[restful] recover from panic situation: - %v\r\n", panicReason))
for i := 2; ; i += 1 {
_, file, line, ok := runtime.Caller(i)
if !ok {
break
}
buffer.WriteString(fmt.Sprintf(" %s:%d\r\n", file, line))
}
log.Print(buffer.String())
httpWriter.WriteHeader(http.StatusInternalServerError)
httpWriter.Write(buffer.Bytes())
}
// writeServiceError is the default ServiceErrorHandleFunction and is called
// when a ServiceError is returned during route selection. Default implementation
// calls resp.WriteErrorString(err.Code, err.Message)
func writeServiceError(err ServiceError, req *Request, resp *Response) {
resp.WriteErrorString(err.Code, err.Message)
}
// Dispatch the incoming Http Request to a matching WebService.
func (c *Container) Dispatch(httpWriter http.ResponseWriter, httpRequest *http.Request) {
if httpWriter == nil {
panic("httpWriter cannot be nil")
}
if httpRequest == nil {
panic("httpRequest cannot be nil")
}
c.dispatch(httpWriter, httpRequest)
}
// Dispatch the incoming Http Request to a matching WebService.
func (c *Container) dispatch(httpWriter http.ResponseWriter, httpRequest *http.Request) {
writer := httpWriter
// CompressingResponseWriter should be closed after all operations are done
defer func() {
if compressWriter, ok := writer.(*CompressingResponseWriter); ok {
compressWriter.Close()
}
}()
// Instal panic recovery unless told otherwise
if !c.doNotRecover { // catch all for 500 response
defer func() {
if r := recover(); r != nil {
c.recoverHandleFunc(r, writer)
return
}
}()
}
// Detect if compression is needed
// assume without compression, test for override
if c.contentEncodingEnabled {
doCompress, encoding := wantsCompressedResponse(httpRequest)
if doCompress {
var err error
writer, err = NewCompressingResponseWriter(httpWriter, encoding)
if err != nil {
log.Print("[restful] unable to install compressor: ", err)
httpWriter.WriteHeader(http.StatusInternalServerError)
return
}
}
}
// Find best match Route ; err is non nil if no match was found
var webService *WebService
var route *Route
var err error
func() {
c.webServicesLock.RLock()
defer c.webServicesLock.RUnlock()
webService, route, err = c.router.SelectRoute(
c.webServices,
httpRequest)
}()
if err != nil {
// a non-200 response has already been written
// run container filters anyway ; they should not touch the response...
chain := FilterChain{Filters: c.containerFilters, Target: func(req *Request, resp *Response) {
switch err.(type) {
case ServiceError:
ser := err.(ServiceError)
c.serviceErrorHandleFunc(ser, req, resp)
}
// TODO
}}
chain.ProcessFilter(NewRequest(httpRequest), NewResponse(writer))
return
}
pathProcessor, routerProcessesPath := c.router.(PathProcessor)
if !routerProcessesPath {
pathProcessor = defaultPathProcessor{}
}
pathParams := pathProcessor.ExtractParameters(route, webService, httpRequest.URL.Path)
wrappedRequest, wrappedResponse := route.wrapRequestResponse(writer, httpRequest, pathParams)
// pass through filters (if any)
if len(c.containerFilters)+len(webService.filters)+len(route.Filters) > 0 {
// compose filter chain
allFilters := []FilterFunction{}
allFilters = append(allFilters, c.containerFilters...)
allFilters = append(allFilters, webService.filters...)
allFilters = append(allFilters, route.Filters...)
chain := FilterChain{Filters: allFilters, Target: func(req *Request, resp *Response) {
// handle request by route after passing all filters
route.Function(wrappedRequest, wrappedResponse)
}}
chain.ProcessFilter(wrappedRequest, wrappedResponse)
} else {
// no filters, handle request by route
route.Function(wrappedRequest, wrappedResponse)
}
}
// fixedPrefixPath returns the fixed part of the partspec ; it may include template vars {}
func fixedPrefixPath(pathspec string) string {
varBegin := strings.Index(pathspec, "{")
if -1 == varBegin {
return pathspec
}
return pathspec[:varBegin]
}
// ServeHTTP implements net/http.Handler therefore a Container can be a Handler in a http.Server
func (c *Container) ServeHTTP(httpwriter http.ResponseWriter, httpRequest *http.Request) {
c.ServeMux.ServeHTTP(httpwriter, httpRequest)
}
// Handle registers the handler for the given pattern. If a handler already exists for pattern, Handle panics.
func (c *Container) Handle(pattern string, handler http.Handler) {
c.ServeMux.Handle(pattern, handler)
}
// HandleWithFilter registers the handler for the given pattern.
// Container's filter chain is applied for handler.
// If a handler already exists for pattern, HandleWithFilter panics.
func (c *Container) HandleWithFilter(pattern string, handler http.Handler) {
f := func(httpResponse http.ResponseWriter, httpRequest *http.Request) {
if len(c.containerFilters) == 0 {
handler.ServeHTTP(httpResponse, httpRequest)
return
}
chain := FilterChain{Filters: c.containerFilters, Target: func(req *Request, resp *Response) {
handler.ServeHTTP(httpResponse, httpRequest)
}}
chain.ProcessFilter(NewRequest(httpRequest), NewResponse(httpResponse))
}
c.Handle(pattern, http.HandlerFunc(f))
}
// Filter appends a container FilterFunction. These are called before dispatching
// a http.Request to a WebService from the container
func (c *Container) Filter(filter FilterFunction) {
c.containerFilters = append(c.containerFilters, filter)
}
// RegisteredWebServices returns the collections of added WebServices
func (c *Container) RegisteredWebServices() []*WebService {
c.webServicesLock.RLock()
defer c.webServicesLock.RUnlock()
result := make([]*WebService, len(c.webServices))
for ix := range c.webServices {
result[ix] = c.webServices[ix]
}
return result
}
// computeAllowedMethods returns a list of HTTP methods that are valid for a Request
func (c *Container) computeAllowedMethods(req *Request) []string {
// Go through all RegisteredWebServices() and all its Routes to collect the options
methods := []string{}
requestPath := req.Request.URL.Path
for _, ws := range c.RegisteredWebServices() {
matches := ws.pathExpr.Matcher.FindStringSubmatch(requestPath)
if matches != nil {
finalMatch := matches[len(matches)-1]
for _, rt := range ws.Routes() {
matches := rt.pathExpr.Matcher.FindStringSubmatch(finalMatch)
if matches != nil {
lastMatch := matches[len(matches)-1]
if lastMatch == "" || lastMatch == "/" { // do not include if value is neither empty nor /.
methods = append(methods, rt.Method)
}
}
}
}
}
// methods = append(methods, "OPTIONS") not sure about this
return methods
}
// newBasicRequestResponse creates a pair of Request,Response from its http versions.
// It is basic because no parameter or (produces) content-type information is given.
func newBasicRequestResponse(httpWriter http.ResponseWriter, httpRequest *http.Request) (*Request, *Response) {
resp := NewResponse(httpWriter)
resp.requestAccept = httpRequest.Header.Get(HEADER_Accept)
return NewRequest(httpRequest), resp
}

202
vendor/github.com/emicklei/go-restful/cors_filter.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,202 @@
package restful
// Copyright 2013 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.
import (
"regexp"
"strconv"
"strings"
)
// CrossOriginResourceSharing is used to create a Container Filter that implements CORS.
// Cross-origin resource sharing (CORS) is a mechanism that allows JavaScript on a web page
// to make XMLHttpRequests to another domain, not the domain the JavaScript originated from.
//
// http://en.wikipedia.org/wiki/Cross-origin_resource_sharing
// http://enable-cors.org/server.html
// http://www.html5rocks.com/en/tutorials/cors/#toc-handling-a-not-so-simple-request
type CrossOriginResourceSharing struct {
ExposeHeaders []string // list of Header names
AllowedHeaders []string // list of Header names
AllowedDomains []string // list of allowed values for Http Origin. An allowed value can be a regular expression to support subdomain matching. If empty all are allowed.
AllowedMethods []string
MaxAge int // number of seconds before requiring new Options request
CookiesAllowed bool
Container *Container
allowedOriginPatterns []*regexp.Regexp // internal field for origin regexp check.
}
// Filter is a filter function that implements the CORS flow as documented on http://enable-cors.org/server.html
// and http://www.html5rocks.com/static/images/cors_server_flowchart.png
func (c CrossOriginResourceSharing) Filter(req *Request, resp *Response, chain *FilterChain) {
origin := req.Request.Header.Get(HEADER_Origin)
if len(origin) == 0 {
if trace {
traceLogger.Print("no Http header Origin set")
}
chain.ProcessFilter(req, resp)
return
}
if !c.isOriginAllowed(origin) { // check whether this origin is allowed
if trace {
traceLogger.Printf("HTTP Origin:%s is not part of %v, neither matches any part of %v", origin, c.AllowedDomains, c.allowedOriginPatterns)
}
chain.ProcessFilter(req, resp)
return
}
if req.Request.Method != "OPTIONS" {
c.doActualRequest(req, resp)
chain.ProcessFilter(req, resp)
return
}
if acrm := req.Request.Header.Get(HEADER_AccessControlRequestMethod); acrm != "" {
c.doPreflightRequest(req, resp)
} else {
c.doActualRequest(req, resp)
chain.ProcessFilter(req, resp)
return
}
}
func (c CrossOriginResourceSharing) doActualRequest(req *Request, resp *Response) {
c.setOptionsHeaders(req, resp)
// continue processing the response
}
func (c *CrossOriginResourceSharing) doPreflightRequest(req *Request, resp *Response) {
if len(c.AllowedMethods) == 0 {
if c.Container == nil {
c.AllowedMethods = DefaultContainer.computeAllowedMethods(req)
} else {
c.AllowedMethods = c.Container.computeAllowedMethods(req)
}
}
acrm := req.Request.Header.Get(HEADER_AccessControlRequestMethod)
if !c.isValidAccessControlRequestMethod(acrm, c.AllowedMethods) {
if trace {
traceLogger.Printf("Http header %s:%s is not in %v",
HEADER_AccessControlRequestMethod,
acrm,
c.AllowedMethods)
}
return
}
acrhs := req.Request.Header.Get(HEADER_AccessControlRequestHeaders)
if len(acrhs) > 0 {
for _, each := range strings.Split(acrhs, ",") {
if !c.isValidAccessControlRequestHeader(strings.Trim(each, " ")) {
if trace {
traceLogger.Printf("Http header %s:%s is not in %v",
HEADER_AccessControlRequestHeaders,
acrhs,
c.AllowedHeaders)
}
return
}
}
}
resp.AddHeader(HEADER_AccessControlAllowMethods, strings.Join(c.AllowedMethods, ","))
resp.AddHeader(HEADER_AccessControlAllowHeaders, acrhs)
c.setOptionsHeaders(req, resp)
// return http 200 response, no body
}
func (c CrossOriginResourceSharing) setOptionsHeaders(req *Request, resp *Response) {
c.checkAndSetExposeHeaders(resp)
c.setAllowOriginHeader(req, resp)
c.checkAndSetAllowCredentials(resp)
if c.MaxAge > 0 {
resp.AddHeader(HEADER_AccessControlMaxAge, strconv.Itoa(c.MaxAge))
}
}
func (c CrossOriginResourceSharing) isOriginAllowed(origin string) bool {
if len(origin) == 0 {
return false
}
if len(c.AllowedDomains) == 0 {
return true
}
allowed := false
for _, domain := range c.AllowedDomains {
if domain == origin {
allowed = true
break
}
}
if !allowed {
if len(c.allowedOriginPatterns) == 0 {
// compile allowed domains to allowed origin patterns
allowedOriginRegexps, err := compileRegexps(c.AllowedDomains)
if err != nil {
return false
}
c.allowedOriginPatterns = allowedOriginRegexps
}
for _, pattern := range c.allowedOriginPatterns {
if allowed = pattern.MatchString(origin); allowed {
break
}
}
}
return allowed
}
func (c CrossOriginResourceSharing) setAllowOriginHeader(req *Request, resp *Response) {
origin := req.Request.Header.Get(HEADER_Origin)
if c.isOriginAllowed(origin) {
resp.AddHeader(HEADER_AccessControlAllowOrigin, origin)
}
}
func (c CrossOriginResourceSharing) checkAndSetExposeHeaders(resp *Response) {
if len(c.ExposeHeaders) > 0 {
resp.AddHeader(HEADER_AccessControlExposeHeaders, strings.Join(c.ExposeHeaders, ","))
}
}
func (c CrossOriginResourceSharing) checkAndSetAllowCredentials(resp *Response) {
if c.CookiesAllowed {
resp.AddHeader(HEADER_AccessControlAllowCredentials, "true")
}
}
func (c CrossOriginResourceSharing) isValidAccessControlRequestMethod(method string, allowedMethods []string) bool {
for _, each := range allowedMethods {
if each == method {
return true
}
}
return false
}
func (c CrossOriginResourceSharing) isValidAccessControlRequestHeader(header string) bool {
for _, each := range c.AllowedHeaders {
if strings.ToLower(each) == strings.ToLower(header) {
return true
}
}
return false
}
// Take a list of strings and compile them into a list of regular expressions.
func compileRegexps(regexpStrings []string) ([]*regexp.Regexp, error) {
regexps := []*regexp.Regexp{}
for _, regexpStr := range regexpStrings {
r, err := regexp.Compile(regexpStr)
if err != nil {
return regexps, err
}
regexps = append(regexps, r)
}
return regexps, nil
}

164
vendor/github.com/emicklei/go-restful/curly.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,164 @@
package restful
// Copyright 2013 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.
import (
"net/http"
"regexp"
"sort"
"strings"
)
// CurlyRouter expects Routes with paths that contain zero or more parameters in curly brackets.
type CurlyRouter struct{}
// SelectRoute is part of the Router interface and returns the best match
// for the WebService and its Route for the given Request.
func (c CurlyRouter) SelectRoute(
webServices []*WebService,
httpRequest *http.Request) (selectedService *WebService, selected *Route, err error) {
requestTokens := tokenizePath(httpRequest.URL.Path)
detectedService := c.detectWebService(requestTokens, webServices)
if detectedService == nil {
if trace {
traceLogger.Printf("no WebService was found to match URL path:%s\n", httpRequest.URL.Path)
}
return nil, nil, NewError(http.StatusNotFound, "404: Page Not Found")
}
candidateRoutes := c.selectRoutes(detectedService, requestTokens)
if len(candidateRoutes) == 0 {
if trace {
traceLogger.Printf("no Route in WebService with path %s was found to match URL path:%s\n", detectedService.rootPath, httpRequest.URL.Path)
}
return detectedService, nil, NewError(http.StatusNotFound, "404: Page Not Found")
}
selectedRoute, err := c.detectRoute(candidateRoutes, httpRequest)
if selectedRoute == nil {
return detectedService, nil, err
}
return detectedService, selectedRoute, nil
}
// selectRoutes return a collection of Route from a WebService that matches the path tokens from the request.
func (c CurlyRouter) selectRoutes(ws *WebService, requestTokens []string) sortableCurlyRoutes {
candidates := sortableCurlyRoutes{}
for _, each := range ws.routes {
matches, paramCount, staticCount := c.matchesRouteByPathTokens(each.pathParts, requestTokens)
if matches {
candidates.add(curlyRoute{each, paramCount, staticCount}) // TODO make sure Routes() return pointers?
}
}
sort.Sort(sort.Reverse(candidates))
return candidates
}
// matchesRouteByPathTokens computes whether it matches, howmany parameters do match and what the number of static path elements are.
func (c CurlyRouter) matchesRouteByPathTokens(routeTokens, requestTokens []string) (matches bool, paramCount int, staticCount int) {
if len(routeTokens) < len(requestTokens) {
// proceed in matching only if last routeToken is wildcard
count := len(routeTokens)
if count == 0 || !strings.HasSuffix(routeTokens[count-1], "*}") {
return false, 0, 0
}
// proceed
}
for i, routeToken := range routeTokens {
if i == len(requestTokens) {
// reached end of request path
return false, 0, 0
}
requestToken := requestTokens[i]
if strings.HasPrefix(routeToken, "{") {
paramCount++
if colon := strings.Index(routeToken, ":"); colon != -1 {
// match by regex
matchesToken, matchesRemainder := c.regularMatchesPathToken(routeToken, colon, requestToken)
if !matchesToken {
return false, 0, 0
}
if matchesRemainder {
break
}
}
} else { // no { prefix
if requestToken != routeToken {
return false, 0, 0
}
staticCount++
}
}
return true, paramCount, staticCount
}
// regularMatchesPathToken tests whether the regular expression part of routeToken matches the requestToken or all remaining tokens
// format routeToken is {someVar:someExpression}, e.g. {zipcode:[\d][\d][\d][\d][A-Z][A-Z]}
func (c CurlyRouter) regularMatchesPathToken(routeToken string, colon int, requestToken string) (matchesToken bool, matchesRemainder bool) {
regPart := routeToken[colon+1 : len(routeToken)-1]
if regPart == "*" {
if trace {
traceLogger.Printf("wildcard parameter detected in route token %s that matches %s\n", routeToken, requestToken)
}
return true, true
}
matched, err := regexp.MatchString(regPart, requestToken)
return (matched && err == nil), false
}
var jsr311Router = RouterJSR311{}
// detectRoute selectes from a list of Route the first match by inspecting both the Accept and Content-Type
// headers of the Request. See also RouterJSR311 in jsr311.go
func (c CurlyRouter) detectRoute(candidateRoutes sortableCurlyRoutes, httpRequest *http.Request) (*Route, error) {
// tracing is done inside detectRoute
return jsr311Router.detectRoute(candidateRoutes.routes(), httpRequest)
}
// detectWebService returns the best matching webService given the list of path tokens.
// see also computeWebserviceScore
func (c CurlyRouter) detectWebService(requestTokens []string, webServices []*WebService) *WebService {
var best *WebService
score := -1
for _, each := range webServices {
matches, eachScore := c.computeWebserviceScore(requestTokens, each.pathExpr.tokens)
if matches && (eachScore > score) {
best = each
score = eachScore
}
}
return best
}
// computeWebserviceScore returns whether tokens match and
// the weighted score of the longest matching consecutive tokens from the beginning.
func (c CurlyRouter) computeWebserviceScore(requestTokens []string, tokens []string) (bool, int) {
if len(tokens) > len(requestTokens) {
return false, 0
}
score := 0
for i := 0; i < len(tokens); i++ {
each := requestTokens[i]
other := tokens[i]
if len(each) == 0 && len(other) == 0 {
score++
continue
}
if len(other) > 0 && strings.HasPrefix(other, "{") {
// no empty match
if len(each) == 0 {
return false, score
}
score += 1
} else {
// not a parameter
if each != other {
return false, score
}
score += (len(tokens) - i) * 10 //fuzzy
}
}
return true, score
}

52
vendor/github.com/emicklei/go-restful/curly_route.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,52 @@
package restful
// Copyright 2013 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.
// curlyRoute exits for sorting Routes by the CurlyRouter based on number of parameters and number of static path elements.
type curlyRoute struct {
route Route
paramCount int
staticCount int
}
type sortableCurlyRoutes []curlyRoute
func (s *sortableCurlyRoutes) add(route curlyRoute) {
*s = append(*s, route)
}
func (s sortableCurlyRoutes) routes() (routes []Route) {
for _, each := range s {
routes = append(routes, each.route) // TODO change return type
}
return routes
}
func (s sortableCurlyRoutes) Len() int {
return len(s)
}
func (s sortableCurlyRoutes) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s sortableCurlyRoutes) Less(i, j int) bool {
ci := s[i]
cj := s[j]
// primary key
if ci.staticCount < cj.staticCount {
return true
}
if ci.staticCount > cj.staticCount {
return false
}
// secundary key
if ci.paramCount < cj.paramCount {
return true
}
if ci.paramCount > cj.paramCount {
return false
}
return ci.route.Path < cj.route.Path
}

185
vendor/github.com/emicklei/go-restful/doc.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,185 @@
/*
Package restful , a lean package for creating REST-style WebServices without magic.
WebServices and Routes
A WebService has a collection of Route objects that dispatch incoming Http Requests to a function calls.
Typically, a WebService has a root path (e.g. /users) and defines common MIME types for its routes.
WebServices must be added to a container (see below) in order to handler Http requests from a server.
A Route is defined by a HTTP method, an URL path and (optionally) the MIME types it consumes (Content-Type) and produces (Accept).
This package has the logic to find the best matching Route and if found, call its Function.
ws := new(restful.WebService)
ws.
Path("/users").
Consumes(restful.MIME_JSON, restful.MIME_XML).
Produces(restful.MIME_JSON, restful.MIME_XML)
ws.Route(ws.GET("/{user-id}").To(u.findUser)) // u is a UserResource
...
// GET http://localhost:8080/users/1
func (u UserResource) findUser(request *restful.Request, response *restful.Response) {
id := request.PathParameter("user-id")
...
}
The (*Request, *Response) arguments provide functions for reading information from the request and writing information back to the response.
See the example https://github.com/emicklei/go-restful/blob/master/examples/restful-user-resource.go with a full implementation.
Regular expression matching Routes
A Route parameter can be specified using the format "uri/{var[:regexp]}" or the special version "uri/{var:*}" for matching the tail of the path.
For example, /persons/{name:[A-Z][A-Z]} can be used to restrict values for the parameter "name" to only contain capital alphabetic characters.
Regular expressions must use the standard Go syntax as described in the regexp package. (https://code.google.com/p/re2/wiki/Syntax)
This feature requires the use of a CurlyRouter.
Containers
A Container holds a collection of WebServices, Filters and a http.ServeMux for multiplexing http requests.
Using the statements "restful.Add(...) and restful.Filter(...)" will register WebServices and Filters to the Default Container.
The Default container of go-restful uses the http.DefaultServeMux.
You can create your own Container and create a new http.Server for that particular container.
container := restful.NewContainer()
server := &http.Server{Addr: ":8081", Handler: container}
Filters
A filter dynamically intercepts requests and responses to transform or use the information contained in the requests or responses.
You can use filters to perform generic logging, measurement, authentication, redirect, set response headers etc.
In the restful package there are three hooks into the request,response flow where filters can be added.
Each filter must define a FilterFunction:
func (req *restful.Request, resp *restful.Response, chain *restful.FilterChain)
Use the following statement to pass the request,response pair to the next filter or RouteFunction
chain.ProcessFilter(req, resp)
Container Filters
These are processed before any registered WebService.
// install a (global) filter for the default container (processed before any webservice)
restful.Filter(globalLogging)
WebService Filters
These are processed before any Route of a WebService.
// install a webservice filter (processed before any route)
ws.Filter(webserviceLogging).Filter(measureTime)
Route Filters
These are processed before calling the function associated with the Route.
// install 2 chained route filters (processed before calling findUser)
ws.Route(ws.GET("/{user-id}").Filter(routeLogging).Filter(NewCountFilter().routeCounter).To(findUser))
See the example https://github.com/emicklei/go-restful/blob/master/examples/restful-filters.go with full implementations.
Response Encoding
Two encodings are supported: gzip and deflate. To enable this for all responses:
restful.DefaultContainer.EnableContentEncoding(true)
If a Http request includes the Accept-Encoding header then the response content will be compressed using the specified encoding.
Alternatively, you can create a Filter that performs the encoding and install it per WebService or Route.
See the example https://github.com/emicklei/go-restful/blob/master/examples/restful-encoding-filter.go
OPTIONS support
By installing a pre-defined container filter, your Webservice(s) can respond to the OPTIONS Http request.
Filter(OPTIONSFilter())
CORS
By installing the filter of a CrossOriginResourceSharing (CORS), your WebService(s) can handle CORS requests.
cors := CrossOriginResourceSharing{ExposeHeaders: []string{"X-My-Header"}, CookiesAllowed: false, Container: DefaultContainer}
Filter(cors.Filter)
Error Handling
Unexpected things happen. If a request cannot be processed because of a failure, your service needs to tell via the response what happened and why.
For this reason HTTP status codes exist and it is important to use the correct code in every exceptional situation.
400: Bad Request
If path or query parameters are not valid (content or type) then use http.StatusBadRequest.
404: Not Found
Despite a valid URI, the resource requested may not be available
500: Internal Server Error
If the application logic could not process the request (or write the response) then use http.StatusInternalServerError.
405: Method Not Allowed
The request has a valid URL but the method (GET,PUT,POST,...) is not allowed.
406: Not Acceptable
The request does not have or has an unknown Accept Header set for this operation.
415: Unsupported Media Type
The request does not have or has an unknown Content-Type Header set for this operation.
ServiceError
In addition to setting the correct (error) Http status code, you can choose to write a ServiceError message on the response.
Performance options
This package has several options that affect the performance of your service. It is important to understand them and how you can change it.
restful.DefaultContainer.DoNotRecover(false)
DoNotRecover controls whether panics will be caught to return HTTP 500.
If set to false, the container will recover from panics.
Default value is true
restful.SetCompressorProvider(NewBoundedCachedCompressors(20, 20))
If content encoding is enabled then the default strategy for getting new gzip/zlib writers and readers is to use a sync.Pool.
Because writers are expensive structures, performance is even more improved when using a preloaded cache. You can also inject your own implementation.
Trouble shooting
This package has the means to produce detail logging of the complete Http request matching process and filter invocation.
Enabling this feature requires you to set an implementation of restful.StdLogger (e.g. log.Logger) instance such as:
restful.TraceLogger(log.New(os.Stdout, "[restful] ", log.LstdFlags|log.Lshortfile))
Logging
The restful.SetLogger() method allows you to override the logger used by the package. By default restful
uses the standard library `log` package and logs to stdout. Different logging packages are supported as
long as they conform to `StdLogger` interface defined in the `log` sub-package, writing an adapter for your
preferred package is simple.
Resources
[project]: https://github.com/emicklei/go-restful
[examples]: https://github.com/emicklei/go-restful/blob/master/examples
[design]: http://ernestmicklei.com/2012/11/11/go-restful-api-design/
[showcases]: https://github.com/emicklei/mora, https://github.com/emicklei/landskape
(c) 2012-2015, http://ernestmicklei.com. MIT License
*/
package restful

162
vendor/github.com/emicklei/go-restful/entity_accessors.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,162 @@
package restful
// Copyright 2015 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.
import (
"encoding/xml"
"strings"
"sync"
)
// EntityReaderWriter can read and write values using an encoding such as JSON,XML.
type EntityReaderWriter interface {
// Read a serialized version of the value from the request.
// The Request may have a decompressing reader. Depends on Content-Encoding.
Read(req *Request, v interface{}) error
// Write a serialized version of the value on the response.
// The Response may have a compressing writer. Depends on Accept-Encoding.
// status should be a valid Http Status code
Write(resp *Response, status int, v interface{}) error
}
// entityAccessRegistry is a singleton
var entityAccessRegistry = &entityReaderWriters{
protection: new(sync.RWMutex),
accessors: map[string]EntityReaderWriter{},
}
// entityReaderWriters associates MIME to an EntityReaderWriter
type entityReaderWriters struct {
protection *sync.RWMutex
accessors map[string]EntityReaderWriter
}
func init() {
RegisterEntityAccessor(MIME_JSON, NewEntityAccessorJSON(MIME_JSON))
RegisterEntityAccessor(MIME_XML, NewEntityAccessorXML(MIME_XML))
}
// RegisterEntityAccessor add/overrides the ReaderWriter for encoding content with this MIME type.
func RegisterEntityAccessor(mime string, erw EntityReaderWriter) {
entityAccessRegistry.protection.Lock()
defer entityAccessRegistry.protection.Unlock()
entityAccessRegistry.accessors[mime] = erw
}
// NewEntityAccessorJSON returns a new EntityReaderWriter for accessing JSON content.
// This package is already initialized with such an accessor using the MIME_JSON contentType.
func NewEntityAccessorJSON(contentType string) EntityReaderWriter {
return entityJSONAccess{ContentType: contentType}
}
// NewEntityAccessorXML returns a new EntityReaderWriter for accessing XML content.
// This package is already initialized with such an accessor using the MIME_XML contentType.
func NewEntityAccessorXML(contentType string) EntityReaderWriter {
return entityXMLAccess{ContentType: contentType}
}
// accessorAt returns the registered ReaderWriter for this MIME type.
func (r *entityReaderWriters) accessorAt(mime string) (EntityReaderWriter, bool) {
r.protection.RLock()
defer r.protection.RUnlock()
er, ok := r.accessors[mime]
if !ok {
// retry with reverse lookup
// more expensive but we are in an exceptional situation anyway
for k, v := range r.accessors {
if strings.Contains(mime, k) {
return v, true
}
}
}
return er, ok
}
// entityXMLAccess is a EntityReaderWriter for XML encoding
type entityXMLAccess struct {
// This is used for setting the Content-Type header when writing
ContentType string
}
// Read unmarshalls the value from XML
func (e entityXMLAccess) Read(req *Request, v interface{}) error {
return xml.NewDecoder(req.Request.Body).Decode(v)
}
// Write marshalls the value to JSON and set the Content-Type Header.
func (e entityXMLAccess) Write(resp *Response, status int, v interface{}) error {
return writeXML(resp, status, e.ContentType, v)
}
// writeXML marshalls the value to JSON and set the Content-Type Header.
func writeXML(resp *Response, status int, contentType string, v interface{}) error {
if v == nil {
resp.WriteHeader(status)
// do not write a nil representation
return nil
}
if resp.prettyPrint {
// pretty output must be created and written explicitly
output, err := xml.MarshalIndent(v, " ", " ")
if err != nil {
return err
}
resp.Header().Set(HEADER_ContentType, contentType)
resp.WriteHeader(status)
_, err = resp.Write([]byte(xml.Header))
if err != nil {
return err
}
_, err = resp.Write(output)
return err
}
// not-so-pretty
resp.Header().Set(HEADER_ContentType, contentType)
resp.WriteHeader(status)
return xml.NewEncoder(resp).Encode(v)
}
// entityJSONAccess is a EntityReaderWriter for JSON encoding
type entityJSONAccess struct {
// This is used for setting the Content-Type header when writing
ContentType string
}
// Read unmarshalls the value from JSON
func (e entityJSONAccess) Read(req *Request, v interface{}) error {
decoder := NewDecoder(req.Request.Body)
decoder.UseNumber()
return decoder.Decode(v)
}
// Write marshalls the value to JSON and set the Content-Type Header.
func (e entityJSONAccess) Write(resp *Response, status int, v interface{}) error {
return writeJSON(resp, status, e.ContentType, v)
}
// write marshalls the value to JSON and set the Content-Type Header.
func writeJSON(resp *Response, status int, contentType string, v interface{}) error {
if v == nil {
resp.WriteHeader(status)
// do not write a nil representation
return nil
}
if resp.prettyPrint {
// pretty output must be created and written explicitly
output, err := MarshalIndent(v, "", " ")
if err != nil {
return err
}
resp.Header().Set(HEADER_ContentType, contentType)
resp.WriteHeader(status)
_, err = resp.Write(output)
return err
}
// not-so-pretty
resp.Header().Set(HEADER_ContentType, contentType)
resp.WriteHeader(status)
return NewEncoder(resp).Encode(v)
}

35
vendor/github.com/emicklei/go-restful/filter.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,35 @@
package restful
// Copyright 2013 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.
// FilterChain is a request scoped object to process one or more filters before calling the target RouteFunction.
type FilterChain struct {
Filters []FilterFunction // ordered list of FilterFunction
Index int // index into filters that is currently in progress
Target RouteFunction // function to call after passing all filters
}
// ProcessFilter passes the request,response pair through the next of Filters.
// Each filter can decide to proceed to the next Filter or handle the Response itself.
func (f *FilterChain) ProcessFilter(request *Request, response *Response) {
if f.Index < len(f.Filters) {
f.Index++
f.Filters[f.Index-1](request, response, f)
} else {
f.Target(request, response)
}
}
// FilterFunction definitions must call ProcessFilter on the FilterChain to pass on the control and eventually call the RouteFunction
type FilterFunction func(*Request, *Response, *FilterChain)
// NoBrowserCacheFilter is a filter function to set HTTP headers that disable browser caching
// See examples/restful-no-cache-filter.go for usage
func NoBrowserCacheFilter(req *Request, resp *Response, chain *FilterChain) {
resp.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") // HTTP 1.1.
resp.Header().Set("Pragma", "no-cache") // HTTP 1.0.
resp.Header().Set("Expires", "0") // Proxies.
chain.ProcessFilter(req, resp)
}

11
vendor/github.com/emicklei/go-restful/json.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,11 @@
// +build !jsoniter
package restful
import "encoding/json"
var (
MarshalIndent = json.MarshalIndent
NewDecoder = json.NewDecoder
NewEncoder = json.NewEncoder
)

12
vendor/github.com/emicklei/go-restful/jsoniter.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,12 @@
// +build jsoniter
package restful
import "github.com/json-iterator/go"
var (
json = jsoniter.ConfigCompatibleWithStandardLibrary
MarshalIndent = json.MarshalIndent
NewDecoder = json.NewDecoder
NewEncoder = json.NewEncoder
)

293
vendor/github.com/emicklei/go-restful/jsr311.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,293 @@
package restful
// Copyright 2013 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.
import (
"errors"
"fmt"
"net/http"
"sort"
)
// RouterJSR311 implements the flow for matching Requests to Routes (and consequently Resource Functions)
// as specified by the JSR311 http://jsr311.java.net/nonav/releases/1.1/spec/spec.html.
// RouterJSR311 implements the Router interface.
// Concept of locators is not implemented.
type RouterJSR311 struct{}
// SelectRoute is part of the Router interface and returns the best match
// for the WebService and its Route for the given Request.
func (r RouterJSR311) SelectRoute(
webServices []*WebService,
httpRequest *http.Request) (selectedService *WebService, selectedRoute *Route, err error) {
// Identify the root resource class (WebService)
dispatcher, finalMatch, err := r.detectDispatcher(httpRequest.URL.Path, webServices)
if err != nil {
return nil, nil, NewError(http.StatusNotFound, "")
}
// Obtain the set of candidate methods (Routes)
routes := r.selectRoutes(dispatcher, finalMatch)
if len(routes) == 0 {
return dispatcher, nil, NewError(http.StatusNotFound, "404: Page Not Found")
}
// Identify the method (Route) that will handle the request
route, ok := r.detectRoute(routes, httpRequest)
return dispatcher, route, ok
}
// ExtractParameters is used to obtain the path parameters from the route using the same matching
// engine as the JSR 311 router.
func (r RouterJSR311) ExtractParameters(route *Route, webService *WebService, urlPath string) map[string]string {
webServiceExpr := webService.pathExpr
webServiceMatches := webServiceExpr.Matcher.FindStringSubmatch(urlPath)
pathParameters := r.extractParams(webServiceExpr, webServiceMatches)
routeExpr := route.pathExpr
routeMatches := routeExpr.Matcher.FindStringSubmatch(webServiceMatches[len(webServiceMatches)-1])
routeParams := r.extractParams(routeExpr, routeMatches)
for key, value := range routeParams {
pathParameters[key] = value
}
return pathParameters
}
func (RouterJSR311) extractParams(pathExpr *pathExpression, matches []string) map[string]string {
params := map[string]string{}
for i := 1; i < len(matches); i++ {
if len(pathExpr.VarNames) >= i {
params[pathExpr.VarNames[i-1]] = matches[i]
}
}
return params
}
// http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-360003.7.2
func (r RouterJSR311) detectRoute(routes []Route, httpRequest *http.Request) (*Route, error) {
ifOk := []Route{}
for _, each := range routes {
ok := true
for _, fn := range each.If {
if !fn(httpRequest) {
ok = false
break
}
}
if ok {
ifOk = append(ifOk, each)
}
}
if len(ifOk) == 0 {
if trace {
traceLogger.Printf("no Route found (from %d) that passes conditional checks", len(routes))
}
return nil, NewError(http.StatusNotFound, "404: Not Found")
}
// http method
methodOk := []Route{}
for _, each := range ifOk {
if httpRequest.Method == each.Method {
methodOk = append(methodOk, each)
}
}
if len(methodOk) == 0 {
if trace {
traceLogger.Printf("no Route found (in %d routes) that matches HTTP method %s\n", len(routes), httpRequest.Method)
}
return nil, NewError(http.StatusMethodNotAllowed, "405: Method Not Allowed")
}
inputMediaOk := methodOk
// content-type
contentType := httpRequest.Header.Get(HEADER_ContentType)
inputMediaOk = []Route{}
for _, each := range methodOk {
if each.matchesContentType(contentType) {
inputMediaOk = append(inputMediaOk, each)
}
}
if len(inputMediaOk) == 0 {
if trace {
traceLogger.Printf("no Route found (from %d) that matches HTTP Content-Type: %s\n", len(methodOk), contentType)
}
return nil, NewError(http.StatusUnsupportedMediaType, "415: Unsupported Media Type")
}
// accept
outputMediaOk := []Route{}
accept := httpRequest.Header.Get(HEADER_Accept)
if len(accept) == 0 {
accept = "*/*"
}
for _, each := range inputMediaOk {
if each.matchesAccept(accept) {
outputMediaOk = append(outputMediaOk, each)
}
}
if len(outputMediaOk) == 0 {
if trace {
traceLogger.Printf("no Route found (from %d) that matches HTTP Accept: %s\n", len(inputMediaOk), accept)
}
return nil, NewError(http.StatusNotAcceptable, "406: Not Acceptable")
}
// return r.bestMatchByMedia(outputMediaOk, contentType, accept), nil
return &outputMediaOk[0], nil
}
// http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-360003.7.2
// n/m > n/* > */*
func (r RouterJSR311) bestMatchByMedia(routes []Route, contentType string, accept string) *Route {
// TODO
return &routes[0]
}
// http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-360003.7.2 (step 2)
func (r RouterJSR311) selectRoutes(dispatcher *WebService, pathRemainder string) []Route {
filtered := &sortableRouteCandidates{}
for _, each := range dispatcher.Routes() {
pathExpr := each.pathExpr
matches := pathExpr.Matcher.FindStringSubmatch(pathRemainder)
if matches != nil {
lastMatch := matches[len(matches)-1]
if len(lastMatch) == 0 || lastMatch == "/" { // do not include if value is neither empty nor /.
filtered.candidates = append(filtered.candidates,
routeCandidate{each, len(matches) - 1, pathExpr.LiteralCount, pathExpr.VarCount})
}
}
}
if len(filtered.candidates) == 0 {
if trace {
traceLogger.Printf("WebService on path %s has no routes that match URL path remainder:%s\n", dispatcher.rootPath, pathRemainder)
}
return []Route{}
}
sort.Sort(sort.Reverse(filtered))
// select other routes from candidates whoes expression matches rmatch
matchingRoutes := []Route{filtered.candidates[0].route}
for c := 1; c < len(filtered.candidates); c++ {
each := filtered.candidates[c]
if each.route.pathExpr.Matcher.MatchString(pathRemainder) {
matchingRoutes = append(matchingRoutes, each.route)
}
}
return matchingRoutes
}
// http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-360003.7.2 (step 1)
func (r RouterJSR311) detectDispatcher(requestPath string, dispatchers []*WebService) (*WebService, string, error) {
filtered := &sortableDispatcherCandidates{}
for _, each := range dispatchers {
matches := each.pathExpr.Matcher.FindStringSubmatch(requestPath)
if matches != nil {
filtered.candidates = append(filtered.candidates,
dispatcherCandidate{each, matches[len(matches)-1], len(matches), each.pathExpr.LiteralCount, each.pathExpr.VarCount})
}
}
if len(filtered.candidates) == 0 {
if trace {
traceLogger.Printf("no WebService was found to match URL path:%s\n", requestPath)
}
return nil, "", errors.New("not found")
}
sort.Sort(sort.Reverse(filtered))
return filtered.candidates[0].dispatcher, filtered.candidates[0].finalMatch, nil
}
// Types and functions to support the sorting of Routes
type routeCandidate struct {
route Route
matchesCount int // the number of capturing groups
literalCount int // the number of literal characters (means those not resulting from template variable substitution)
nonDefaultCount int // the number of capturing groups with non-default regular expressions (i.e. not ([^ /]+?))
}
func (r routeCandidate) expressionToMatch() string {
return r.route.pathExpr.Source
}
func (r routeCandidate) String() string {
return fmt.Sprintf("(m=%d,l=%d,n=%d)", r.matchesCount, r.literalCount, r.nonDefaultCount)
}
type sortableRouteCandidates struct {
candidates []routeCandidate
}
func (rcs *sortableRouteCandidates) Len() int {
return len(rcs.candidates)
}
func (rcs *sortableRouteCandidates) Swap(i, j int) {
rcs.candidates[i], rcs.candidates[j] = rcs.candidates[j], rcs.candidates[i]
}
func (rcs *sortableRouteCandidates) Less(i, j int) bool {
ci := rcs.candidates[i]
cj := rcs.candidates[j]
// primary key
if ci.literalCount < cj.literalCount {
return true
}
if ci.literalCount > cj.literalCount {
return false
}
// secundary key
if ci.matchesCount < cj.matchesCount {
return true
}
if ci.matchesCount > cj.matchesCount {
return false
}
// tertiary key
if ci.nonDefaultCount < cj.nonDefaultCount {
return true
}
if ci.nonDefaultCount > cj.nonDefaultCount {
return false
}
// quaternary key ("source" is interpreted as Path)
return ci.route.Path < cj.route.Path
}
// Types and functions to support the sorting of Dispatchers
type dispatcherCandidate struct {
dispatcher *WebService
finalMatch string
matchesCount int // the number of capturing groups
literalCount int // the number of literal characters (means those not resulting from template variable substitution)
nonDefaultCount int // the number of capturing groups with non-default regular expressions (i.e. not ([^ /]+?))
}
type sortableDispatcherCandidates struct {
candidates []dispatcherCandidate
}
func (dc *sortableDispatcherCandidates) Len() int {
return len(dc.candidates)
}
func (dc *sortableDispatcherCandidates) Swap(i, j int) {
dc.candidates[i], dc.candidates[j] = dc.candidates[j], dc.candidates[i]
}
func (dc *sortableDispatcherCandidates) Less(i, j int) bool {
ci := dc.candidates[i]
cj := dc.candidates[j]
// primary key
if ci.matchesCount < cj.matchesCount {
return true
}
if ci.matchesCount > cj.matchesCount {
return false
}
// secundary key
if ci.literalCount < cj.literalCount {
return true
}
if ci.literalCount > cj.literalCount {
return false
}
// tertiary key
return ci.nonDefaultCount < cj.nonDefaultCount
}

34
vendor/github.com/emicklei/go-restful/log/log.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,34 @@
package log
import (
stdlog "log"
"os"
)
// StdLogger corresponds to a minimal subset of the interface satisfied by stdlib log.Logger
type StdLogger interface {
Print(v ...interface{})
Printf(format string, v ...interface{})
}
var Logger StdLogger
func init() {
// default Logger
SetLogger(stdlog.New(os.Stderr, "[restful] ", stdlog.LstdFlags|stdlog.Lshortfile))
}
// SetLogger sets the logger for this package
func SetLogger(customLogger StdLogger) {
Logger = customLogger
}
// Print delegates to the Logger
func Print(v ...interface{}) {
Logger.Print(v...)
}
// Printf delegates to the Logger
func Printf(format string, v ...interface{}) {
Logger.Printf(format, v...)
}

32
vendor/github.com/emicklei/go-restful/logger.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,32 @@
package restful
// Copyright 2014 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.
import (
"github.com/emicklei/go-restful/log"
)
var trace bool = false
var traceLogger log.StdLogger
func init() {
traceLogger = log.Logger // use the package logger by default
}
// TraceLogger enables detailed logging of Http request matching and filter invocation. Default no logger is set.
// You may call EnableTracing() directly to enable trace logging to the package-wide logger.
func TraceLogger(logger log.StdLogger) {
traceLogger = logger
EnableTracing(logger != nil)
}
// SetLogger exposes the setter for the global logger on the top-level package
func SetLogger(customLogger log.StdLogger) {
log.SetLogger(customLogger)
}
// EnableTracing can be used to Trace logging on and off.
func EnableTracing(enabled bool) {
trace = enabled
}

45
vendor/github.com/emicklei/go-restful/mime.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,45 @@
package restful
import (
"strconv"
"strings"
)
type mime struct {
media string
quality float64
}
// insertMime adds a mime to a list and keeps it sorted by quality.
func insertMime(l []mime, e mime) []mime {
for i, each := range l {
// if current mime has lower quality then insert before
if e.quality > each.quality {
left := append([]mime{}, l[0:i]...)
return append(append(left, e), l[i:]...)
}
}
return append(l, e)
}
// sortedMimes returns a list of mime sorted (desc) by its specified quality.
func sortedMimes(accept string) (sorted []mime) {
for _, each := range strings.Split(accept, ",") {
typeAndQuality := strings.Split(strings.Trim(each, " "), ";")
if len(typeAndQuality) == 1 {
sorted = insertMime(sorted, mime{typeAndQuality[0], 1.0})
} else {
// take factor
parts := strings.Split(typeAndQuality[1], "=")
if len(parts) == 2 {
f, err := strconv.ParseFloat(parts[1], 64)
if err != nil {
traceLogger.Printf("unable to parse quality in %s, %v", each, err)
} else {
sorted = insertMime(sorted, mime{typeAndQuality[0], f})
}
}
}
}
return
}

34
vendor/github.com/emicklei/go-restful/options_filter.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,34 @@
package restful
import "strings"
// Copyright 2013 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.
// OPTIONSFilter is a filter function that inspects the Http Request for the OPTIONS method
// and provides the response with a set of allowed methods for the request URL Path.
// As for any filter, you can also install it for a particular WebService within a Container.
// Note: this filter is not needed when using CrossOriginResourceSharing (for CORS).
func (c *Container) OPTIONSFilter(req *Request, resp *Response, chain *FilterChain) {
if "OPTIONS" != req.Request.Method {
chain.ProcessFilter(req, resp)
return
}
archs := req.Request.Header.Get(HEADER_AccessControlRequestHeaders)
methods := strings.Join(c.computeAllowedMethods(req), ",")
origin := req.Request.Header.Get(HEADER_Origin)
resp.AddHeader(HEADER_Allow, methods)
resp.AddHeader(HEADER_AccessControlAllowOrigin, origin)
resp.AddHeader(HEADER_AccessControlAllowHeaders, archs)
resp.AddHeader(HEADER_AccessControlAllowMethods, methods)
}
// OPTIONSFilter is a filter function that inspects the Http Request for the OPTIONS method
// and provides the response with a set of allowed methods for the request URL Path.
// Note: this filter is not needed when using CrossOriginResourceSharing (for CORS).
func OPTIONSFilter() FilterFunction {
return DefaultContainer.OPTIONSFilter
}

143
vendor/github.com/emicklei/go-restful/parameter.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,143 @@
package restful
// Copyright 2013 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.
const (
// PathParameterKind = indicator of Request parameter type "path"
PathParameterKind = iota
// QueryParameterKind = indicator of Request parameter type "query"
QueryParameterKind
// BodyParameterKind = indicator of Request parameter type "body"
BodyParameterKind
// HeaderParameterKind = indicator of Request parameter type "header"
HeaderParameterKind
// FormParameterKind = indicator of Request parameter type "form"
FormParameterKind
// CollectionFormatCSV comma separated values `foo,bar`
CollectionFormatCSV = CollectionFormat("csv")
// CollectionFormatSSV space separated values `foo bar`
CollectionFormatSSV = CollectionFormat("ssv")
// CollectionFormatTSV tab separated values `foo\tbar`
CollectionFormatTSV = CollectionFormat("tsv")
// CollectionFormatPipes pipe separated values `foo|bar`
CollectionFormatPipes = CollectionFormat("pipes")
// CollectionFormatMulti corresponds to multiple parameter instances instead of multiple values for a single
// instance `foo=bar&foo=baz`. This is valid only for QueryParameters and FormParameters
CollectionFormatMulti = CollectionFormat("multi")
)
type CollectionFormat string
func (cf CollectionFormat) String() string {
return string(cf)
}
// Parameter is for documententing the parameter used in a Http Request
// ParameterData kinds are Path,Query and Body
type Parameter struct {
data *ParameterData
}
// ParameterData represents the state of a Parameter.
// It is made public to make it accessible to e.g. the Swagger package.
type ParameterData struct {
Name, Description, DataType, DataFormat string
Kind int
Required bool
AllowableValues map[string]string
AllowMultiple bool
DefaultValue string
CollectionFormat string
}
// Data returns the state of the Parameter
func (p *Parameter) Data() ParameterData {
return *p.data
}
// Kind returns the parameter type indicator (see const for valid values)
func (p *Parameter) Kind() int {
return p.data.Kind
}
func (p *Parameter) bePath() *Parameter {
p.data.Kind = PathParameterKind
return p
}
func (p *Parameter) beQuery() *Parameter {
p.data.Kind = QueryParameterKind
return p
}
func (p *Parameter) beBody() *Parameter {
p.data.Kind = BodyParameterKind
return p
}
func (p *Parameter) beHeader() *Parameter {
p.data.Kind = HeaderParameterKind
return p
}
func (p *Parameter) beForm() *Parameter {
p.data.Kind = FormParameterKind
return p
}
// Required sets the required field and returns the receiver
func (p *Parameter) Required(required bool) *Parameter {
p.data.Required = required
return p
}
// AllowMultiple sets the allowMultiple field and returns the receiver
func (p *Parameter) AllowMultiple(multiple bool) *Parameter {
p.data.AllowMultiple = multiple
return p
}
// AllowableValues sets the allowableValues field and returns the receiver
func (p *Parameter) AllowableValues(values map[string]string) *Parameter {
p.data.AllowableValues = values
return p
}
// DataType sets the dataType field and returns the receiver
func (p *Parameter) DataType(typeName string) *Parameter {
p.data.DataType = typeName
return p
}
// DataFormat sets the dataFormat field for Swagger UI
func (p *Parameter) DataFormat(formatName string) *Parameter {
p.data.DataFormat = formatName
return p
}
// DefaultValue sets the default value field and returns the receiver
func (p *Parameter) DefaultValue(stringRepresentation string) *Parameter {
p.data.DefaultValue = stringRepresentation
return p
}
// Description sets the description value field and returns the receiver
func (p *Parameter) Description(doc string) *Parameter {
p.data.Description = doc
return p
}
// CollectionFormat sets the collection format for an array type
func (p *Parameter) CollectionFormat(format CollectionFormat) *Parameter {
p.data.CollectionFormat = format.String()
return p
}

74
vendor/github.com/emicklei/go-restful/path_expression.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,74 @@
package restful
// Copyright 2013 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.
import (
"bytes"
"fmt"
"regexp"
"strings"
)
// PathExpression holds a compiled path expression (RegExp) needed to match against
// Http request paths and to extract path parameter values.
type pathExpression struct {
LiteralCount int // the number of literal characters (means those not resulting from template variable substitution)
VarNames []string // the names of parameters (enclosed by {}) in the path
VarCount int // the number of named parameters (enclosed by {}) in the path
Matcher *regexp.Regexp
Source string // Path as defined by the RouteBuilder
tokens []string
}
// NewPathExpression creates a PathExpression from the input URL path.
// Returns an error if the path is invalid.
func newPathExpression(path string) (*pathExpression, error) {
expression, literalCount, varNames, varCount, tokens := templateToRegularExpression(path)
compiled, err := regexp.Compile(expression)
if err != nil {
return nil, err
}
return &pathExpression{literalCount, varNames, varCount, compiled, expression, tokens}, nil
}
// http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-370003.7.3
func templateToRegularExpression(template string) (expression string, literalCount int, varNames []string, varCount int, tokens []string) {
var buffer bytes.Buffer
buffer.WriteString("^")
//tokens = strings.Split(template, "/")
tokens = tokenizePath(template)
for _, each := range tokens {
if each == "" {
continue
}
buffer.WriteString("/")
if strings.HasPrefix(each, "{") {
// check for regular expression in variable
colon := strings.Index(each, ":")
var varName string
if colon != -1 {
// extract expression
varName = strings.TrimSpace(each[1:colon])
paramExpr := strings.TrimSpace(each[colon+1 : len(each)-1])
if paramExpr == "*" { // special case
buffer.WriteString("(.*)")
} else {
buffer.WriteString(fmt.Sprintf("(%s)", paramExpr)) // between colon and closing moustache
}
} else {
// plain var
varName = strings.TrimSpace(each[1 : len(each)-1])
buffer.WriteString("([^/]+?)")
}
varNames = append(varNames, varName)
varCount += 1
} else {
literalCount += len(each)
encoded := each // TODO URI encode
buffer.WriteString(regexp.QuoteMeta(encoded))
}
}
return strings.TrimRight(buffer.String(), "/") + "(/.*)?$", literalCount, varNames, varCount, tokens
}

63
vendor/github.com/emicklei/go-restful/path_processor.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,63 @@
package restful
import (
"bytes"
"strings"
)
// Copyright 2018 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.
// PathProcessor is extra behaviour that a Router can provide to extract path parameters from the path.
// If a Router does not implement this interface then the default behaviour will be used.
type PathProcessor interface {
// ExtractParameters gets the path parameters defined in the route and webService from the urlPath
ExtractParameters(route *Route, webService *WebService, urlPath string) map[string]string
}
type defaultPathProcessor struct{}
// Extract the parameters from the request url path
func (d defaultPathProcessor) ExtractParameters(r *Route, _ *WebService, urlPath string) map[string]string {
urlParts := tokenizePath(urlPath)
pathParameters := map[string]string{}
for i, key := range r.pathParts {
var value string
if i >= len(urlParts) {
value = ""
} else {
value = urlParts[i]
}
if strings.HasPrefix(key, "{") { // path-parameter
if colon := strings.Index(key, ":"); colon != -1 {
// extract by regex
regPart := key[colon+1 : len(key)-1]
keyPart := key[1:colon]
if regPart == "*" {
pathParameters[keyPart] = untokenizePath(i, urlParts)
break
} else {
pathParameters[keyPart] = value
}
} else {
// without enclosing {}
pathParameters[key[1:len(key)-1]] = value
}
}
}
return pathParameters
}
// Untokenize back into an URL path using the slash separator
func untokenizePath(offset int, parts []string) string {
var buffer bytes.Buffer
for p := offset; p < len(parts); p++ {
buffer.WriteString(parts[p])
// do not end
if p < len(parts)-1 {
buffer.WriteString("/")
}
}
return buffer.String()
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше