Upgrade OpenShift and its dependencies.

OpenShift version 1.4.0-alpha.0
This commit is contained in:
Tomas Kral
2016-10-18 12:04:00 +02:00
parent 5e1a5cbb3b
commit 1f8a0e06c9
1786 changed files with 424709 additions and 33395 deletions
+693
View File
@@ -0,0 +1,693 @@
package graph
import (
"fmt"
"reflect"
"sort"
"strings"
"github.com/gonum/graph"
"github.com/gonum/graph/concrete"
"github.com/gonum/graph/encoding/dot"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/util/sets"
)
type Node struct {
concrete.Node
UniqueName
}
// DOTAttributes implements an attribute getter for the DOT encoding
func (n Node) DOTAttributes() []dot.Attribute {
return []dot.Attribute{{Key: "label", Value: fmt.Sprintf("%q", n.UniqueName)}}
}
// ExistenceChecker is an interface for those nodes that can be created without a backing object.
// This can happen when a node wants an edge to a non-existent node. We know the node should exist,
// The graph needs something in that location to track the information we have about the node, but the
// backing object doesn't exist.
type ExistenceChecker interface {
// Found returns false if the node represents an object that we don't have the backing object for
Found() bool
}
type UniqueName string
type UniqueNameFunc func(obj interface{}) UniqueName
func (n UniqueName) UniqueName() string {
return string(n)
}
func (n UniqueName) String() string {
return string(n)
}
type uniqueNamer interface {
UniqueName() UniqueName
}
type NodeFinder interface {
Find(name UniqueName) graph.Node
}
// UniqueNodeInitializer is a graph that allows nodes with a unique name to be added without duplication.
// If the node is newly added, true will be returned.
type UniqueNodeInitializer interface {
FindOrCreate(name UniqueName, fn NodeInitializerFunc) (graph.Node, bool)
}
type NodeInitializerFunc func(Node) graph.Node
func EnsureUnique(g UniqueNodeInitializer, name UniqueName, fn NodeInitializerFunc) graph.Node {
node, _ := g.FindOrCreate(name, fn)
return node
}
type MutableDirectedEdge interface {
AddEdge(from, to graph.Node, edgeKind string)
}
type MutableUniqueGraph interface {
graph.Mutable
MutableDirectedEdge
UniqueNodeInitializer
NodeFinder
}
type Edge struct {
concrete.Edge
kinds sets.String
}
func NewEdge(from, to graph.Node, kinds ...string) Edge {
return Edge{concrete.Edge{F: from, T: to}, sets.NewString(kinds...)}
}
func (e Edge) Kinds() sets.String {
return e.kinds
}
func (e Edge) IsKind(kind string) bool {
return e.kinds.Has(kind)
}
// DOTAttributes implements an attribute getter for the DOT encoding
func (e Edge) DOTAttributes() []dot.Attribute {
return []dot.Attribute{{Key: "label", Value: fmt.Sprintf("%q", strings.Join(e.Kinds().List(), ","))}}
}
type GraphDescriber interface {
Name(node graph.Node) string
Kind(node graph.Node) string
Object(node graph.Node) interface{}
EdgeKinds(edge graph.Edge) sets.String
}
type Interface interface {
graph.Directed
GraphDescriber
MutableUniqueGraph
Edges() []graph.Edge
}
type Namer interface {
ResourceName(obj interface{}) string
}
type namer struct{}
var DefaultNamer Namer = namer{}
func (namer) ResourceName(obj interface{}) string {
switch t := obj.(type) {
case uniqueNamer:
return t.UniqueName().String()
default:
return reflect.TypeOf(obj).String()
}
}
type Graph struct {
// the standard graph
graph.Directed
// helper methods for switching on the kind and types of the node
GraphDescriber
// exposes the public interface for adding nodes
uniqueNamedGraph
// the internal graph object, which allows edges and nodes to be directly added
internal *concrete.DirectedGraph
}
// Graph must implement MutableUniqueGraph
var _ MutableUniqueGraph = Graph{}
// New initializes a graph from input to output.
func New() Graph {
g := concrete.NewDirectedGraph()
return Graph{
Directed: g,
GraphDescriber: typedGraph{},
uniqueNamedGraph: newUniqueNamedGraph(g),
internal: g,
}
}
// Edges returns all the edges of the graph. Note that the returned set
// will have no specific ordering.
func (g Graph) Edges() []graph.Edge {
return g.internal.Edges()
}
func (g Graph) String() string {
ret := ""
nodes := g.Nodes()
sort.Sort(ByID(nodes))
for _, node := range nodes {
ret += fmt.Sprintf("%d: %v\n", node.ID(), g.GraphDescriber.Name(node))
// can't use SuccessorEdges, because I want stable ordering
successors := g.From(node)
sort.Sort(ByID(successors))
for _, successor := range successors {
edge := g.Edge(node, successor)
kinds := g.EdgeKinds(edge)
for _, kind := range kinds.List() {
ret += fmt.Sprintf("\t%v to %d: %v\n", kind, successor.ID(), g.GraphDescriber.Name(successor))
}
}
}
return ret
}
// ByID is a sorted group of nodes by ID
type ByID []graph.Node
func (m ByID) Len() int { return len(m) }
func (m ByID) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
func (m ByID) Less(i, j int) bool {
return m[i].ID() < m[j].ID()
}
// SyntheticNodes returns back the set of nodes that were created in response to edge requests, but did not exist
func (g Graph) SyntheticNodes() []graph.Node {
ret := []graph.Node{}
nodes := g.Nodes()
sort.Sort(ByID(nodes))
for _, node := range nodes {
if potentiallySyntheticNode, ok := node.(ExistenceChecker); ok {
if !potentiallySyntheticNode.Found() {
ret = append(ret, node)
}
}
}
return ret
}
// NodesByKind returns all the nodes of the graph with the provided kinds
func (g Graph) NodesByKind(nodeKinds ...string) []graph.Node {
ret := []graph.Node{}
kinds := sets.NewString(nodeKinds...)
for _, node := range g.internal.Nodes() {
if kinds.Has(g.Kind(node)) {
ret = append(ret, node)
}
}
return ret
}
// RootNodes returns all the roots of this graph.
func (g Graph) RootNodes() []graph.Node {
roots := []graph.Node{}
for _, n := range g.Nodes() {
if len(g.To(n)) != 0 {
continue
}
roots = append(roots, n)
}
return roots
}
// PredecessorEdges invokes fn with all of the predecessor edges of node that have the specified
// edge kind.
func (g Graph) PredecessorEdges(node graph.Node, fn EdgeFunc, edgeKinds ...string) {
for _, n := range g.To(node) {
edge := g.Edge(n, node)
kinds := g.EdgeKinds(edge)
if kinds.HasAny(edgeKinds...) {
fn(g, n, node, kinds)
}
}
}
// SuccessorEdges invokes fn with all of the successor edges of node that have the specified
// edge kind.
func (g Graph) SuccessorEdges(node graph.Node, fn EdgeFunc, edgeKinds ...string) {
for _, n := range g.From(node) {
edge := g.Edge(node, n)
kinds := g.EdgeKinds(edge)
if kinds.HasAny(edgeKinds...) {
fn(g, n, node, kinds)
}
}
}
// OutboundEdges returns all the outbound edges from node that are in the list of edgeKinds
// if edgeKinds is empty, then all edges are returned
func (g Graph) OutboundEdges(node graph.Node, edgeKinds ...string) []graph.Edge {
ret := []graph.Edge{}
for _, n := range g.From(node) {
edge := g.Edge(node, n)
if edge == nil {
continue
}
if len(edgeKinds) == 0 || g.EdgeKinds(edge).HasAny(edgeKinds...) {
ret = append(ret, edge)
}
}
return ret
}
// InboundEdges returns all the inbound edges to node that are in the list of edgeKinds
// if edgeKinds is empty, then all edges are returned
func (g Graph) InboundEdges(node graph.Node, edgeKinds ...string) []graph.Edge {
ret := []graph.Edge{}
for _, n := range g.To(node) {
edge := g.Edge(n, node)
if edge == nil {
continue
}
if len(edgeKinds) == 0 || g.EdgeKinds(edge).HasAny(edgeKinds...) {
ret = append(ret, edge)
}
}
return ret
}
// PredecessorNodesByEdgeKind returns all the predecessor nodes of the given node
// that can be reached via edges of the provided kinds
func (g Graph) PredecessorNodesByEdgeKind(node graph.Node, edgeKinds ...string) []graph.Node {
ret := []graph.Node{}
for _, inboundEdges := range g.InboundEdges(node, edgeKinds...) {
ret = append(ret, inboundEdges.From())
}
return ret
}
// SuccessorNodesByEdgeKind returns all the successor nodes of the given node
// that can be reached via edges of the provided kinds
func (g Graph) SuccessorNodesByEdgeKind(node graph.Node, edgeKinds ...string) []graph.Node {
ret := []graph.Node{}
for _, outboundEdge := range g.OutboundEdges(node, edgeKinds...) {
ret = append(ret, outboundEdge.To())
}
return ret
}
func (g Graph) SuccessorNodesByNodeAndEdgeKind(node graph.Node, nodeKind, edgeKind string) []graph.Node {
ret := []graph.Node{}
for _, successor := range g.SuccessorNodesByEdgeKind(node, edgeKind) {
if g.Kind(successor) != nodeKind {
continue
}
ret = append(ret, successor)
}
return ret
}
func (g Graph) AddNode(n graph.Node) {
g.internal.AddNode(n)
}
// AddEdge implements MutableUniqueGraph
func (g Graph) AddEdge(from, to graph.Node, edgeKind string) {
// a Contains edge has semantic meaning for osgraph.Graph objects. It never makes sense
// to allow a single object to be "contained" by multiple nodes.
if edgeKind == ContainsEdgeKind {
// check incoming edges on the 'to' node to be certain that we aren't already contained
containsEdges := g.InboundEdges(to, ContainsEdgeKind)
if len(containsEdges) != 0 {
// TODO consider changing the AddEdge API to make this cleaner. This is a pretty severe programming error
panic(fmt.Sprintf("%v is already contained by %v", to, containsEdges))
}
}
kinds := sets.NewString(edgeKind)
if existingEdge := g.Edge(from, to); existingEdge != nil {
kinds.Insert(g.EdgeKinds(existingEdge).List()...)
}
g.internal.SetEdge(NewEdge(from, to, kinds.List()...), 1.0)
}
// addEdges adds the specified edges, filtered by the provided edge connection
// function.
func (g Graph) addEdges(edges []graph.Edge, fn EdgeFunc) {
for _, e := range edges {
switch t := e.(type) {
case concrete.WeightedEdge:
if fn(g, t.From(), t.To(), t.Edge.(Edge).Kinds()) {
g.internal.SetEdge(t.Edge.(Edge), t.Cost)
}
case Edge:
if fn(g, t.From(), t.To(), t.Kinds()) {
g.internal.SetEdge(t, 1.0)
}
default:
panic("bad edge")
}
}
}
// NodeFunc is passed a new graph, a node in the graph, and should return true if the
// node should be included.
type NodeFunc func(g Interface, n graph.Node) bool
// NodesOfKind returns a new NodeFunc accepting the provided kinds of nodes
// If no kinds are specified, the returned NodeFunc will accept all nodes
func NodesOfKind(kinds ...string) NodeFunc {
if len(kinds) == 0 {
return func(g Interface, n graph.Node) bool {
return true
}
}
allowedKinds := sets.NewString(kinds...)
return func(g Interface, n graph.Node) bool {
return allowedKinds.Has(g.Kind(n))
}
}
// EdgeFunc is passed a new graph, an edge in the current graph, and should mutate
// the new graph as needed. If true is returned, the existing edge will be added to the graph.
type EdgeFunc func(g Interface, from, to graph.Node, edgeKinds sets.String) bool
// EdgesOfKind returns a new EdgeFunc accepting the provided kinds of edges
// If no kinds are specified, the returned EdgeFunc will accept all edges
func EdgesOfKind(kinds ...string) EdgeFunc {
if len(kinds) == 0 {
return func(g Interface, from, to graph.Node, edgeKinds sets.String) bool {
return true
}
}
allowedKinds := sets.NewString(kinds...)
return func(g Interface, from, to graph.Node, edgeKinds sets.String) bool {
return allowedKinds.HasAny(edgeKinds.List()...)
}
}
// RemoveInboundEdges returns a new EdgeFunc dismissing any inbound edges to
// the provided set of nodes
func RemoveInboundEdges(nodes []graph.Node) EdgeFunc {
return func(g Interface, from, to graph.Node, edgeKinds sets.String) bool {
for _, node := range nodes {
if node == to {
return false
}
}
return true
}
}
func RemoveOutboundEdges(nodes []graph.Node) EdgeFunc {
return func(g Interface, from, to graph.Node, edgeKinds sets.String) bool {
for _, node := range nodes {
if node == from {
return false
}
}
return true
}
}
// EdgeSubgraph returns the directed subgraph with only the edges that match the
// provided function.
func (g Graph) EdgeSubgraph(edgeFn EdgeFunc) Graph {
out := New()
for _, node := range g.Nodes() {
out.internal.AddNode(node)
}
out.addEdges(g.internal.Edges(), edgeFn)
return out
}
// Subgraph returns the directed subgraph with only the nodes and edges that match the
// provided functions.
func (g Graph) Subgraph(nodeFn NodeFunc, edgeFn EdgeFunc) Graph {
out := New()
for _, node := range g.Nodes() {
if nodeFn(out, node) {
out.internal.AddNode(node)
}
}
out.addEdges(g.internal.Edges(), edgeFn)
return out
}
// SubgraphWithNodes returns the directed subgraph with only the listed nodes and edges that
// match the provided function.
func (g Graph) SubgraphWithNodes(nodes []graph.Node, fn EdgeFunc) Graph {
out := New()
for _, node := range nodes {
out.internal.AddNode(node)
}
out.addEdges(g.internal.Edges(), fn)
return out
}
// ConnectedEdgeSubgraph creates a new graph that iterates through all edges in the graph
// and includes all edges the provided function returns true for. Nodes not referenced by
// an edge will be dropped unless the function adds them explicitly.
func (g Graph) ConnectedEdgeSubgraph(fn EdgeFunc) Graph {
out := New()
out.addEdges(g.internal.Edges(), fn)
return out
}
// AllNodes includes all nodes in the graph
func AllNodes(g Interface, node graph.Node) bool {
return true
}
// ExistingDirectEdge returns true if both from and to already exist in the graph and the edge kind is
// not ReferencedByEdgeKind (the generic reverse edge kind). This will purge the graph of any
// edges created by AddReversedEdge.
func ExistingDirectEdge(g Interface, from, to graph.Node, edgeKinds sets.String) bool {
return !edgeKinds.Has(ReferencedByEdgeKind) && g.Has(from) && g.Has(to)
}
// ReverseExistingDirectEdge reverses the order of the edge and drops the existing edge only if
// both from and to already exist in the graph and the edge kind is not ReferencedByEdgeKind
// (the generic reverse edge kind).
func ReverseExistingDirectEdge(g Interface, from, to graph.Node, edgeKinds sets.String) bool {
return ExistingDirectEdge(g, from, to, edgeKinds) && ReverseGraphEdge(g, from, to, edgeKinds)
}
// ReverseGraphEdge reverses the order of the edge and drops the existing edge.
func ReverseGraphEdge(g Interface, from, to graph.Node, edgeKinds sets.String) bool {
for edgeKind := range edgeKinds {
g.AddEdge(to, from, edgeKind)
}
return false
}
// AddReversedEdge adds a reversed edge for every passed edge and preserves the existing
// edge. Used to convert a one directional edge into a bidirectional edge, but will
// create duplicate edges if a bidirectional edge between two nodes already exists.
func AddReversedEdge(g Interface, from, to graph.Node, edgeKinds sets.String) bool {
g.AddEdge(to, from, ReferencedByEdgeKind)
return true
}
// AddGraphEdgesTo returns an EdgeFunc that will add the selected edges to the passed
// graph.
func AddGraphEdgesTo(g Interface) EdgeFunc {
return func(_ Interface, from, to graph.Node, edgeKinds sets.String) bool {
for edgeKind := range edgeKinds {
g.AddEdge(from, to, edgeKind)
}
return false
}
}
type uniqueNamedGraph struct {
graph.Mutable
names map[UniqueName]graph.Node
}
func newUniqueNamedGraph(g graph.Mutable) uniqueNamedGraph {
return uniqueNamedGraph{
Mutable: g,
names: make(map[UniqueName]graph.Node),
}
}
func (g uniqueNamedGraph) FindOrCreate(name UniqueName, fn NodeInitializerFunc) (graph.Node, bool) {
if node, ok := g.names[name]; ok {
return node, true
}
id := g.NewNodeID()
node := fn(Node{concrete.Node(id), name})
g.names[name] = node
g.AddNode(node)
return node, false
}
func (g uniqueNamedGraph) Find(name UniqueName) graph.Node {
if node, ok := g.names[name]; ok {
return node
}
return nil
}
type typedGraph struct{}
func (g typedGraph) Name(node graph.Node) string {
switch t := node.(type) {
case fmt.Stringer:
return t.String()
case uniqueNamer:
return t.UniqueName().String()
default:
return fmt.Sprintf("<unknown:%d>", node.ID())
}
}
type objectifier interface {
Object() interface{}
}
func (g typedGraph) Object(node graph.Node) interface{} {
switch t := node.(type) {
case objectifier:
return t.Object()
default:
return nil
}
}
type kind interface {
Kind() string
}
func (g typedGraph) Kind(node graph.Node) string {
if k, ok := node.(kind); ok {
return k.Kind()
}
return UnknownNodeKind
}
func (g typedGraph) EdgeKinds(edge graph.Edge) sets.String {
var e Edge
switch t := edge.(type) {
case concrete.WeightedEdge:
e = t.Edge.(Edge)
case Edge:
e = t
default:
return sets.NewString(UnknownEdgeKind)
}
return e.Kinds()
}
type NodeSet map[int]struct{}
func (n NodeSet) Has(id int) bool {
_, ok := n[id]
return ok
}
func (n NodeSet) Add(id int) {
n[id] = struct{}{}
}
func NodesByKind(g Interface, nodes []graph.Node, kinds ...string) [][]graph.Node {
buckets := make(map[string]int)
for i, kind := range kinds {
buckets[kind] = i
}
if nodes == nil {
nodes = g.Nodes()
}
last := len(kinds)
result := make([][]graph.Node, last+1)
for _, node := range nodes {
if bucket, ok := buckets[g.Kind(node)]; ok {
result[bucket] = append(result[bucket], node)
} else {
result[last] = append(result[last], node)
}
}
return result
}
// IsFromDifferentNamespace returns if a node is in a different namespace
// than the one provided.
func IsFromDifferentNamespace(namespace string, node graph.Node) bool {
potentiallySyntheticNode, ok := node.(ExistenceChecker)
if !ok || potentiallySyntheticNode.Found() {
return false
}
objectified, ok := node.(objectifier)
if !ok {
return false
}
object, err := meta.Accessor(objectified)
if err != nil {
return false
}
return object.GetNamespace() != namespace
}
func pathCovered(path []graph.Node, paths map[int][]graph.Node) bool {
l := len(path)
for _, existing := range paths {
if l >= len(existing) {
continue
}
if pathEqual(path, existing) {
return true
}
}
return false
}
func pathEqual(a, b []graph.Node) bool {
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}
@@ -0,0 +1,84 @@
package graphview
import (
"sort"
osgraph "github.com/openshift/origin/pkg/api/graph"
kubegraph "github.com/openshift/origin/pkg/api/kubegraph/nodes"
deployedges "github.com/openshift/origin/pkg/deploy/graph"
deploygraph "github.com/openshift/origin/pkg/deploy/graph/nodes"
)
type DeploymentConfigPipeline struct {
Deployment *deploygraph.DeploymentConfigNode
ActiveDeployment *kubegraph.ReplicationControllerNode
InactiveDeployments []*kubegraph.ReplicationControllerNode
Images []ImagePipeline
}
// AllDeploymentConfigPipelines returns all the DCPipelines that aren't in the excludes set and the set of covered NodeIDs
func AllDeploymentConfigPipelines(g osgraph.Graph, excludeNodeIDs IntSet) ([]DeploymentConfigPipeline, IntSet) {
covered := IntSet{}
dcPipelines := []DeploymentConfigPipeline{}
for _, uncastNode := range g.NodesByKind(deploygraph.DeploymentConfigNodeKind) {
if excludeNodeIDs.Has(uncastNode.ID()) {
continue
}
pipeline, covers := NewDeploymentConfigPipeline(g, uncastNode.(*deploygraph.DeploymentConfigNode))
covered.Insert(covers.List()...)
dcPipelines = append(dcPipelines, pipeline)
}
sort.Sort(SortedDeploymentConfigPipeline(dcPipelines))
return dcPipelines, covered
}
// NewDeploymentConfigPipeline returns the DeploymentConfigPipeline and a set of all the NodeIDs covered by the DeploymentConfigPipeline
func NewDeploymentConfigPipeline(g osgraph.Graph, dcNode *deploygraph.DeploymentConfigNode) (DeploymentConfigPipeline, IntSet) {
covered := IntSet{}
covered.Insert(dcNode.ID())
dcPipeline := DeploymentConfigPipeline{}
dcPipeline.Deployment = dcNode
// for everything that can trigger a deployment, create an image pipeline and add it to the list
for _, istNode := range g.PredecessorNodesByEdgeKind(dcNode, deployedges.TriggersDeploymentEdgeKind) {
imagePipeline, covers := NewImagePipelineFromImageTagLocation(g, istNode, istNode.(ImageTagLocation))
covered.Insert(covers.List()...)
dcPipeline.Images = append(dcPipeline.Images, imagePipeline)
}
// for image that we use, create an image pipeline and add it to the list
for _, tagNode := range g.PredecessorNodesByEdgeKind(dcNode, deployedges.UsedInDeploymentEdgeKind) {
imagePipeline, covers := NewImagePipelineFromImageTagLocation(g, tagNode, tagNode.(ImageTagLocation))
covered.Insert(covers.List()...)
dcPipeline.Images = append(dcPipeline.Images, imagePipeline)
}
dcPipeline.ActiveDeployment, dcPipeline.InactiveDeployments = deployedges.RelevantDeployments(g, dcNode)
for _, rc := range dcPipeline.InactiveDeployments {
_, covers := NewReplicationController(g, rc)
covered.Insert(covers.List()...)
}
if dcPipeline.ActiveDeployment != nil {
_, covers := NewReplicationController(g, dcPipeline.ActiveDeployment)
covered.Insert(covers.List()...)
}
return dcPipeline, covered
}
type SortedDeploymentConfigPipeline []DeploymentConfigPipeline
func (m SortedDeploymentConfigPipeline) Len() int { return len(m) }
func (m SortedDeploymentConfigPipeline) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
func (m SortedDeploymentConfigPipeline) Less(i, j int) bool {
return CompareObjectMeta(&m[i].Deployment.DeploymentConfig.ObjectMeta, &m[j].Deployment.DeploymentConfig.ObjectMeta)
}
@@ -0,0 +1,240 @@
package graphview
import (
"sort"
"github.com/gonum/graph"
osgraph "github.com/openshift/origin/pkg/api/graph"
buildedges "github.com/openshift/origin/pkg/build/graph"
buildgraph "github.com/openshift/origin/pkg/build/graph/nodes"
imageedges "github.com/openshift/origin/pkg/image/graph"
imagegraph "github.com/openshift/origin/pkg/image/graph/nodes"
)
// ImagePipeline represents a build, its output, and any inputs. The input
// to a build may be another ImagePipeline.
type ImagePipeline struct {
Image ImageTagLocation
DestinationResolved bool
ScheduledImport bool
Build *buildgraph.BuildConfigNode
LastSuccessfulBuild *buildgraph.BuildNode
LastUnsuccessfulBuild *buildgraph.BuildNode
ActiveBuilds []*buildgraph.BuildNode
// If set, the base image used by the build
BaseImage ImageTagLocation
// if set, the build config names that produces the base image
BaseBuilds []string
// If set, the source repository that inputs to the build
Source SourceLocation
}
// ImageTagLocation identifies the source or destination of an image. Represents
// both a tag in a Docker image repository, as well as a tag in an OpenShift image stream.
type ImageTagLocation interface {
ID() int
ImageSpec() string
ImageTag() string
}
// SourceLocation identifies a repository that is an input to a build.
type SourceLocation interface {
ID() int
}
func AllImagePipelinesFromBuildConfig(g osgraph.Graph, excludeNodeIDs IntSet) ([]ImagePipeline, IntSet) {
covered := IntSet{}
pipelines := []ImagePipeline{}
for _, uncastNode := range g.NodesByKind(buildgraph.BuildConfigNodeKind) {
if excludeNodeIDs.Has(uncastNode.ID()) {
continue
}
pipeline, covers := NewImagePipelineFromBuildConfigNode(g, uncastNode.(*buildgraph.BuildConfigNode))
covered.Insert(covers.List()...)
pipelines = append(pipelines, pipeline)
}
sort.Sort(SortedImagePipelines(pipelines))
outputImageToBCMap := make(map[string][]string)
for _, pipeline := range pipelines {
// note, bc does not have to have an output image
if pipeline.Image != nil {
bcs, ok := outputImageToBCMap[pipeline.Image.ImageSpec()]
if !ok {
bcs = []string{}
}
bcs = append(bcs, pipeline.Build.BuildConfig.Name)
outputImageToBCMap[pipeline.Image.ImageSpec()] = bcs
}
}
if len(outputImageToBCMap) > 0 {
for i, pipeline := range pipelines {
// note, bc does not have to have an input strategy image
if pipeline.BaseImage != nil {
baseBCs, ok := outputImageToBCMap[pipeline.BaseImage.ImageSpec()]
if ok && len(baseBCs) > 0 {
pipelines[i].BaseBuilds = baseBCs
}
}
}
}
return pipelines, covered
}
// NewImagePipeline attempts to locate a build flow from the provided node. If no such
// build flow can be located, false is returned.
func NewImagePipelineFromBuildConfigNode(g osgraph.Graph, bcNode *buildgraph.BuildConfigNode) (ImagePipeline, IntSet) {
covered := IntSet{}
covered.Insert(bcNode.ID())
flow := ImagePipeline{}
base, src, coveredInputs, scheduled, _ := findBuildInputs(g, bcNode)
covered.Insert(coveredInputs.List()...)
flow.BaseImage = base
flow.Source = src
flow.Build = bcNode
flow.ScheduledImport = scheduled
flow.LastSuccessfulBuild, flow.LastUnsuccessfulBuild, flow.ActiveBuilds = buildedges.RelevantBuilds(g, flow.Build)
flow.Image = findBuildOutput(g, bcNode)
// we should have at most one
for _, buildOutputNode := range g.SuccessorNodesByEdgeKind(bcNode, buildedges.BuildOutputEdgeKind) {
// this will handle the imagestream tag case
for _, input := range g.SuccessorNodesByEdgeKind(buildOutputNode, imageedges.ReferencedImageStreamGraphEdgeKind) {
imageStreamNode := input.(*imagegraph.ImageStreamNode)
flow.DestinationResolved = (len(imageStreamNode.Status.DockerImageRepository) != 0)
}
// this will handle the imagestream image case
for _, input := range g.SuccessorNodesByEdgeKind(buildOutputNode, imageedges.ReferencedImageStreamImageGraphEdgeKind) {
imageStreamNode := input.(*imagegraph.ImageStreamNode)
flow.DestinationResolved = (len(imageStreamNode.Status.DockerImageRepository) != 0)
}
// TODO handle the DockerImage case
}
return flow, covered
}
// NewImagePipelineFromImageTagLocation returns the ImagePipeline and all the nodes contributing to it
func NewImagePipelineFromImageTagLocation(g osgraph.Graph, node graph.Node, imageTagLocation ImageTagLocation) (ImagePipeline, IntSet) {
covered := IntSet{}
covered.Insert(node.ID())
flow := ImagePipeline{}
flow.Image = imageTagLocation
for _, input := range g.PredecessorNodesByEdgeKind(node, buildedges.BuildOutputEdgeKind) {
covered.Insert(input.ID())
build := input.(*buildgraph.BuildConfigNode)
if flow.Build != nil {
// report this as an error (unexpected duplicate input build)
}
if build.BuildConfig == nil {
// report this as as a missing build / broken link
break
}
base, src, coveredInputs, scheduled, _ := findBuildInputs(g, build)
covered.Insert(coveredInputs.List()...)
flow.BaseImage = base
flow.Source = src
flow.Build = build
flow.ScheduledImport = scheduled
flow.LastSuccessfulBuild, flow.LastUnsuccessfulBuild, flow.ActiveBuilds = buildedges.RelevantBuilds(g, flow.Build)
}
for _, input := range g.SuccessorNodesByEdgeKind(node, imageedges.ReferencedImageStreamGraphEdgeKind) {
covered.Insert(input.ID())
imageStreamNode := input.(*imagegraph.ImageStreamNode)
flow.DestinationResolved = (len(imageStreamNode.Status.DockerImageRepository) != 0)
}
for _, input := range g.SuccessorNodesByEdgeKind(node, imageedges.ReferencedImageStreamImageGraphEdgeKind) {
covered.Insert(input.ID())
imageStreamNode := input.(*imagegraph.ImageStreamNode)
flow.DestinationResolved = (len(imageStreamNode.Status.DockerImageRepository) != 0)
}
return flow, covered
}
func findBuildInputs(g osgraph.Graph, bcNode *buildgraph.BuildConfigNode) (base ImageTagLocation, source SourceLocation, covered IntSet, scheduled bool, err error) {
covered = IntSet{}
// find inputs to the build
for _, input := range g.PredecessorNodesByEdgeKind(bcNode, buildedges.BuildInputEdgeKind) {
if source != nil {
// report this as an error (unexpected duplicate source)
}
covered.Insert(input.ID())
source = input.(SourceLocation)
}
for _, input := range g.PredecessorNodesByEdgeKind(bcNode, buildedges.BuildInputImageEdgeKind) {
if base != nil {
// report this as an error (unexpected duplicate input build)
}
covered.Insert(input.ID())
base = input.(ImageTagLocation)
scheduled = imageStreamTagScheduled(g, input, base)
}
return
}
func findBuildOutput(g osgraph.Graph, bcNode *buildgraph.BuildConfigNode) (result ImageTagLocation) {
for _, output := range g.SuccessorNodesByEdgeKind(bcNode, buildedges.BuildOutputEdgeKind) {
result = output.(ImageTagLocation)
return
}
return
}
func imageStreamTagScheduled(g osgraph.Graph, input graph.Node, base ImageTagLocation) (scheduled bool) {
for _, uncastImageStreamNode := range g.SuccessorNodesByEdgeKind(input, imageedges.ReferencedImageStreamGraphEdgeKind) {
imageStreamNode := uncastImageStreamNode.(*imagegraph.ImageStreamNode)
if imageStreamNode.ImageStream != nil {
if tag, ok := imageStreamNode.ImageStream.Spec.Tags[base.ImageTag()]; ok {
scheduled = tag.ImportPolicy.Scheduled
return
}
}
}
return
}
type SortedImagePipelines []ImagePipeline
func (m SortedImagePipelines) Len() int { return len(m) }
func (m SortedImagePipelines) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
func (m SortedImagePipelines) Less(i, j int) bool {
return CompareImagePipeline(&m[i], &m[j])
}
func CompareImagePipeline(a, b *ImagePipeline) bool {
switch {
case a.Build != nil && b.Build != nil && a.Build.BuildConfig != nil && b.Build.BuildConfig != nil:
return CompareObjectMeta(&a.Build.BuildConfig.ObjectMeta, &b.Build.BuildConfig.ObjectMeta)
case a.Build != nil && a.Build.BuildConfig != nil:
return true
case b.Build != nil && b.Build.BuildConfig != nil:
return false
}
if a.Image == nil || b.Image == nil {
return true
}
return a.Image.ImageSpec() < b.Image.ImageSpec()
}
+46
View File
@@ -0,0 +1,46 @@
package graphview
import (
"sort"
"k8s.io/kubernetes/pkg/util/sets"
)
type IntSet map[int]sets.Empty
// NewIntSet creates a IntSet from a list of values.
func NewIntSet(items ...int) IntSet {
ss := IntSet{}
ss.Insert(items...)
return ss
}
// Insert adds items to the set.
func (s IntSet) Insert(items ...int) {
for _, item := range items {
s[item] = sets.Empty{}
}
}
// Delete removes all items from the set.
func (s IntSet) Delete(items ...int) {
for _, item := range items {
delete(s, item)
}
}
// Has returns true iff item is contained in the set.
func (s IntSet) Has(item int) bool {
_, contained := s[item]
return contained
}
// List returns the contents as a sorted string slice.
func (s IntSet) List() []int {
res := make([]int, 0, len(s))
for key := range s {
res = append(res, key)
}
sort.IntSlice(res).Sort()
return res
}
+51
View File
@@ -0,0 +1,51 @@
package graphview
import (
osgraph "github.com/openshift/origin/pkg/api/graph"
kubeedges "github.com/openshift/origin/pkg/api/kubegraph"
kubegraph "github.com/openshift/origin/pkg/api/kubegraph/nodes"
)
type PetSet struct {
PetSet *kubegraph.PetSetNode
OwnedPods []*kubegraph.PodNode
CreatedPods []*kubegraph.PodNode
// TODO: handle conflicting once controller refs are present, not worth it yet
}
// AllPetSets returns all the PetSets that aren't in the excludes set and the set of covered NodeIDs
func AllPetSets(g osgraph.Graph, excludeNodeIDs IntSet) ([]PetSet, IntSet) {
covered := IntSet{}
views := []PetSet{}
for _, uncastNode := range g.NodesByKind(kubegraph.PetSetNodeKind) {
if excludeNodeIDs.Has(uncastNode.ID()) {
continue
}
view, covers := NewPetSet(g, uncastNode.(*kubegraph.PetSetNode))
covered.Insert(covers.List()...)
views = append(views, view)
}
return views, covered
}
// NewPetSet returns the PetSet and a set of all the NodeIDs covered by the PetSet
func NewPetSet(g osgraph.Graph, node *kubegraph.PetSetNode) (PetSet, IntSet) {
covered := IntSet{}
covered.Insert(node.ID())
view := PetSet{}
view.PetSet = node
for _, uncastPodNode := range g.PredecessorNodesByEdgeKind(node, kubeedges.ManagedByControllerEdgeKind) {
podNode := uncastPodNode.(*kubegraph.PodNode)
covered.Insert(podNode.ID())
view.OwnedPods = append(view.OwnedPods, podNode)
}
return view, covered
}
+39
View File
@@ -0,0 +1,39 @@
package graphview
import (
osgraph "github.com/openshift/origin/pkg/api/graph"
kubegraph "github.com/openshift/origin/pkg/api/kubegraph/nodes"
)
type Pod struct {
Pod *kubegraph.PodNode
}
// AllPods returns all Pods and the set of covered NodeIDs
func AllPods(g osgraph.Graph, excludeNodeIDs IntSet) ([]Pod, IntSet) {
covered := IntSet{}
pods := []Pod{}
for _, uncastNode := range g.NodesByKind(kubegraph.PodNodeKind) {
if excludeNodeIDs.Has(uncastNode.ID()) {
continue
}
pod, covers := NewPod(g, uncastNode.(*kubegraph.PodNode))
covered.Insert(covers.List()...)
pods = append(pods, pod)
}
return pods, covered
}
// NewPod returns the Pod and a set of all the NodeIDs covered by the Pod
func NewPod(g osgraph.Graph, podNode *kubegraph.PodNode) (Pod, IntSet) {
covered := IntSet{}
covered.Insert(podNode.ID())
podView := Pod{}
podView.Pod = podNode
return podView, covered
}
+100
View File
@@ -0,0 +1,100 @@
package graphview
import (
"k8s.io/kubernetes/pkg/api/unversioned"
osgraph "github.com/openshift/origin/pkg/api/graph"
kubeedges "github.com/openshift/origin/pkg/api/kubegraph"
"github.com/openshift/origin/pkg/api/kubegraph/analysis"
kubegraph "github.com/openshift/origin/pkg/api/kubegraph/nodes"
)
type ReplicationController struct {
RC *kubegraph.ReplicationControllerNode
OwnedPods []*kubegraph.PodNode
CreatedPods []*kubegraph.PodNode
ConflictingRCs []*kubegraph.ReplicationControllerNode
ConflictingRCIDToPods map[int][]*kubegraph.PodNode
}
// AllReplicationControllers returns all the ReplicationControllers that aren't in the excludes set and the set of covered NodeIDs
func AllReplicationControllers(g osgraph.Graph, excludeNodeIDs IntSet) ([]ReplicationController, IntSet) {
covered := IntSet{}
rcViews := []ReplicationController{}
for _, uncastNode := range g.NodesByKind(kubegraph.ReplicationControllerNodeKind) {
if excludeNodeIDs.Has(uncastNode.ID()) {
continue
}
rcView, covers := NewReplicationController(g, uncastNode.(*kubegraph.ReplicationControllerNode))
covered.Insert(covers.List()...)
rcViews = append(rcViews, rcView)
}
return rcViews, covered
}
// MaxRecentContainerRestarts returns the maximum container restarts for all pods in
// replication controller.
func (rc *ReplicationController) MaxRecentContainerRestarts() int32 {
var maxRestarts int32
for _, pod := range rc.OwnedPods {
for _, status := range pod.Status.ContainerStatuses {
if status.RestartCount > maxRestarts && analysis.ContainerRestartedRecently(status, unversioned.Now()) {
maxRestarts = status.RestartCount
}
}
}
return maxRestarts
}
// NewReplicationController returns the ReplicationController and a set of all the NodeIDs covered by the ReplicationController
func NewReplicationController(g osgraph.Graph, rcNode *kubegraph.ReplicationControllerNode) (ReplicationController, IntSet) {
covered := IntSet{}
covered.Insert(rcNode.ID())
rcView := ReplicationController{}
rcView.RC = rcNode
rcView.ConflictingRCIDToPods = map[int][]*kubegraph.PodNode{}
for _, uncastPodNode := range g.PredecessorNodesByEdgeKind(rcNode, kubeedges.ManagedByControllerEdgeKind) {
podNode := uncastPodNode.(*kubegraph.PodNode)
covered.Insert(podNode.ID())
rcView.OwnedPods = append(rcView.OwnedPods, podNode)
// check to see if this pod is managed by more than one RC
uncastOwningRCs := g.SuccessorNodesByEdgeKind(podNode, kubeedges.ManagedByControllerEdgeKind)
if len(uncastOwningRCs) > 1 {
for _, uncastOwningRC := range uncastOwningRCs {
if uncastOwningRC.ID() == rcNode.ID() {
continue
}
conflictingRC := uncastOwningRC.(*kubegraph.ReplicationControllerNode)
rcView.ConflictingRCs = append(rcView.ConflictingRCs, conflictingRC)
conflictingPods, ok := rcView.ConflictingRCIDToPods[conflictingRC.ID()]
if !ok {
conflictingPods = []*kubegraph.PodNode{}
}
conflictingPods = append(conflictingPods, podNode)
rcView.ConflictingRCIDToPods[conflictingRC.ID()] = conflictingPods
}
}
}
return rcView, covered
}
// MaxRecentContainerRestartsForRC returns the maximum container restarts in pods
// in the replication controller node for the last 10 minutes.
func MaxRecentContainerRestartsForRC(g osgraph.Graph, rcNode *kubegraph.ReplicationControllerNode) int32 {
if rcNode == nil {
return 0
}
rc, _ := NewReplicationController(g, rcNode)
return rc.MaxRecentContainerRestarts()
}
@@ -0,0 +1,134 @@
package graphview
import (
"fmt"
"sort"
kapi "k8s.io/kubernetes/pkg/api"
utilruntime "k8s.io/kubernetes/pkg/util/runtime"
osgraph "github.com/openshift/origin/pkg/api/graph"
kubeedges "github.com/openshift/origin/pkg/api/kubegraph"
kubegraph "github.com/openshift/origin/pkg/api/kubegraph/nodes"
deploygraph "github.com/openshift/origin/pkg/deploy/graph/nodes"
routeedges "github.com/openshift/origin/pkg/route/graph"
routegraph "github.com/openshift/origin/pkg/route/graph/nodes"
)
// ServiceGroup is a service, the DeploymentConfigPipelines it covers, and lists of the other nodes that fulfill it
type ServiceGroup struct {
Service *kubegraph.ServiceNode
DeploymentConfigPipelines []DeploymentConfigPipeline
ReplicationControllers []ReplicationController
PetSets []PetSet
// TODO: this has to stop
FulfillingPetSets []*kubegraph.PetSetNode
FulfillingDCs []*deploygraph.DeploymentConfigNode
FulfillingRCs []*kubegraph.ReplicationControllerNode
FulfillingPods []*kubegraph.PodNode
ExposingRoutes []*routegraph.RouteNode
}
// AllServiceGroups returns all the ServiceGroups that aren't in the excludes set and the set of covered NodeIDs
func AllServiceGroups(g osgraph.Graph, excludeNodeIDs IntSet) ([]ServiceGroup, IntSet) {
covered := IntSet{}
services := []ServiceGroup{}
for _, uncastNode := range g.NodesByKind(kubegraph.ServiceNodeKind) {
if excludeNodeIDs.Has(uncastNode.ID()) {
continue
}
service, covers := NewServiceGroup(g, uncastNode.(*kubegraph.ServiceNode))
covered.Insert(covers.List()...)
services = append(services, service)
}
sort.Sort(ServiceGroupByObjectMeta(services))
return services, covered
}
// NewServiceGroup returns the ServiceGroup and a set of all the NodeIDs covered by the service
func NewServiceGroup(g osgraph.Graph, serviceNode *kubegraph.ServiceNode) (ServiceGroup, IntSet) {
covered := IntSet{}
covered.Insert(serviceNode.ID())
service := ServiceGroup{}
service.Service = serviceNode
for _, uncastServiceFulfiller := range g.PredecessorNodesByEdgeKind(serviceNode, kubeedges.ExposedThroughServiceEdgeKind) {
container := osgraph.GetTopLevelContainerNode(g, uncastServiceFulfiller)
switch castContainer := container.(type) {
case *deploygraph.DeploymentConfigNode:
service.FulfillingDCs = append(service.FulfillingDCs, castContainer)
case *kubegraph.ReplicationControllerNode:
service.FulfillingRCs = append(service.FulfillingRCs, castContainer)
case *kubegraph.PodNode:
service.FulfillingPods = append(service.FulfillingPods, castContainer)
case *kubegraph.PetSetNode:
service.FulfillingPetSets = append(service.FulfillingPetSets, castContainer)
default:
utilruntime.HandleError(fmt.Errorf("unrecognized container: %v", castContainer))
}
}
for _, uncastServiceFulfiller := range g.PredecessorNodesByEdgeKind(serviceNode, routeedges.ExposedThroughRouteEdgeKind) {
container := osgraph.GetTopLevelContainerNode(g, uncastServiceFulfiller)
switch castContainer := container.(type) {
case *routegraph.RouteNode:
service.ExposingRoutes = append(service.ExposingRoutes, castContainer)
default:
utilruntime.HandleError(fmt.Errorf("unrecognized container: %v", castContainer))
}
}
// add the DCPipelines for all the DCs that fulfill the service
for _, fulfillingDC := range service.FulfillingDCs {
dcPipeline, dcCovers := NewDeploymentConfigPipeline(g, fulfillingDC)
covered.Insert(dcCovers.List()...)
service.DeploymentConfigPipelines = append(service.DeploymentConfigPipelines, dcPipeline)
}
for _, fulfillingRC := range service.FulfillingRCs {
rcView, rcCovers := NewReplicationController(g, fulfillingRC)
covered.Insert(rcCovers.List()...)
service.ReplicationControllers = append(service.ReplicationControllers, rcView)
}
for _, fulfillingPetSet := range service.FulfillingPetSets {
view, covers := NewPetSet(g, fulfillingPetSet)
covered.Insert(covers.List()...)
service.PetSets = append(service.PetSets, view)
}
for _, fulfillingPod := range service.FulfillingPods {
_, podCovers := NewPod(g, fulfillingPod)
covered.Insert(podCovers.List()...)
}
return service, covered
}
type ServiceGroupByObjectMeta []ServiceGroup
func (m ServiceGroupByObjectMeta) Len() int { return len(m) }
func (m ServiceGroupByObjectMeta) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
func (m ServiceGroupByObjectMeta) Less(i, j int) bool {
a, b := m[i], m[j]
return CompareObjectMeta(&a.Service.Service.ObjectMeta, &b.Service.Service.ObjectMeta)
}
func CompareObjectMeta(a, b *kapi.ObjectMeta) bool {
if a.Namespace == b.Namespace {
return a.Name < b.Name
}
return a.Namespace < b.Namespace
}
+134
View File
@@ -0,0 +1,134 @@
package graph
import (
"github.com/gonum/graph"
)
// Marker is a struct that describes something interesting on a Node
type Marker struct {
// Node is the optional node that this message is attached to
Node graph.Node
// RelatedNodes is an optional list of other nodes that are involved in this marker.
RelatedNodes []graph.Node
// Severity indicates how important this problem is.
Severity Severity
// Key is a short string to identify this message
Key string
// Message is a human-readable string that describes what is interesting
Message string
// Suggestion is a human-readable string that holds advice for resolving this
// marker.
Suggestion Suggestion
}
// Severity indicates how important this problem is.
type Severity string
const (
// InfoSeverity is interesting
// TODO: Consider what to do with this once we revisit the graph api - currently not used.
InfoSeverity Severity = "info"
// WarningSeverity is probably wrong, but we aren't certain
WarningSeverity Severity = "warning"
// ErrorSeverity is definitely wrong, this won't work
ErrorSeverity Severity = "error"
)
type Markers []Marker
// MarkerScanner is a function for analyzing a graph and finding interesting things in it
type MarkerScanner func(g Graph, f Namer) []Marker
func (m Markers) BySeverity(severity Severity) []Marker {
ret := []Marker{}
for i := range m {
if m[i].Severity == severity {
ret = append(ret, m[i])
}
}
return ret
}
// FilterByNamespace returns all the markers that are not associated with missing nodes
// from other namespaces (other than the provided namespace).
func (m Markers) FilterByNamespace(namespace string) Markers {
filtered := Markers{}
for i := range m {
markerNodes := []graph.Node{}
markerNodes = append(markerNodes, m[i].Node)
markerNodes = append(markerNodes, m[i].RelatedNodes...)
hasCrossNamespaceLink := false
for _, node := range markerNodes {
if IsFromDifferentNamespace(namespace, node) {
hasCrossNamespaceLink = true
break
}
}
if !hasCrossNamespaceLink {
filtered = append(filtered, m[i])
}
}
return filtered
}
type BySeverity []Marker
func (m BySeverity) Len() int { return len(m) }
func (m BySeverity) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
func (m BySeverity) Less(i, j int) bool {
lhs := m[i]
rhs := m[j]
switch lhs.Severity {
case ErrorSeverity:
switch rhs.Severity {
case ErrorSeverity:
return false
}
case WarningSeverity:
switch rhs.Severity {
case ErrorSeverity, WarningSeverity:
return false
}
case InfoSeverity:
switch rhs.Severity {
case ErrorSeverity, WarningSeverity, InfoSeverity:
return false
}
}
return true
}
type ByNodeID []Marker
func (m ByNodeID) Len() int { return len(m) }
func (m ByNodeID) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
func (m ByNodeID) Less(i, j int) bool {
if m[i].Node == nil {
return true
}
if m[j].Node == nil {
return false
}
return m[i].Node.ID() < m[j].Node.ID()
}
type ByKey []Marker
func (m ByKey) Len() int { return len(m) }
func (m ByKey) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
func (m ByKey) Less(i, j int) bool {
return m[i].Key < m[j].Key
}
type Suggestion string
func (s Suggestion) String() string {
return string(s)
}
+69
View File
@@ -0,0 +1,69 @@
package graph
import (
"fmt"
"github.com/gonum/graph"
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/runtime"
)
const (
UnknownNodeKind = "UnknownNode"
)
const (
UnknownEdgeKind = "UnknownEdge"
// ReferencedByEdgeKind is the kind to use if you're building reverse links that don't have a specific edge in the other direction
// other uses are discouraged. You should create a kind for your edge
ReferencedByEdgeKind = "ReferencedBy"
// ContainsEdgeKind is the kind to use if one node's contents logically contain another node's contents. A given node can only have
// a single inbound Contais edge. The code does not prevent contains cycles, but that's insane, don't do that.
ContainsEdgeKind = "Contains"
)
func GetUniqueRuntimeObjectNodeName(nodeKind string, obj runtime.Object) UniqueName {
meta, err := kapi.ObjectMetaFor(obj)
if err != nil {
panic(err)
}
return UniqueName(fmt.Sprintf("%s|%s/%s", nodeKind, meta.Namespace, meta.Name))
}
// GetTopLevelContainerNode traverses the reverse ContainsEdgeKind edges until it finds a node
// that does not have an inbound ContainsEdgeKind edge. This could be the node itself
func GetTopLevelContainerNode(g Graph, containedNode graph.Node) graph.Node {
// my kingdom for a LinkedHashSet
visited := map[int]bool{}
prevContainingNode := containedNode
for {
visited[prevContainingNode.ID()] = true
currContainingNode := GetContainingNode(g, prevContainingNode)
if currContainingNode == nil {
return prevContainingNode
}
if _, alreadyVisited := visited[currContainingNode.ID()]; alreadyVisited {
panic(fmt.Sprintf("contains cycle in %v", visited))
}
prevContainingNode = currContainingNode
}
}
// GetContainingNode returns the direct predecessor that is linked to the node by a ContainsEdgeKind. It returns
// nil if no container is found.
func GetContainingNode(g Graph, containedNode graph.Node) graph.Node {
for _, node := range g.To(containedNode) {
edge := g.Edge(node, containedNode)
if g.EdgeKinds(edge).Has(ContainsEdgeKind) {
return node
}
}
return nil
}
+180
View File
@@ -0,0 +1,180 @@
package analysis
import (
"fmt"
"strings"
"k8s.io/kubernetes/pkg/util/sets"
graphapi "github.com/gonum/graph"
"github.com/gonum/graph/path"
osgraph "github.com/openshift/origin/pkg/api/graph"
"github.com/openshift/origin/pkg/api/kubegraph"
kubenodes "github.com/openshift/origin/pkg/api/kubegraph/nodes"
deploygraph "github.com/openshift/origin/pkg/deploy/graph"
deploynodes "github.com/openshift/origin/pkg/deploy/graph/nodes"
)
const (
// HPAMissingScaleRefError denotes an error where a Horizontal Pod Autoscaler does not have a reference to an object to scale
HPAMissingScaleRefError = "HPAMissingScaleRef"
// HPAMissingCPUTargetError denotes an error where a Horizontal Pod Autoscaler does not have a CPU target to scale by.
// Currently, the only supported scale metric is CPU utilization, so without this metric an HPA is useless.
HPAMissingCPUTargetError = "HPAMissingCPUTarget"
// HPAOverlappingScaleRefWarning denotes a warning where a Horizontal Pod Autoscaler scales an object that is scaled by some other object as well
HPAOverlappingScaleRefWarning = "HPAOverlappingScaleRef"
)
// FindHPASpecsMissingCPUTargets scans the graph in search of HorizontalPodAutoscalers that are missing a CPU utilization target.
// As of right now, the only metric that HPAs can use to scale pods is the CPU utilization, so if a HPA is missing this target it
// is effectively useless.
func FindHPASpecsMissingCPUTargets(graph osgraph.Graph, namer osgraph.Namer) []osgraph.Marker {
markers := []osgraph.Marker{}
for _, uncastNode := range graph.NodesByKind(kubenodes.HorizontalPodAutoscalerNodeKind) {
node := uncastNode.(*kubenodes.HorizontalPodAutoscalerNode)
if node.HorizontalPodAutoscaler.Spec.TargetCPUUtilizationPercentage == nil {
markers = append(markers, osgraph.Marker{
Node: node,
Severity: osgraph.ErrorSeverity,
Key: HPAMissingCPUTargetError,
Message: fmt.Sprintf("%s is missing a CPU utilization target", namer.ResourceName(node)),
Suggestion: osgraph.Suggestion(fmt.Sprintf(`oc patch %s -p '{"spec":{"targetCPUUtilizationPercentage": 80}}'`, namer.ResourceName(node))),
})
}
}
return markers
}
// FindHPASpecsMissingScaleRefs finds all Horizontal Pod Autoscalers whose scale reference points to an object that doesn't exist
// or that the client does not have the permission to see.
func FindHPASpecsMissingScaleRefs(graph osgraph.Graph, namer osgraph.Namer) []osgraph.Marker {
markers := []osgraph.Marker{}
for _, uncastNode := range graph.NodesByKind(kubenodes.HorizontalPodAutoscalerNodeKind) {
node := uncastNode.(*kubenodes.HorizontalPodAutoscalerNode)
scaledObjects := graph.SuccessorNodesByEdgeKind(
uncastNode,
kubegraph.ScalingEdgeKind,
)
if len(scaledObjects) < 1 {
markers = append(markers, createMissingScaleRefMarker(node, nil, namer))
continue
}
for _, scaleRef := range scaledObjects {
if existenceChecker, ok := scaleRef.(osgraph.ExistenceChecker); ok && !existenceChecker.Found() {
// if this node is synthetic, we can't be sure that the HPA is scaling something that actually exists
markers = append(markers, createMissingScaleRefMarker(node, scaleRef, namer))
}
}
}
return markers
}
func createMissingScaleRefMarker(hpaNode *kubenodes.HorizontalPodAutoscalerNode, scaleRef graphapi.Node, namer osgraph.Namer) osgraph.Marker {
return osgraph.Marker{
Node: hpaNode,
Severity: osgraph.ErrorSeverity,
RelatedNodes: []graphapi.Node{scaleRef},
Key: HPAMissingScaleRefError,
Message: fmt.Sprintf("%s is attempting to scale %s/%s, which doesn't exist",
namer.ResourceName(hpaNode),
hpaNode.HorizontalPodAutoscaler.Spec.ScaleTargetRef.Kind,
hpaNode.HorizontalPodAutoscaler.Spec.ScaleTargetRef.Name,
),
}
}
// FindOverlappingHPAs scans the graph in search of HorizontalPodAutoscalers that are attempting to scale the same set of pods.
// This can occur in two ways:
// - 1. label selectors for two ReplicationControllers/DeploymentConfigs/etc overlap
// - 2. multiple HorizontalPodAutoscalers are attempting to scale the same ReplicationController/DeploymentConfig/etc
// Case 1 is handled by deconflicting the area of influence of ReplicationControllers/DeploymentConfigs/etc, and therefore we
// can assume that it will be handled before this step. Therefore, we are only concerned with finding HPAs that are trying to
// scale the same resources.
//
// The algorithm that is used to implement this check is described as follows:
// - create a sub-graph containing only HPA nodes and other nodes that can be scaled, as well as any scaling edges or other
// edges used to connect between objects that can be scaled
// - for every resulting edge in the new sub-graph, create an edge in the reverse direction
// - find the shortest paths between all HPA nodes in the graph
// - shortest paths connecting two horizontal pod autoscalers are used to create markers for the graph
func FindOverlappingHPAs(graph osgraph.Graph, namer osgraph.Namer) []osgraph.Marker {
markers := []osgraph.Marker{}
nodeFilter := osgraph.NodesOfKind(
kubenodes.HorizontalPodAutoscalerNodeKind,
kubenodes.ReplicationControllerNodeKind,
deploynodes.DeploymentConfigNodeKind,
)
edgeFilter := osgraph.EdgesOfKind(
kubegraph.ScalingEdgeKind,
deploygraph.DeploymentEdgeKind,
)
hpaSubGraph := graph.Subgraph(nodeFilter, edgeFilter)
for _, edge := range hpaSubGraph.Edges() {
osgraph.AddReversedEdge(hpaSubGraph, edge.From(), edge.To(), sets.NewString())
}
hpaNodes := hpaSubGraph.NodesByKind(kubenodes.HorizontalPodAutoscalerNodeKind)
for _, firstHPA := range hpaNodes {
// we can use Dijkstra's algorithm as we know we do not have any negative edge weights
shortestPaths := path.DijkstraFrom(firstHPA, hpaSubGraph)
for _, secondHPA := range hpaNodes {
if firstHPA == secondHPA {
continue
}
shortestPath, _ := shortestPaths.To(secondHPA)
if shortestPath == nil {
// if two HPAs have no path between them, no error exists
continue
}
markers = append(markers, osgraph.Marker{
Node: firstHPA,
Severity: osgraph.WarningSeverity,
RelatedNodes: shortestPath[1:],
Key: HPAOverlappingScaleRefWarning,
Message: fmt.Sprintf("%s and %s overlap because they both attempt to scale %s",
namer.ResourceName(firstHPA), namer.ResourceName(secondHPA), nameList(shortestPath[1:len(shortestPath)-1], namer)),
})
}
}
return markers
}
// nameList outputs a nicely-formatted list of names:
// - given nodes ['a', 'b', 'c'], this will return "one of a, b, or c"
// - given nodes ['a', 'b'], this will return "a or b"
// - given nodes ['a'], this will return "a"
func nameList(nodes []graphapi.Node, namer osgraph.Namer) string {
names := []string{}
for _, node := range nodes {
names = append(names, namer.ResourceName(node))
}
switch len(names) {
case 0:
return ""
case 1:
return names[0]
case 2:
return names[0] + " or " + names[1]
default:
return "one of " + strings.Join(names[:len(names)-1], ", ") + ", or " + names[len(names)-1]
}
}
+135
View File
@@ -0,0 +1,135 @@
package analysis
import (
"fmt"
"time"
"github.com/MakeNowJust/heredoc"
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
osgraph "github.com/openshift/origin/pkg/api/graph"
kubegraph "github.com/openshift/origin/pkg/api/kubegraph/nodes"
)
const (
CrashLoopingPodError = "CrashLoopingPod"
RestartingPodWarning = "RestartingPod"
RestartThreshold = 5
// TODO: if you change this, you must change the messages below.
RestartRecentDuration = 10 * time.Minute
)
// exposed for testing
var nowFn = unversioned.Now
// FindRestartingPods inspects all Pods to see if they've restarted more than the threshold. logsCommandName is the name of
// the command that should be invoked to see pod logs. securityPolicyCommandPattern is a format string accepting two replacement
// variables for fmt.Sprintf - 1, the namespace of the current pod, 2 the service account of the pod.
func FindRestartingPods(g osgraph.Graph, f osgraph.Namer, logsCommandName, securityPolicyCommandPattern string) []osgraph.Marker {
markers := []osgraph.Marker{}
for _, uncastPodNode := range g.NodesByKind(kubegraph.PodNodeKind) {
podNode := uncastPodNode.(*kubegraph.PodNode)
pod, ok := podNode.Object().(*kapi.Pod)
if !ok {
continue
}
for _, containerStatus := range pod.Status.ContainerStatuses {
containerString := ""
if len(pod.Spec.Containers) > 1 {
containerString = fmt.Sprintf("container %q in ", containerStatus.Name)
}
switch {
case containerCrashLoopBackOff(containerStatus):
var suggestion string
switch {
case containerIsNonRoot(pod, containerStatus.Name):
suggestion = heredoc.Docf(`
The container is starting and exiting repeatedly. This usually means the container is unable
to start, misconfigured, or limited by security restrictions. Check the container logs with
%s %s -c %s
Current security policy prevents your containers from being run as the root user. Some images
may fail expecting to be able to change ownership or permissions on directories. Your admin
can grant you access to run containers that need to run as the root user with this command:
%s
`, logsCommandName, pod.Name, containerStatus.Name, fmt.Sprintf(securityPolicyCommandPattern, pod.Namespace, pod.Spec.ServiceAccountName))
default:
suggestion = heredoc.Docf(`
The container is starting and exiting repeatedly. This usually means the container is unable
to start, misconfigured, or limited by security restrictions. Check the container logs with
%s %s -c %s
`, logsCommandName, pod.Name, containerStatus.Name)
}
markers = append(markers, osgraph.Marker{
Node: podNode,
Severity: osgraph.ErrorSeverity,
Key: CrashLoopingPodError,
Message: fmt.Sprintf("%s%s is crash-looping", containerString,
f.ResourceName(podNode)),
Suggestion: osgraph.Suggestion(suggestion),
})
case ContainerRestartedRecently(containerStatus, nowFn()):
markers = append(markers, osgraph.Marker{
Node: podNode,
Severity: osgraph.WarningSeverity,
Key: RestartingPodWarning,
Message: fmt.Sprintf("%s%s has restarted within the last 10 minutes", containerString,
f.ResourceName(podNode)),
})
case containerRestartedFrequently(containerStatus):
markers = append(markers, osgraph.Marker{
Node: podNode,
Severity: osgraph.WarningSeverity,
Key: RestartingPodWarning,
Message: fmt.Sprintf("%s%s has restarted %d times", containerString,
f.ResourceName(podNode), containerStatus.RestartCount),
})
}
}
}
return markers
}
func containerIsNonRoot(pod *kapi.Pod, container string) bool {
for _, c := range pod.Spec.Containers {
if c.Name != container || c.SecurityContext == nil {
continue
}
switch {
case c.SecurityContext.RunAsUser != nil && *c.SecurityContext.RunAsUser != 0:
//c.SecurityContext.RunAsNonRoot != nil && *c.SecurityContext.RunAsNonRoot,
return true
}
}
return false
}
func containerCrashLoopBackOff(status kapi.ContainerStatus) bool {
return status.State.Waiting != nil && status.State.Waiting.Reason == "CrashLoopBackOff"
}
func ContainerRestartedRecently(status kapi.ContainerStatus, now unversioned.Time) bool {
if status.RestartCount == 0 {
return false
}
if status.LastTerminationState.Terminated != nil && now.Sub(status.LastTerminationState.Terminated.FinishedAt.Time) < RestartRecentDuration {
return true
}
return false
}
func containerRestartedFrequently(status kapi.ContainerStatus) bool {
return status.RestartCount > RestartThreshold
}
@@ -0,0 +1,124 @@
package analysis
import (
"fmt"
"github.com/gonum/graph"
osgraph "github.com/openshift/origin/pkg/api/graph"
kubeedges "github.com/openshift/origin/pkg/api/kubegraph"
kubegraph "github.com/openshift/origin/pkg/api/kubegraph/nodes"
)
const (
UnmountableSecretWarning = "UnmountableSecret"
MissingSecretWarning = "MissingSecret"
)
// FindUnmountableSecrets inspects all PodSpecs for any Secret reference that isn't listed as mountable by the referenced ServiceAccount
func FindUnmountableSecrets(g osgraph.Graph, f osgraph.Namer) []osgraph.Marker {
markers := []osgraph.Marker{}
for _, uncastPodSpecNode := range g.NodesByKind(kubegraph.PodSpecNodeKind) {
podSpecNode := uncastPodSpecNode.(*kubegraph.PodSpecNode)
unmountableSecrets := CheckForUnmountableSecrets(g, podSpecNode)
topLevelNode := osgraph.GetTopLevelContainerNode(g, podSpecNode)
topLevelString := f.ResourceName(topLevelNode)
saString := "MISSING_SA"
saNodes := g.SuccessorNodesByEdgeKind(podSpecNode, kubeedges.ReferencedServiceAccountEdgeKind)
if len(saNodes) > 0 {
saString = f.ResourceName(saNodes[0])
}
for _, unmountableSecret := range unmountableSecrets {
markers = append(markers, osgraph.Marker{
Node: podSpecNode,
RelatedNodes: []graph.Node{unmountableSecret},
Severity: osgraph.WarningSeverity,
Key: UnmountableSecretWarning,
Message: fmt.Sprintf("%s is attempting to mount a secret %s disallowed by %s",
topLevelString, f.ResourceName(unmountableSecret), saString),
})
}
}
return markers
}
// FindMissingSecrets inspects all PodSpecs for any Secret reference that is a synthetic node (not a pre-existing node in the graph)
func FindMissingSecrets(g osgraph.Graph, f osgraph.Namer) []osgraph.Marker {
markers := []osgraph.Marker{}
for _, uncastPodSpecNode := range g.NodesByKind(kubegraph.PodSpecNodeKind) {
podSpecNode := uncastPodSpecNode.(*kubegraph.PodSpecNode)
missingSecrets := CheckMissingMountedSecrets(g, podSpecNode)
topLevelNode := osgraph.GetTopLevelContainerNode(g, podSpecNode)
topLevelString := f.ResourceName(topLevelNode)
for _, missingSecret := range missingSecrets {
markers = append(markers, osgraph.Marker{
Node: podSpecNode,
RelatedNodes: []graph.Node{missingSecret},
Severity: osgraph.WarningSeverity,
Key: UnmountableSecretWarning,
Message: fmt.Sprintf("%s is attempting to mount a missing secret %s",
topLevelString, f.ResourceName(missingSecret)),
})
}
}
return markers
}
// CheckForUnmountableSecrets checks to be sure that all the referenced secrets are mountable (by service account)
func CheckForUnmountableSecrets(g osgraph.Graph, podSpecNode *kubegraph.PodSpecNode) []*kubegraph.SecretNode {
saNodes := g.SuccessorNodesByNodeAndEdgeKind(podSpecNode, kubegraph.ServiceAccountNodeKind, kubeedges.ReferencedServiceAccountEdgeKind)
saMountableSecrets := []*kubegraph.SecretNode{}
if len(saNodes) > 0 {
saNode := saNodes[0].(*kubegraph.ServiceAccountNode)
for _, secretNode := range g.SuccessorNodesByNodeAndEdgeKind(saNode, kubegraph.SecretNodeKind, kubeedges.MountableSecretEdgeKind) {
saMountableSecrets = append(saMountableSecrets, secretNode.(*kubegraph.SecretNode))
}
}
unmountableSecrets := []*kubegraph.SecretNode{}
for _, uncastMountedSecretNode := range g.SuccessorNodesByNodeAndEdgeKind(podSpecNode, kubegraph.SecretNodeKind, kubeedges.MountedSecretEdgeKind) {
mountedSecretNode := uncastMountedSecretNode.(*kubegraph.SecretNode)
mountable := false
for _, mountableSecretNode := range saMountableSecrets {
if mountableSecretNode == mountedSecretNode {
mountable = true
break
}
}
if !mountable {
unmountableSecrets = append(unmountableSecrets, mountedSecretNode)
continue
}
}
return unmountableSecrets
}
// CheckMissingMountedSecrets checks to be sure that all the referenced secrets are present (not synthetic)
func CheckMissingMountedSecrets(g osgraph.Graph, podSpecNode *kubegraph.PodSpecNode) []*kubegraph.SecretNode {
missingSecrets := []*kubegraph.SecretNode{}
for _, uncastMountedSecretNode := range g.SuccessorNodesByNodeAndEdgeKind(podSpecNode, kubegraph.SecretNodeKind, kubeedges.MountedSecretEdgeKind) {
mountedSecretNode := uncastMountedSecretNode.(*kubegraph.SecretNode)
if !mountedSecretNode.Found() {
missingSecrets = append(missingSecrets, mountedSecretNode)
}
}
return missingSecrets
}
+56
View File
@@ -0,0 +1,56 @@
package analysis
import (
"fmt"
"strings"
"github.com/gonum/graph"
osgraph "github.com/openshift/origin/pkg/api/graph"
kubeedges "github.com/openshift/origin/pkg/api/kubegraph"
kubegraph "github.com/openshift/origin/pkg/api/kubegraph/nodes"
)
const (
DuelingReplicationControllerWarning = "DuelingReplicationControllers"
)
func FindDuelingReplicationControllers(g osgraph.Graph, f osgraph.Namer) []osgraph.Marker {
markers := []osgraph.Marker{}
for _, uncastRCNode := range g.NodesByKind(kubegraph.ReplicationControllerNodeKind) {
rcNode := uncastRCNode.(*kubegraph.ReplicationControllerNode)
for _, uncastPodNode := range g.PredecessorNodesByEdgeKind(rcNode, kubeedges.ManagedByControllerEdgeKind) {
podNode := uncastPodNode.(*kubegraph.PodNode)
// check to see if this pod is managed by more than one RC
uncastOwningRCs := g.SuccessorNodesByEdgeKind(podNode, kubeedges.ManagedByControllerEdgeKind)
if len(uncastOwningRCs) > 1 {
involvedRCNames := []string{}
relatedNodes := []graph.Node{uncastPodNode}
for _, uncastOwningRC := range uncastOwningRCs {
if uncastOwningRC.ID() == rcNode.ID() {
continue
}
owningRC := uncastOwningRC.(*kubegraph.ReplicationControllerNode)
involvedRCNames = append(involvedRCNames, f.ResourceName(owningRC))
relatedNodes = append(relatedNodes, uncastOwningRC)
}
markers = append(markers, osgraph.Marker{
Node: rcNode,
RelatedNodes: relatedNodes,
Severity: osgraph.WarningSeverity,
Key: DuelingReplicationControllerWarning,
Message: fmt.Sprintf("%s is competing for %s with %s", f.ResourceName(rcNode), f.ResourceName(podNode), strings.Join(involvedRCNames, ", ")),
})
}
}
}
return markers
}
+248
View File
@@ -0,0 +1,248 @@
package kubegraph
import (
"strings"
"github.com/gonum/graph"
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apimachinery/registered"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/runtime"
osgraph "github.com/openshift/origin/pkg/api/graph"
kubegraph "github.com/openshift/origin/pkg/api/kubegraph/nodes"
deployapi "github.com/openshift/origin/pkg/deploy/api"
deploygraph "github.com/openshift/origin/pkg/deploy/graph/nodes"
)
const (
// ExposedThroughServiceEdgeKind goes from a PodTemplateSpec or a Pod to Service. The head should make the service's selector.
ExposedThroughServiceEdgeKind = "ExposedThroughService"
// ManagedByControllerEdgeKind goes from Pod to controller when the Pod satisfies a controller's label selector
ManagedByControllerEdgeKind = "ManagedByController"
// MountedSecretEdgeKind goes from PodSpec to Secret indicating that is or will be a request to mount a volume with the Secret.
MountedSecretEdgeKind = "MountedSecret"
// MountableSecretEdgeKind goes from ServiceAccount to Secret indicating that the SA allows the Secret to be mounted
MountableSecretEdgeKind = "MountableSecret"
// ReferencedServiceAccountEdgeKind goes from PodSpec to ServiceAccount indicating that Pod is or will be running as the SA.
ReferencedServiceAccountEdgeKind = "ReferencedServiceAccount"
// ScalingEdgeKind goes from HorizontalPodAutoscaler to scaled objects indicating that the HPA scales the object
ScalingEdgeKind = "Scaling"
)
// AddExposedPodTemplateSpecEdges ensures that a directed edge exists between a service and all the PodTemplateSpecs
// in the graph that match the service selector
func AddExposedPodTemplateSpecEdges(g osgraph.MutableUniqueGraph, node *kubegraph.ServiceNode) {
if node.Service.Spec.Selector == nil {
return
}
query := labels.SelectorFromSet(node.Service.Spec.Selector)
for _, n := range g.(graph.Graph).Nodes() {
switch target := n.(type) {
case *kubegraph.PodTemplateSpecNode:
if target.Namespace != node.Namespace {
continue
}
if query.Matches(labels.Set(target.PodTemplateSpec.Labels)) {
g.AddEdge(target, node, ExposedThroughServiceEdgeKind)
}
}
}
}
// AddAllExposedPodTemplateSpecEdges calls AddExposedPodTemplateSpecEdges for every ServiceNode in the graph
func AddAllExposedPodTemplateSpecEdges(g osgraph.MutableUniqueGraph) {
for _, node := range g.(graph.Graph).Nodes() {
if serviceNode, ok := node.(*kubegraph.ServiceNode); ok {
AddExposedPodTemplateSpecEdges(g, serviceNode)
}
}
}
// AddExposedPodEdges ensures that a directed edge exists between a service and all the pods
// in the graph that match the service selector
func AddExposedPodEdges(g osgraph.MutableUniqueGraph, node *kubegraph.ServiceNode) {
if node.Service.Spec.Selector == nil {
return
}
query := labels.SelectorFromSet(node.Service.Spec.Selector)
for _, n := range g.(graph.Graph).Nodes() {
switch target := n.(type) {
case *kubegraph.PodNode:
if target.Namespace != node.Namespace {
continue
}
if query.Matches(labels.Set(target.Labels)) {
g.AddEdge(target, node, ExposedThroughServiceEdgeKind)
}
}
}
}
// AddAllExposedPodEdges calls AddExposedPodEdges for every ServiceNode in the graph
func AddAllExposedPodEdges(g osgraph.MutableUniqueGraph) {
for _, node := range g.(graph.Graph).Nodes() {
if serviceNode, ok := node.(*kubegraph.ServiceNode); ok {
AddExposedPodEdges(g, serviceNode)
}
}
}
// AddManagedByControllerPodEdges ensures that a directed edge exists between a controller and all the pods
// in the graph that match the label selector
func AddManagedByControllerPodEdges(g osgraph.MutableUniqueGraph, to graph.Node, namespace string, selector map[string]string) {
if selector == nil {
return
}
query := labels.SelectorFromSet(selector)
for _, n := range g.(graph.Graph).Nodes() {
switch target := n.(type) {
case *kubegraph.PodNode:
if target.Namespace != namespace {
continue
}
if query.Matches(labels.Set(target.Labels)) {
g.AddEdge(target, to, ManagedByControllerEdgeKind)
}
}
}
}
// AddAllManagedByControllerPodEdges calls AddManagedByControllerPodEdges for every node in the graph
// TODO: should do this through an interface (selects pods)
func AddAllManagedByControllerPodEdges(g osgraph.MutableUniqueGraph) {
for _, node := range g.(graph.Graph).Nodes() {
switch cast := node.(type) {
case *kubegraph.ReplicationControllerNode:
AddManagedByControllerPodEdges(g, cast, cast.ReplicationController.Namespace, cast.ReplicationController.Spec.Selector)
case *kubegraph.PetSetNode:
// TODO: refactor to handle expanded selectors (along with ReplicaSets and Deployments)
AddManagedByControllerPodEdges(g, cast, cast.PetSet.Namespace, cast.PetSet.Spec.Selector.MatchLabels)
}
}
}
func AddMountedSecretEdges(g osgraph.Graph, podSpec *kubegraph.PodSpecNode) {
//pod specs are always contained. We'll get the toplevel container so that we can pull a namespace from it
containerNode := osgraph.GetTopLevelContainerNode(g, podSpec)
containerObj := g.GraphDescriber.Object(containerNode)
meta, err := kapi.ObjectMetaFor(containerObj.(runtime.Object))
if err != nil {
// this should never happen. it means that a podSpec is owned by a top level container that is not a runtime.Object
panic(err)
}
for _, volume := range podSpec.Volumes {
source := volume.VolumeSource
if source.Secret == nil {
continue
}
// pod secrets must be in the same namespace
syntheticSecret := &kapi.Secret{}
syntheticSecret.Namespace = meta.Namespace
syntheticSecret.Name = source.Secret.SecretName
secretNode := kubegraph.FindOrCreateSyntheticSecretNode(g, syntheticSecret)
g.AddEdge(podSpec, secretNode, MountedSecretEdgeKind)
}
}
func AddAllMountedSecretEdges(g osgraph.Graph) {
for _, node := range g.Nodes() {
if podSpecNode, ok := node.(*kubegraph.PodSpecNode); ok {
AddMountedSecretEdges(g, podSpecNode)
}
}
}
func AddMountableSecretEdges(g osgraph.Graph, saNode *kubegraph.ServiceAccountNode) {
for _, mountableSecret := range saNode.ServiceAccount.Secrets {
syntheticSecret := &kapi.Secret{}
syntheticSecret.Namespace = saNode.ServiceAccount.Namespace
syntheticSecret.Name = mountableSecret.Name
secretNode := kubegraph.FindOrCreateSyntheticSecretNode(g, syntheticSecret)
g.AddEdge(saNode, secretNode, MountableSecretEdgeKind)
}
}
func AddAllMountableSecretEdges(g osgraph.Graph) {
for _, node := range g.Nodes() {
if saNode, ok := node.(*kubegraph.ServiceAccountNode); ok {
AddMountableSecretEdges(g, saNode)
}
}
}
func AddRequestedServiceAccountEdges(g osgraph.Graph, podSpecNode *kubegraph.PodSpecNode) {
//pod specs are always contained. We'll get the toplevel container so that we can pull a namespace from it
containerNode := osgraph.GetTopLevelContainerNode(g, podSpecNode)
containerObj := g.GraphDescriber.Object(containerNode)
meta, err := kapi.ObjectMetaFor(containerObj.(runtime.Object))
if err != nil {
panic(err)
}
// if no SA name is present, admission will set 'default'
name := "default"
if len(podSpecNode.ServiceAccountName) > 0 {
name = podSpecNode.ServiceAccountName
}
syntheticSA := &kapi.ServiceAccount{}
syntheticSA.Namespace = meta.Namespace
syntheticSA.Name = name
saNode := kubegraph.FindOrCreateSyntheticServiceAccountNode(g, syntheticSA)
g.AddEdge(podSpecNode, saNode, ReferencedServiceAccountEdgeKind)
}
func AddAllRequestedServiceAccountEdges(g osgraph.Graph) {
for _, node := range g.Nodes() {
if podSpecNode, ok := node.(*kubegraph.PodSpecNode); ok {
AddRequestedServiceAccountEdges(g, podSpecNode)
}
}
}
func AddHPAScaleRefEdges(g osgraph.Graph) {
for _, node := range g.NodesByKind(kubegraph.HorizontalPodAutoscalerNodeKind) {
hpaNode := node.(*kubegraph.HorizontalPodAutoscalerNode)
syntheticMeta := kapi.ObjectMeta{
Name: hpaNode.HorizontalPodAutoscaler.Spec.ScaleTargetRef.Name,
Namespace: hpaNode.HorizontalPodAutoscaler.Namespace,
}
var groupVersionResource unversioned.GroupVersionResource
resource := strings.ToLower(hpaNode.HorizontalPodAutoscaler.Spec.ScaleTargetRef.Kind)
if groupVersion, err := unversioned.ParseGroupVersion(hpaNode.HorizontalPodAutoscaler.Spec.ScaleTargetRef.APIVersion); err == nil {
groupVersionResource = groupVersion.WithResource(resource)
} else {
groupVersionResource = unversioned.GroupVersionResource{Resource: resource}
}
groupVersionResource, err := registered.RESTMapper().ResourceFor(groupVersionResource)
if err != nil {
continue
}
var syntheticNode graph.Node
switch groupVersionResource.GroupResource() {
case kapi.Resource("replicationcontrollers"):
syntheticNode = kubegraph.FindOrCreateSyntheticReplicationControllerNode(g, &kapi.ReplicationController{ObjectMeta: syntheticMeta})
case deployapi.Resource("deploymentconfigs"):
syntheticNode = deploygraph.FindOrCreateSyntheticDeploymentConfigNode(g, &deployapi.DeploymentConfig{ObjectMeta: syntheticMeta})
default:
continue
}
g.AddEdge(hpaNode, syntheticNode, ScalingEdgeKind)
}
}
+187
View File
@@ -0,0 +1,187 @@
package nodes
import (
"github.com/gonum/graph"
kapi "k8s.io/kubernetes/pkg/api"
kapps "k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/apis/autoscaling"
osgraph "github.com/openshift/origin/pkg/api/graph"
)
func EnsurePodNode(g osgraph.MutableUniqueGraph, pod *kapi.Pod) *PodNode {
podNodeName := PodNodeName(pod)
podNode := osgraph.EnsureUnique(g,
podNodeName,
func(node osgraph.Node) graph.Node {
return &PodNode{node, pod}
},
).(*PodNode)
podSpecNode := EnsurePodSpecNode(g, &pod.Spec, pod.Namespace, podNodeName)
g.AddEdge(podNode, podSpecNode, osgraph.ContainsEdgeKind)
return podNode
}
func EnsurePodSpecNode(g osgraph.MutableUniqueGraph, podSpec *kapi.PodSpec, namespace string, ownerName osgraph.UniqueName) *PodSpecNode {
return osgraph.EnsureUnique(g,
PodSpecNodeName(podSpec, ownerName),
func(node osgraph.Node) graph.Node {
return &PodSpecNode{node, podSpec, namespace, ownerName}
},
).(*PodSpecNode)
}
// EnsureServiceNode adds the provided service to the graph if it does not already exist.
func EnsureServiceNode(g osgraph.MutableUniqueGraph, svc *kapi.Service) *ServiceNode {
return osgraph.EnsureUnique(g,
ServiceNodeName(svc),
func(node osgraph.Node) graph.Node {
return &ServiceNode{node, svc, true}
},
).(*ServiceNode)
}
// FindOrCreateSyntheticServiceNode returns the existing service node or creates a synthetic node in its place
func FindOrCreateSyntheticServiceNode(g osgraph.MutableUniqueGraph, svc *kapi.Service) *ServiceNode {
return osgraph.EnsureUnique(g,
ServiceNodeName(svc),
func(node osgraph.Node) graph.Node {
return &ServiceNode{node, svc, false}
},
).(*ServiceNode)
}
func EnsureServiceAccountNode(g osgraph.MutableUniqueGraph, o *kapi.ServiceAccount) *ServiceAccountNode {
return osgraph.EnsureUnique(g,
ServiceAccountNodeName(o),
func(node osgraph.Node) graph.Node {
return &ServiceAccountNode{node, o, true}
},
).(*ServiceAccountNode)
}
func FindOrCreateSyntheticServiceAccountNode(g osgraph.MutableUniqueGraph, o *kapi.ServiceAccount) *ServiceAccountNode {
return osgraph.EnsureUnique(g,
ServiceAccountNodeName(o),
func(node osgraph.Node) graph.Node {
return &ServiceAccountNode{node, o, false}
},
).(*ServiceAccountNode)
}
func EnsureSecretNode(g osgraph.MutableUniqueGraph, o *kapi.Secret) *SecretNode {
return osgraph.EnsureUnique(g,
SecretNodeName(o),
func(node osgraph.Node) graph.Node {
return &SecretNode{node, o, true}
},
).(*SecretNode)
}
func FindOrCreateSyntheticSecretNode(g osgraph.MutableUniqueGraph, o *kapi.Secret) *SecretNode {
return osgraph.EnsureUnique(g,
SecretNodeName(o),
func(node osgraph.Node) graph.Node {
return &SecretNode{node, o, false}
},
).(*SecretNode)
}
// EnsureReplicationControllerNode adds a graph node for the ReplicationController if it does not already exist.
func EnsureReplicationControllerNode(g osgraph.MutableUniqueGraph, rc *kapi.ReplicationController) *ReplicationControllerNode {
rcNodeName := ReplicationControllerNodeName(rc)
rcNode := osgraph.EnsureUnique(g,
rcNodeName,
func(node osgraph.Node) graph.Node {
return &ReplicationControllerNode{node, rc, true}
},
).(*ReplicationControllerNode)
rcSpecNode := EnsureReplicationControllerSpecNode(g, &rc.Spec, rc.Namespace, rcNodeName)
g.AddEdge(rcNode, rcSpecNode, osgraph.ContainsEdgeKind)
return rcNode
}
func FindOrCreateSyntheticReplicationControllerNode(g osgraph.MutableUniqueGraph, rc *kapi.ReplicationController) *ReplicationControllerNode {
return osgraph.EnsureUnique(g,
ReplicationControllerNodeName(rc),
func(node osgraph.Node) graph.Node {
return &ReplicationControllerNode{node, rc, false}
},
).(*ReplicationControllerNode)
}
func EnsureReplicationControllerSpecNode(g osgraph.MutableUniqueGraph, rcSpec *kapi.ReplicationControllerSpec, namespace string, ownerName osgraph.UniqueName) *ReplicationControllerSpecNode {
rcSpecName := ReplicationControllerSpecNodeName(rcSpec, ownerName)
rcSpecNode := osgraph.EnsureUnique(g,
rcSpecName,
func(node osgraph.Node) graph.Node {
return &ReplicationControllerSpecNode{node, rcSpec, namespace, ownerName}
},
).(*ReplicationControllerSpecNode)
if rcSpec.Template != nil {
ptSpecNode := EnsurePodTemplateSpecNode(g, rcSpec.Template, namespace, rcSpecName)
g.AddEdge(rcSpecNode, ptSpecNode, osgraph.ContainsEdgeKind)
}
return rcSpecNode
}
func EnsurePodTemplateSpecNode(g osgraph.MutableUniqueGraph, ptSpec *kapi.PodTemplateSpec, namespace string, ownerName osgraph.UniqueName) *PodTemplateSpecNode {
ptSpecName := PodTemplateSpecNodeName(ptSpec, ownerName)
ptSpecNode := osgraph.EnsureUnique(g,
ptSpecName,
func(node osgraph.Node) graph.Node {
return &PodTemplateSpecNode{node, ptSpec, namespace, ownerName}
},
).(*PodTemplateSpecNode)
podSpecNode := EnsurePodSpecNode(g, &ptSpec.Spec, namespace, ptSpecName)
g.AddEdge(ptSpecNode, podSpecNode, osgraph.ContainsEdgeKind)
return ptSpecNode
}
func EnsureHorizontalPodAutoscalerNode(g osgraph.MutableUniqueGraph, hpa *autoscaling.HorizontalPodAutoscaler) *HorizontalPodAutoscalerNode {
return osgraph.EnsureUnique(g,
HorizontalPodAutoscalerNodeName(hpa),
func(node osgraph.Node) graph.Node {
return &HorizontalPodAutoscalerNode{Node: node, HorizontalPodAutoscaler: hpa}
},
).(*HorizontalPodAutoscalerNode)
}
func EnsurePetSetNode(g osgraph.MutableUniqueGraph, petset *kapps.PetSet) *PetSetNode {
nodeName := PetSetNodeName(petset)
node := osgraph.EnsureUnique(g,
nodeName,
func(node osgraph.Node) graph.Node {
return &PetSetNode{node, petset}
},
).(*PetSetNode)
specNode := EnsurePetSetSpecNode(g, &petset.Spec, petset.Namespace, nodeName)
g.AddEdge(node, specNode, osgraph.ContainsEdgeKind)
return node
}
func EnsurePetSetSpecNode(g osgraph.MutableUniqueGraph, spec *kapps.PetSetSpec, namespace string, ownerName osgraph.UniqueName) *PetSetSpecNode {
specName := PetSetSpecNodeName(spec, ownerName)
specNode := osgraph.EnsureUnique(g,
specName,
func(node osgraph.Node) graph.Node {
return &PetSetSpecNode{node, spec, namespace, ownerName}
},
).(*PetSetSpecNode)
ptSpecNode := EnsurePodTemplateSpecNode(g, &spec.Template, namespace, specName)
g.AddEdge(specNode, ptSpecNode, osgraph.ContainsEdgeKind)
return specNode
}
+325
View File
@@ -0,0 +1,325 @@
package nodes
import (
"fmt"
"reflect"
kapi "k8s.io/kubernetes/pkg/api"
kapps "k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/apis/autoscaling"
osgraph "github.com/openshift/origin/pkg/api/graph"
)
var (
ServiceNodeKind = reflect.TypeOf(kapi.Service{}).Name()
PodNodeKind = reflect.TypeOf(kapi.Pod{}).Name()
PodSpecNodeKind = reflect.TypeOf(kapi.PodSpec{}).Name()
PodTemplateSpecNodeKind = reflect.TypeOf(kapi.PodTemplateSpec{}).Name()
ReplicationControllerNodeKind = reflect.TypeOf(kapi.ReplicationController{}).Name()
ReplicationControllerSpecNodeKind = reflect.TypeOf(kapi.ReplicationControllerSpec{}).Name()
ServiceAccountNodeKind = reflect.TypeOf(kapi.ServiceAccount{}).Name()
SecretNodeKind = reflect.TypeOf(kapi.Secret{}).Name()
HorizontalPodAutoscalerNodeKind = reflect.TypeOf(autoscaling.HorizontalPodAutoscaler{}).Name()
PetSetNodeKind = reflect.TypeOf(kapps.PetSet{}).Name()
PetSetSpecNodeKind = reflect.TypeOf(kapps.PetSetSpec{}).Name()
)
func ServiceNodeName(o *kapi.Service) osgraph.UniqueName {
return osgraph.GetUniqueRuntimeObjectNodeName(ServiceNodeKind, o)
}
type ServiceNode struct {
osgraph.Node
*kapi.Service
IsFound bool
}
func (n ServiceNode) Object() interface{} {
return n.Service
}
func (n ServiceNode) String() string {
return string(ServiceNodeName(n.Service))
}
func (*ServiceNode) Kind() string {
return ServiceNodeKind
}
func (n ServiceNode) Found() bool {
return n.IsFound
}
func PodNodeName(o *kapi.Pod) osgraph.UniqueName {
return osgraph.GetUniqueRuntimeObjectNodeName(PodNodeKind, o)
}
type PodNode struct {
osgraph.Node
*kapi.Pod
}
func (n PodNode) Object() interface{} {
return n.Pod
}
func (n PodNode) String() string {
return string(PodNodeName(n.Pod))
}
func (n PodNode) UniqueName() osgraph.UniqueName {
return PodNodeName(n.Pod)
}
func (*PodNode) Kind() string {
return PodNodeKind
}
func PodSpecNodeName(o *kapi.PodSpec, ownerName osgraph.UniqueName) osgraph.UniqueName {
return osgraph.UniqueName(fmt.Sprintf("%s|%v", PodSpecNodeKind, ownerName))
}
type PodSpecNode struct {
osgraph.Node
*kapi.PodSpec
Namespace string
OwnerName osgraph.UniqueName
}
func (n PodSpecNode) Object() interface{} {
return n.PodSpec
}
func (n PodSpecNode) String() string {
return string(n.UniqueName())
}
func (n PodSpecNode) UniqueName() osgraph.UniqueName {
return PodSpecNodeName(n.PodSpec, n.OwnerName)
}
func (*PodSpecNode) Kind() string {
return PodSpecNodeKind
}
func ReplicationControllerNodeName(o *kapi.ReplicationController) osgraph.UniqueName {
return osgraph.GetUniqueRuntimeObjectNodeName(ReplicationControllerNodeKind, o)
}
type ReplicationControllerNode struct {
osgraph.Node
ReplicationController *kapi.ReplicationController
IsFound bool
}
func (n ReplicationControllerNode) Found() bool {
return n.IsFound
}
func (n ReplicationControllerNode) Object() interface{} {
return n.ReplicationController
}
func (n ReplicationControllerNode) String() string {
return string(ReplicationControllerNodeName(n.ReplicationController))
}
func (n ReplicationControllerNode) UniqueName() osgraph.UniqueName {
return ReplicationControllerNodeName(n.ReplicationController)
}
func (*ReplicationControllerNode) Kind() string {
return ReplicationControllerNodeKind
}
func ReplicationControllerSpecNodeName(o *kapi.ReplicationControllerSpec, ownerName osgraph.UniqueName) osgraph.UniqueName {
return osgraph.UniqueName(fmt.Sprintf("%s|%v", ReplicationControllerSpecNodeKind, ownerName))
}
type ReplicationControllerSpecNode struct {
osgraph.Node
ReplicationControllerSpec *kapi.ReplicationControllerSpec
Namespace string
OwnerName osgraph.UniqueName
}
func (n ReplicationControllerSpecNode) Object() interface{} {
return n.ReplicationControllerSpec
}
func (n ReplicationControllerSpecNode) String() string {
return string(n.UniqueName())
}
func (n ReplicationControllerSpecNode) UniqueName() osgraph.UniqueName {
return ReplicationControllerSpecNodeName(n.ReplicationControllerSpec, n.OwnerName)
}
func (*ReplicationControllerSpecNode) Kind() string {
return ReplicationControllerSpecNodeKind
}
func PodTemplateSpecNodeName(o *kapi.PodTemplateSpec, ownerName osgraph.UniqueName) osgraph.UniqueName {
return osgraph.UniqueName(fmt.Sprintf("%s|%v", PodTemplateSpecNodeKind, ownerName))
}
type PodTemplateSpecNode struct {
osgraph.Node
*kapi.PodTemplateSpec
Namespace string
OwnerName osgraph.UniqueName
}
func (n PodTemplateSpecNode) Object() interface{} {
return n.PodTemplateSpec
}
func (n PodTemplateSpecNode) String() string {
return string(n.UniqueName())
}
func (n PodTemplateSpecNode) UniqueName() osgraph.UniqueName {
return PodTemplateSpecNodeName(n.PodTemplateSpec, n.OwnerName)
}
func (*PodTemplateSpecNode) Kind() string {
return PodTemplateSpecNodeKind
}
func ServiceAccountNodeName(o *kapi.ServiceAccount) osgraph.UniqueName {
return osgraph.GetUniqueRuntimeObjectNodeName(ServiceAccountNodeKind, o)
}
type ServiceAccountNode struct {
osgraph.Node
*kapi.ServiceAccount
IsFound bool
}
func (n ServiceAccountNode) Found() bool {
return n.IsFound
}
func (n ServiceAccountNode) Object() interface{} {
return n.ServiceAccount
}
func (n ServiceAccountNode) String() string {
return string(ServiceAccountNodeName(n.ServiceAccount))
}
func (*ServiceAccountNode) Kind() string {
return ServiceAccountNodeKind
}
func SecretNodeName(o *kapi.Secret) osgraph.UniqueName {
return osgraph.GetUniqueRuntimeObjectNodeName(SecretNodeKind, o)
}
type SecretNode struct {
osgraph.Node
*kapi.Secret
IsFound bool
}
func (n SecretNode) Found() bool {
return n.IsFound
}
func (n SecretNode) Object() interface{} {
return n.Secret
}
func (n SecretNode) String() string {
return string(SecretNodeName(n.Secret))
}
func (*SecretNode) Kind() string {
return SecretNodeKind
}
func HorizontalPodAutoscalerNodeName(o *autoscaling.HorizontalPodAutoscaler) osgraph.UniqueName {
return osgraph.GetUniqueRuntimeObjectNodeName(HorizontalPodAutoscalerNodeKind, o)
}
type HorizontalPodAutoscalerNode struct {
osgraph.Node
HorizontalPodAutoscaler *autoscaling.HorizontalPodAutoscaler
}
func (n HorizontalPodAutoscalerNode) Object() interface{} {
return n.HorizontalPodAutoscaler
}
func (n HorizontalPodAutoscalerNode) String() string {
return string(n.UniqueName())
}
func (*HorizontalPodAutoscalerNode) Kind() string {
return HorizontalPodAutoscalerNodeKind
}
func (n HorizontalPodAutoscalerNode) UniqueName() osgraph.UniqueName {
return HorizontalPodAutoscalerNodeName(n.HorizontalPodAutoscaler)
}
func PetSetNodeName(o *kapps.PetSet) osgraph.UniqueName {
return osgraph.GetUniqueRuntimeObjectNodeName(PetSetNodeKind, o)
}
type PetSetNode struct {
osgraph.Node
PetSet *kapps.PetSet
}
func (n PetSetNode) Object() interface{} {
return n.PetSet
}
func (n PetSetNode) String() string {
return string(n.UniqueName())
}
func (n PetSetNode) UniqueName() osgraph.UniqueName {
return PetSetNodeName(n.PetSet)
}
func (*PetSetNode) Kind() string {
return PetSetNodeKind
}
func PetSetSpecNodeName(o *kapps.PetSetSpec, ownerName osgraph.UniqueName) osgraph.UniqueName {
return osgraph.UniqueName(fmt.Sprintf("%s|%v", PetSetSpecNodeKind, ownerName))
}
type PetSetSpecNode struct {
osgraph.Node
PetSetSpec *kapps.PetSetSpec
Namespace string
OwnerName osgraph.UniqueName
}
func (n PetSetSpecNode) Object() interface{} {
return n.PetSetSpec
}
func (n PetSetSpecNode) String() string {
return string(n.UniqueName())
}
func (n PetSetSpecNode) UniqueName() osgraph.UniqueName {
return PetSetSpecNodeName(n.PetSetSpec, n.OwnerName)
}
func (*PetSetSpecNode) Kind() string {
return PetSetSpecNodeKind
}
+5
View File
@@ -0,0 +1,5 @@
// Package latest defines the default output serializations that code should
// use and imports the required schemas. It also ensures all previously known
// and supported API versions are available for conversion. Consumers may
// import this package in lieu of importing individual versions.
package latest
+73
View File
@@ -0,0 +1,73 @@
package latest
import (
"strings"
"sync"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
)
// HACK TO ELIMINATE CYCLES UNTIL WE KILL THIS PACKAGE
// Version is the string that represents the current external default version.
var Version = unversioned.GroupVersion{Group: "", Version: "v1"}
// OldestVersion is the string that represents the oldest server version supported,
// for client code that wants to hardcode the lowest common denominator.
var OldestVersion = unversioned.GroupVersion{Group: "", Version: "v1"}
// Versions is the list of versions that are recognized in code. The order provided
// may be assumed to be most preferred to least preferred, and clients may
// choose to prefer the earlier items in the list over the latter items when presented
// with a set of versions to choose.
var Versions = []unversioned.GroupVersion{{Group: "", Version: "v1"}}
// originTypes are the hardcoded types defined by the OpenShift API.
var originTypes map[unversioned.GroupVersionKind]bool
// originTypesLock allows lazying initialization of originTypes to allow initializers to run before
// loading the map. It means that initializers have to know ahead of time where their type is from,
// but that is not onerous
var originTypesLock sync.Once
// OriginKind returns true if OpenShift owns the GroupVersionKind.
func OriginKind(gvk unversioned.GroupVersionKind) bool {
return getOrCreateOriginKinds()[gvk]
}
// IsKindInAnyOriginGroup returns true if OpenShift owns the kind described in any apiVersion.
// TODO: this may not work once we divide builds/deployments/images into their own API groups
func IsKindInAnyOriginGroup(kind string) bool {
for _, version := range Versions {
if OriginKind(version.WithKind(kind)) {
return true
}
}
return false
}
func getOrCreateOriginKinds() map[unversioned.GroupVersionKind]bool {
if originTypes == nil {
originTypesLock.Do(func() {
newOriginTypes := map[unversioned.GroupVersionKind]bool{}
// enumerate all supported versions, get the kinds, and register with the mapper how to address our resources
for _, version := range Versions {
for kind, t := range api.Scheme.KnownTypes(version) {
if !strings.Contains(t.PkgPath(), "github.com/openshift/origin") || strings.Contains(t.PkgPath(), "github.com/openshift/origin/vendor/") {
continue
}
gvk := version.WithKind(kind)
newOriginTypes[gvk] = true
}
}
originTypes = newOriginTypes
})
return originTypes
}
return originTypes
}
+176
View File
@@ -0,0 +1,176 @@
package restmapper
import (
"sync"
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apimachinery/registered"
"k8s.io/kubernetes/pkg/client/typed/discovery"
)
type discoveryRESTMapper struct {
discoveryClient discovery.DiscoveryInterface
delegate meta.RESTMapper
initLock sync.Mutex
}
// NewDiscoveryRESTMapper that initializes using the discovery APIs, relying on group ordering and preferred versions
// to build its appropriate priorities. Only versions are registered with API machinery are added now.
// TODO make this work with generic resources at some point. For now, this handles enabled and disabled resources cleanly.
func NewDiscoveryRESTMapper(discoveryClient discovery.DiscoveryInterface) meta.RESTMapper {
return &discoveryRESTMapper{discoveryClient: discoveryClient}
}
func (d *discoveryRESTMapper) getDelegate() (meta.RESTMapper, error) {
d.initLock.Lock()
defer d.initLock.Unlock()
if d.delegate != nil {
return d.delegate, nil
}
serverGroups, err := d.discoveryClient.ServerGroups()
if err != nil {
return nil, err
}
// always prefer our default group for now. The version should be discovered from discovery, but this will hold us
// for quite some time.
resourcePriority := []unversioned.GroupVersionResource{
{Group: kapi.GroupName, Version: meta.AnyVersion, Resource: meta.AnyResource},
}
kindPriority := []unversioned.GroupVersionKind{
{Group: kapi.GroupName, Version: meta.AnyVersion, Kind: meta.AnyKind},
}
groupPriority := []string{}
unionMapper := meta.MultiRESTMapper{}
for _, group := range serverGroups.Groups {
if len(group.Versions) == 0 {
continue
}
groupPriority = append(groupPriority, group.Name)
if len(group.PreferredVersion.Version) != 0 {
preferredVersion := unversioned.GroupVersion{Group: group.Name, Version: group.PreferredVersion.Version}
if registered.IsEnabledVersion(preferredVersion) {
resourcePriority = append(resourcePriority, preferredVersion.WithResource(meta.AnyResource))
kindPriority = append(kindPriority, preferredVersion.WithKind(meta.AnyKind))
}
}
for _, discoveryVersion := range group.Versions {
version := unversioned.GroupVersion{Group: group.Name, Version: discoveryVersion.Version}
if !registered.IsEnabledVersion(version) {
continue
}
groupMeta, err := registered.Group(group.Name)
if err != nil {
return nil, err
}
resources, err := d.discoveryClient.ServerResourcesForGroupVersion(version.String())
if err != nil {
return nil, err
}
versionMapper := meta.NewDefaultRESTMapper([]unversioned.GroupVersion{version}, groupMeta.InterfacesFor)
for _, resource := range resources.APIResources {
// TODO properly handle resource versus kind
gvk := version.WithKind(resource.Kind)
scope := meta.RESTScopeNamespace
if !resource.Namespaced {
scope = meta.RESTScopeRoot
}
versionMapper.Add(gvk, scope)
// TODO formalize this by checking to see if they support listing
versionMapper.Add(version.WithKind(resource.Kind+"List"), scope)
}
// we need to add List. Its a special case of something we need that isn't in the discovery doc
if group.Name == kapi.GroupName {
versionMapper.Add(version.WithKind("List"), meta.RESTScopeNamespace)
}
unionMapper = append(unionMapper, versionMapper)
}
}
for _, group := range groupPriority {
resourcePriority = append(resourcePriority, unversioned.GroupVersionResource{Group: group, Version: meta.AnyVersion, Resource: meta.AnyResource})
kindPriority = append(kindPriority, unversioned.GroupVersionKind{Group: group, Version: meta.AnyVersion, Kind: meta.AnyKind})
}
d.delegate = meta.PriorityRESTMapper{Delegate: unionMapper, ResourcePriority: resourcePriority, KindPriority: kindPriority}
return d.delegate, nil
}
func (d *discoveryRESTMapper) KindFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionKind, error) {
delegate, err := d.getDelegate()
if err != nil {
return unversioned.GroupVersionKind{}, err
}
return delegate.KindFor(resource)
}
func (d *discoveryRESTMapper) KindsFor(resource unversioned.GroupVersionResource) ([]unversioned.GroupVersionKind, error) {
delegate, err := d.getDelegate()
if err != nil {
return nil, err
}
return delegate.KindsFor(resource)
}
func (d *discoveryRESTMapper) ResourceFor(input unversioned.GroupVersionResource) (unversioned.GroupVersionResource, error) {
delegate, err := d.getDelegate()
if err != nil {
return unversioned.GroupVersionResource{}, err
}
return delegate.ResourceFor(input)
}
func (d *discoveryRESTMapper) ResourcesFor(input unversioned.GroupVersionResource) ([]unversioned.GroupVersionResource, error) {
delegate, err := d.getDelegate()
if err != nil {
return nil, err
}
return delegate.ResourcesFor(input)
}
func (d *discoveryRESTMapper) RESTMapping(gk unversioned.GroupKind, versions ...string) (*meta.RESTMapping, error) {
delegate, err := d.getDelegate()
if err != nil {
return nil, err
}
return delegate.RESTMapping(gk, versions...)
}
func (d *discoveryRESTMapper) RESTMappings(gk unversioned.GroupKind) ([]*meta.RESTMapping, error) {
delegate, err := d.getDelegate()
if err != nil {
return nil, err
}
return delegate.RESTMappings(gk)
}
func (d *discoveryRESTMapper) AliasesForResource(resource string) ([]string, bool) {
delegate, err := d.getDelegate()
if err != nil {
return nil, false
}
return delegate.AliasesForResource(resource)
}
func (d *discoveryRESTMapper) ResourceSingularizer(resource string) (singular string, err error) {
delegate, err := d.getDelegate()
if err != nil {
return resource, err
}
return delegate.ResourceSingularizer(resource)
}
+85
View File
@@ -0,0 +1,85 @@
package api
import (
"k8s.io/kubernetes/pkg/auth/user"
)
const (
// IdentityDisplayNameKey is the key for an optional display name in an identity's Extra map
IdentityDisplayNameKey = "name"
// IdentityEmailKey is the key for an optional email address in an identity's Extra map
IdentityEmailKey = "email"
// IdentityPreferredUsernameKey is the key for an optional preferred username in an identity's Extra map.
// This is useful when the immutable providerUserName is different than the login used to authenticate
// If present, this extra value is used as the preferred username
IdentityPreferredUsernameKey = "preferred_username"
ImpersonateUserHeader = "Impersonate-User"
ImpersonateGroupHeader = "Impersonate-Group"
ImpersonateUserScopeHeader = "Impersonate-User-Scope"
)
// UserIdentityInfo contains information about an identity. Identities are distinct from users. An authentication server of
// some kind (like oauth for example) describes an identity. Our system controls the users mapped to this identity.
type UserIdentityInfo interface {
// GetIdentityName returns the name of this identity. It must be equal to GetProviderName() + ":" + GetProviderUserName()
GetIdentityName() string
// GetProviderName returns the name of the provider of this identity.
GetProviderName() string
// GetProviderUserName uniquely identifies this particular identity for this provider. It is NOT guaranteed to be unique across providers
GetProviderUserName() string
// GetExtra is a map to allow providers to add additional fields that they understand
GetExtra() map[string]string
}
// UserIdentityMapper maps UserIdentities into user.Info objects to allow different user abstractions within auth code.
type UserIdentityMapper interface {
// UserFor takes an identity, ignores the passed identity.Provider, forces the provider value to some other value and then creates the mapping.
// It returns the corresponding user.Info
UserFor(identityInfo UserIdentityInfo) (user.Info, error)
}
type Client interface {
GetId() string
ValidateSecret(secret string) bool
GetRedirectUri() string
GetUserData() interface{}
}
type Grant struct {
Client Client
Scope string
Expiration int64
RedirectURI string
}
type DefaultUserIdentityInfo struct {
ProviderName string
ProviderUserName string
Extra map[string]string
}
// NewDefaultUserIdentityInfo returns a DefaultUserIdentityInfo with a non-nil Extra component
func NewDefaultUserIdentityInfo(providerName, providerUserName string) *DefaultUserIdentityInfo {
return &DefaultUserIdentityInfo{
ProviderName: providerName,
ProviderUserName: providerUserName,
Extra: map[string]string{},
}
}
func (i *DefaultUserIdentityInfo) GetIdentityName() string {
return i.ProviderName + ":" + i.ProviderUserName
}
func (i *DefaultUserIdentityInfo) GetProviderName() string {
return i.ProviderName
}
func (i *DefaultUserIdentityInfo) GetProviderUserName() string {
return i.ProviderUserName
}
func (i *DefaultUserIdentityInfo) GetExtra() map[string]string {
return i.Extra
}
@@ -0,0 +1,34 @@
package authenticator
import (
"net/http"
"github.com/openshift/origin/pkg/auth/api"
"k8s.io/kubernetes/pkg/auth/user"
)
type Token interface {
AuthenticateToken(token string) (user.Info, bool, error)
}
type Request interface {
AuthenticateRequest(req *http.Request) (user.Info, bool, error)
}
type Password interface {
AuthenticatePassword(user, password string) (user.Info, bool, error)
}
type Assertion interface {
AuthenticateAssertion(assertionType, data string) (user.Info, bool, error)
}
type Client interface {
AuthenticateClient(client api.Client) (user.Info, bool, error)
}
type RequestFunc func(req *http.Request) (user.Info, bool, error)
func (f RequestFunc) AuthenticateRequest(req *http.Request) (user.Info, bool, error) {
return f(req)
}
@@ -0,0 +1,3 @@
// Package x509request provides a request authenticator that validates and
// extracts user information from client certificates
package x509request
@@ -0,0 +1,173 @@
package x509request
import (
"crypto/x509"
"crypto/x509/pkix"
"fmt"
"net/http"
"github.com/golang/glog"
"github.com/openshift/origin/pkg/auth/authenticator"
"k8s.io/kubernetes/pkg/auth/user"
kerrors "k8s.io/kubernetes/pkg/util/errors"
"k8s.io/kubernetes/pkg/util/sets"
)
// UserConversion defines an interface for extracting user info from a client certificate chain
type UserConversion interface {
User(chain []*x509.Certificate) (user.Info, bool, error)
}
// UserConversionFunc is a function that implements the UserConversion interface.
type UserConversionFunc func(chain []*x509.Certificate) (user.Info, bool, error)
// User implements x509.UserConversion
func (f UserConversionFunc) User(chain []*x509.Certificate) (user.Info, bool, error) {
return f(chain)
}
// Authenticator implements request.Authenticator by extracting user info from verified client certificates
type Authenticator struct {
opts x509.VerifyOptions
user UserConversion
}
// New returns a request.Authenticator that verifies client certificates using the provided
// VerifyOptions, and converts valid certificate chains into user.Info using the provided UserConversion
func New(opts x509.VerifyOptions, user UserConversion) *Authenticator {
return &Authenticator{opts, user}
}
// AuthenticateRequest authenticates the request using presented client certificates
func (a *Authenticator) AuthenticateRequest(req *http.Request) (user.Info, bool, error) {
if req.TLS == nil {
return nil, false, nil
}
var errlist []error
for _, cert := range req.TLS.PeerCertificates {
chains, err := cert.Verify(a.opts)
if err != nil {
errlist = append(errlist, err)
continue
}
for _, chain := range chains {
user, ok, err := a.user.User(chain)
if err != nil {
errlist = append(errlist, err)
continue
}
if ok {
return user, ok, err
}
}
}
return nil, false, kerrors.NewAggregate(errlist)
}
// Verifier implements request.Authenticator by verifying a client cert on the request, then delegating to the wrapped auth
type Verifier struct {
opts x509.VerifyOptions
auth authenticator.Request
// allowedCommonNames contains the common names which a verified certificate is allowed to have.
// If empty, all verified certificates are allowed.
allowedCommonNames sets.String
}
func NewVerifier(opts x509.VerifyOptions, auth authenticator.Request, allowedCommonNames sets.String) authenticator.Request {
return &Verifier{opts, auth, allowedCommonNames}
}
// AuthenticateRequest verifies the presented client certificates, then delegates to the wrapped auth
func (a *Verifier) AuthenticateRequest(req *http.Request) (user.Info, bool, error) {
if req.TLS == nil {
return nil, false, nil
}
var errlist []error
for _, cert := range req.TLS.PeerCertificates {
if _, err := cert.Verify(a.opts); err != nil {
errlist = append(errlist, err)
continue
}
if err := a.verifySubject(cert.Subject); err != nil {
errlist = append(errlist, err)
continue
}
return a.auth.AuthenticateRequest(req)
}
return nil, false, kerrors.NewAggregate(errlist)
}
func (a *Verifier) verifySubject(subject pkix.Name) error {
// No CN restrictions
if len(a.allowedCommonNames) == 0 {
return nil
}
// Enforce CN restrictions
if a.allowedCommonNames.Has(subject.CommonName) {
return nil
}
glog.Warningf("x509: subject with cn=%s is not in the allowed list: %v", subject.CommonName, a.allowedCommonNames.List())
return fmt.Errorf("x509: subject with cn=%s is not allowed", subject.CommonName)
}
// DefaultVerifyOptions returns VerifyOptions that use the system root certificates, current time,
// and requires certificates to be valid for client auth (x509.ExtKeyUsageClientAuth)
func DefaultVerifyOptions() x509.VerifyOptions {
return x509.VerifyOptions{
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
}
}
// SubjectToUserConversion calls SubjectToUser on the subject of the first certificate in the chain.
// If the resulting user has no name, it returns nil, false, nil
var SubjectToUserConversion = UserConversionFunc(func(chain []*x509.Certificate) (user.Info, bool, error) {
user := SubjectToUser(chain[0].Subject)
if len(user.GetName()) == 0 {
return nil, false, nil
}
return user, true, nil
})
// CommonNameUserConversion builds user info from a certificate chain using the subject's CommonName
var CommonNameUserConversion = UserConversionFunc(func(chain []*x509.Certificate) (user.Info, bool, error) {
if len(chain[0].Subject.CommonName) == 0 {
return nil, false, nil
}
return &user.DefaultInfo{Name: chain[0].Subject.CommonName}, true, nil
})
// DNSNameUserConversion builds user info from a certificate chain using the first DNSName on the certificate
var DNSNameUserConversion = UserConversionFunc(func(chain []*x509.Certificate) (user.Info, bool, error) {
if len(chain[0].DNSNames) == 0 {
return nil, false, nil
}
return &user.DefaultInfo{Name: chain[0].DNSNames[0]}, true, nil
})
// EmailAddressUserConversion builds user info from a certificate chain using the first EmailAddress on the certificate
var EmailAddressUserConversion = UserConversionFunc(func(chain []*x509.Certificate) (user.Info, bool, error) {
if len(chain[0].EmailAddresses) == 0 {
return nil, false, nil
}
return &user.DefaultInfo{Name: chain[0].EmailAddresses[0]}, true, nil
})
func UserToSubject(u user.Info) pkix.Name {
return pkix.Name{
CommonName: u.GetName(),
SerialNumber: u.GetUID(),
Organization: u.GetGroups(),
}
}
func SubjectToUser(subject pkix.Name) user.Info {
return &user.DefaultInfo{
Name: subject.CommonName,
UID: subject.SerialNumber,
Groups: subject.Organization,
}
}
@@ -1,678 +0,0 @@
// +build !ignore_autogenerated_openshift
// This file was autogenerated by deepcopy-gen. Do not edit it manually!
package api
import (
api "k8s.io/kubernetes/pkg/api"
unversioned "k8s.io/kubernetes/pkg/api/unversioned"
conversion "k8s.io/kubernetes/pkg/conversion"
runtime "k8s.io/kubernetes/pkg/runtime"
sets "k8s.io/kubernetes/pkg/util/sets"
)
func init() {
if err := api.Scheme.AddGeneratedDeepCopyFuncs(
DeepCopy_api_AuthorizationAttributes,
DeepCopy_api_ClusterPolicy,
DeepCopy_api_ClusterPolicyBinding,
DeepCopy_api_ClusterPolicyBindingList,
DeepCopy_api_ClusterPolicyList,
DeepCopy_api_ClusterRole,
DeepCopy_api_ClusterRoleBinding,
DeepCopy_api_ClusterRoleBindingList,
DeepCopy_api_ClusterRoleList,
DeepCopy_api_IsPersonalSubjectAccessReview,
DeepCopy_api_LocalResourceAccessReview,
DeepCopy_api_LocalSubjectAccessReview,
DeepCopy_api_Policy,
DeepCopy_api_PolicyBinding,
DeepCopy_api_PolicyBindingList,
DeepCopy_api_PolicyList,
DeepCopy_api_PolicyRule,
DeepCopy_api_ResourceAccessReview,
DeepCopy_api_ResourceAccessReviewResponse,
DeepCopy_api_Role,
DeepCopy_api_RoleBinding,
DeepCopy_api_RoleBindingList,
DeepCopy_api_RoleList,
DeepCopy_api_SelfSubjectRulesReview,
DeepCopy_api_SelfSubjectRulesReviewSpec,
DeepCopy_api_SubjectAccessReview,
DeepCopy_api_SubjectAccessReviewResponse,
DeepCopy_api_SubjectRulesReviewStatus,
); err != nil {
// if one of the deep copy functions is malformed, detect it immediately.
panic(err)
}
}
func DeepCopy_api_AuthorizationAttributes(in AuthorizationAttributes, out *AuthorizationAttributes, c *conversion.Cloner) error {
out.Namespace = in.Namespace
out.Verb = in.Verb
out.Group = in.Group
out.Version = in.Version
out.Resource = in.Resource
out.ResourceName = in.ResourceName
if in.Content == nil {
out.Content = nil
} else if newVal, err := c.DeepCopy(in.Content); err != nil {
return err
} else {
out.Content = newVal.(runtime.Object)
}
return nil
}
func DeepCopy_api_ClusterPolicy(in ClusterPolicy, out *ClusterPolicy, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
if err := api.DeepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil {
return err
}
if err := unversioned.DeepCopy_unversioned_Time(in.LastModified, &out.LastModified, c); err != nil {
return err
}
if in.Roles != nil {
in, out := in.Roles, &out.Roles
*out = make(map[string]*ClusterRole)
for key, val := range in {
if newVal, err := c.DeepCopy(val); err != nil {
return err
} else {
(*out)[key] = newVal.(*ClusterRole)
}
}
} else {
out.Roles = nil
}
return nil
}
func DeepCopy_api_ClusterPolicyBinding(in ClusterPolicyBinding, out *ClusterPolicyBinding, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
if err := api.DeepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil {
return err
}
if err := unversioned.DeepCopy_unversioned_Time(in.LastModified, &out.LastModified, c); err != nil {
return err
}
if err := api.DeepCopy_api_ObjectReference(in.PolicyRef, &out.PolicyRef, c); err != nil {
return err
}
if in.RoleBindings != nil {
in, out := in.RoleBindings, &out.RoleBindings
*out = make(map[string]*ClusterRoleBinding)
for key, val := range in {
if newVal, err := c.DeepCopy(val); err != nil {
return err
} else {
(*out)[key] = newVal.(*ClusterRoleBinding)
}
}
} else {
out.RoleBindings = nil
}
return nil
}
func DeepCopy_api_ClusterPolicyBindingList(in ClusterPolicyBindingList, out *ClusterPolicyBindingList, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
if err := unversioned.DeepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil {
return err
}
if in.Items != nil {
in, out := in.Items, &out.Items
*out = make([]ClusterPolicyBinding, len(in))
for i := range in {
if err := DeepCopy_api_ClusterPolicyBinding(in[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Items = nil
}
return nil
}
func DeepCopy_api_ClusterPolicyList(in ClusterPolicyList, out *ClusterPolicyList, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
if err := unversioned.DeepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil {
return err
}
if in.Items != nil {
in, out := in.Items, &out.Items
*out = make([]ClusterPolicy, len(in))
for i := range in {
if err := DeepCopy_api_ClusterPolicy(in[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Items = nil
}
return nil
}
func DeepCopy_api_ClusterRole(in ClusterRole, out *ClusterRole, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
if err := api.DeepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil {
return err
}
if in.Rules != nil {
in, out := in.Rules, &out.Rules
*out = make([]PolicyRule, len(in))
for i := range in {
if err := DeepCopy_api_PolicyRule(in[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Rules = nil
}
return nil
}
func DeepCopy_api_ClusterRoleBinding(in ClusterRoleBinding, out *ClusterRoleBinding, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
if err := api.DeepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil {
return err
}
if in.Subjects != nil {
in, out := in.Subjects, &out.Subjects
*out = make([]api.ObjectReference, len(in))
for i := range in {
if err := api.DeepCopy_api_ObjectReference(in[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Subjects = nil
}
if err := api.DeepCopy_api_ObjectReference(in.RoleRef, &out.RoleRef, c); err != nil {
return err
}
return nil
}
func DeepCopy_api_ClusterRoleBindingList(in ClusterRoleBindingList, out *ClusterRoleBindingList, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
if err := unversioned.DeepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil {
return err
}
if in.Items != nil {
in, out := in.Items, &out.Items
*out = make([]ClusterRoleBinding, len(in))
for i := range in {
if err := DeepCopy_api_ClusterRoleBinding(in[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Items = nil
}
return nil
}
func DeepCopy_api_ClusterRoleList(in ClusterRoleList, out *ClusterRoleList, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
if err := unversioned.DeepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil {
return err
}
if in.Items != nil {
in, out := in.Items, &out.Items
*out = make([]ClusterRole, len(in))
for i := range in {
if err := DeepCopy_api_ClusterRole(in[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Items = nil
}
return nil
}
func DeepCopy_api_IsPersonalSubjectAccessReview(in IsPersonalSubjectAccessReview, out *IsPersonalSubjectAccessReview, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
return nil
}
func DeepCopy_api_LocalResourceAccessReview(in LocalResourceAccessReview, out *LocalResourceAccessReview, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
if err := DeepCopy_api_AuthorizationAttributes(in.Action, &out.Action, c); err != nil {
return err
}
return nil
}
func DeepCopy_api_LocalSubjectAccessReview(in LocalSubjectAccessReview, out *LocalSubjectAccessReview, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
if err := DeepCopy_api_AuthorizationAttributes(in.Action, &out.Action, c); err != nil {
return err
}
out.User = in.User
if in.Groups != nil {
in, out := in.Groups, &out.Groups
*out = make(sets.String)
for key, val := range in {
if newVal, err := c.DeepCopy(val); err != nil {
return err
} else {
(*out)[key] = newVal.(sets.Empty)
}
}
} else {
out.Groups = nil
}
if in.Scopes != nil {
in, out := in.Scopes, &out.Scopes
*out = make([]string, len(in))
copy(*out, in)
} else {
out.Scopes = nil
}
return nil
}
func DeepCopy_api_Policy(in Policy, out *Policy, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
if err := api.DeepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil {
return err
}
if err := unversioned.DeepCopy_unversioned_Time(in.LastModified, &out.LastModified, c); err != nil {
return err
}
if in.Roles != nil {
in, out := in.Roles, &out.Roles
*out = make(map[string]*Role)
for key, val := range in {
if newVal, err := c.DeepCopy(val); err != nil {
return err
} else {
(*out)[key] = newVal.(*Role)
}
}
} else {
out.Roles = nil
}
return nil
}
func DeepCopy_api_PolicyBinding(in PolicyBinding, out *PolicyBinding, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
if err := api.DeepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil {
return err
}
if err := unversioned.DeepCopy_unversioned_Time(in.LastModified, &out.LastModified, c); err != nil {
return err
}
if err := api.DeepCopy_api_ObjectReference(in.PolicyRef, &out.PolicyRef, c); err != nil {
return err
}
if in.RoleBindings != nil {
in, out := in.RoleBindings, &out.RoleBindings
*out = make(map[string]*RoleBinding)
for key, val := range in {
if newVal, err := c.DeepCopy(val); err != nil {
return err
} else {
(*out)[key] = newVal.(*RoleBinding)
}
}
} else {
out.RoleBindings = nil
}
return nil
}
func DeepCopy_api_PolicyBindingList(in PolicyBindingList, out *PolicyBindingList, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
if err := unversioned.DeepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil {
return err
}
if in.Items != nil {
in, out := in.Items, &out.Items
*out = make([]PolicyBinding, len(in))
for i := range in {
if err := DeepCopy_api_PolicyBinding(in[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Items = nil
}
return nil
}
func DeepCopy_api_PolicyList(in PolicyList, out *PolicyList, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
if err := unversioned.DeepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil {
return err
}
if in.Items != nil {
in, out := in.Items, &out.Items
*out = make([]Policy, len(in))
for i := range in {
if err := DeepCopy_api_Policy(in[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Items = nil
}
return nil
}
func DeepCopy_api_PolicyRule(in PolicyRule, out *PolicyRule, c *conversion.Cloner) error {
if in.Verbs != nil {
in, out := in.Verbs, &out.Verbs
*out = make(sets.String)
for key, val := range in {
if newVal, err := c.DeepCopy(val); err != nil {
return err
} else {
(*out)[key] = newVal.(sets.Empty)
}
}
} else {
out.Verbs = nil
}
if in.AttributeRestrictions == nil {
out.AttributeRestrictions = nil
} else if newVal, err := c.DeepCopy(in.AttributeRestrictions); err != nil {
return err
} else {
out.AttributeRestrictions = newVal.(runtime.Object)
}
if in.APIGroups != nil {
in, out := in.APIGroups, &out.APIGroups
*out = make([]string, len(in))
copy(*out, in)
} else {
out.APIGroups = nil
}
if in.Resources != nil {
in, out := in.Resources, &out.Resources
*out = make(sets.String)
for key, val := range in {
if newVal, err := c.DeepCopy(val); err != nil {
return err
} else {
(*out)[key] = newVal.(sets.Empty)
}
}
} else {
out.Resources = nil
}
if in.ResourceNames != nil {
in, out := in.ResourceNames, &out.ResourceNames
*out = make(sets.String)
for key, val := range in {
if newVal, err := c.DeepCopy(val); err != nil {
return err
} else {
(*out)[key] = newVal.(sets.Empty)
}
}
} else {
out.ResourceNames = nil
}
if in.NonResourceURLs != nil {
in, out := in.NonResourceURLs, &out.NonResourceURLs
*out = make(sets.String)
for key, val := range in {
if newVal, err := c.DeepCopy(val); err != nil {
return err
} else {
(*out)[key] = newVal.(sets.Empty)
}
}
} else {
out.NonResourceURLs = nil
}
return nil
}
func DeepCopy_api_ResourceAccessReview(in ResourceAccessReview, out *ResourceAccessReview, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
if err := DeepCopy_api_AuthorizationAttributes(in.Action, &out.Action, c); err != nil {
return err
}
return nil
}
func DeepCopy_api_ResourceAccessReviewResponse(in ResourceAccessReviewResponse, out *ResourceAccessReviewResponse, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
out.Namespace = in.Namespace
if in.Users != nil {
in, out := in.Users, &out.Users
*out = make(sets.String)
for key, val := range in {
if newVal, err := c.DeepCopy(val); err != nil {
return err
} else {
(*out)[key] = newVal.(sets.Empty)
}
}
} else {
out.Users = nil
}
if in.Groups != nil {
in, out := in.Groups, &out.Groups
*out = make(sets.String)
for key, val := range in {
if newVal, err := c.DeepCopy(val); err != nil {
return err
} else {
(*out)[key] = newVal.(sets.Empty)
}
}
} else {
out.Groups = nil
}
out.EvaluationError = in.EvaluationError
return nil
}
func DeepCopy_api_Role(in Role, out *Role, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
if err := api.DeepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil {
return err
}
if in.Rules != nil {
in, out := in.Rules, &out.Rules
*out = make([]PolicyRule, len(in))
for i := range in {
if err := DeepCopy_api_PolicyRule(in[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Rules = nil
}
return nil
}
func DeepCopy_api_RoleBinding(in RoleBinding, out *RoleBinding, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
if err := api.DeepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil {
return err
}
if in.Subjects != nil {
in, out := in.Subjects, &out.Subjects
*out = make([]api.ObjectReference, len(in))
for i := range in {
if err := api.DeepCopy_api_ObjectReference(in[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Subjects = nil
}
if err := api.DeepCopy_api_ObjectReference(in.RoleRef, &out.RoleRef, c); err != nil {
return err
}
return nil
}
func DeepCopy_api_RoleBindingList(in RoleBindingList, out *RoleBindingList, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
if err := unversioned.DeepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil {
return err
}
if in.Items != nil {
in, out := in.Items, &out.Items
*out = make([]RoleBinding, len(in))
for i := range in {
if err := DeepCopy_api_RoleBinding(in[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Items = nil
}
return nil
}
func DeepCopy_api_RoleList(in RoleList, out *RoleList, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
if err := unversioned.DeepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil {
return err
}
if in.Items != nil {
in, out := in.Items, &out.Items
*out = make([]Role, len(in))
for i := range in {
if err := DeepCopy_api_Role(in[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Items = nil
}
return nil
}
func DeepCopy_api_SelfSubjectRulesReview(in SelfSubjectRulesReview, out *SelfSubjectRulesReview, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
if err := DeepCopy_api_SelfSubjectRulesReviewSpec(in.Spec, &out.Spec, c); err != nil {
return err
}
if err := DeepCopy_api_SubjectRulesReviewStatus(in.Status, &out.Status, c); err != nil {
return err
}
return nil
}
func DeepCopy_api_SelfSubjectRulesReviewSpec(in SelfSubjectRulesReviewSpec, out *SelfSubjectRulesReviewSpec, c *conversion.Cloner) error {
if in.Scopes != nil {
in, out := in.Scopes, &out.Scopes
*out = make([]string, len(in))
copy(*out, in)
} else {
out.Scopes = nil
}
return nil
}
func DeepCopy_api_SubjectAccessReview(in SubjectAccessReview, out *SubjectAccessReview, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
if err := DeepCopy_api_AuthorizationAttributes(in.Action, &out.Action, c); err != nil {
return err
}
out.User = in.User
if in.Groups != nil {
in, out := in.Groups, &out.Groups
*out = make(sets.String)
for key, val := range in {
if newVal, err := c.DeepCopy(val); err != nil {
return err
} else {
(*out)[key] = newVal.(sets.Empty)
}
}
} else {
out.Groups = nil
}
if in.Scopes != nil {
in, out := in.Scopes, &out.Scopes
*out = make([]string, len(in))
copy(*out, in)
} else {
out.Scopes = nil
}
return nil
}
func DeepCopy_api_SubjectAccessReviewResponse(in SubjectAccessReviewResponse, out *SubjectAccessReviewResponse, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
out.Namespace = in.Namespace
out.Allowed = in.Allowed
out.Reason = in.Reason
return nil
}
func DeepCopy_api_SubjectRulesReviewStatus(in SubjectRulesReviewStatus, out *SubjectRulesReviewStatus, c *conversion.Cloner) error {
if in.Rules != nil {
in, out := in.Rules, &out.Rules
*out = make([]PolicyRule, len(in))
for i := range in {
if err := DeepCopy_api_PolicyRule(in[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Rules = nil
}
out.EvaluationError = in.EvaluationError
return nil
}
+4
View File
@@ -0,0 +1,4 @@
// +k8s:deepcopy-gen=package,register
// Package api is the internal version of the API.
package api
+53
View File
@@ -244,6 +244,59 @@ func SubjectsStrings(currentNamespace string, subjects []kapi.ObjectReference) (
return users, groups, sas, others
}
// SubjectsContainUser returns true if the provided subjects contain the named user. currentNamespace
// is used to identify service accounts that are defined in a relative fashion.
func SubjectsContainUser(subjects []kapi.ObjectReference, currentNamespace string, user string) bool {
if !strings.HasPrefix(user, serviceaccount.ServiceAccountUsernamePrefix) {
for _, subject := range subjects {
switch subject.Kind {
case UserKind, SystemUserKind:
if user == subject.Name {
return true
}
}
}
return false
}
for _, subject := range subjects {
switch subject.Kind {
case ServiceAccountKind:
namespace := currentNamespace
if len(subject.Namespace) > 0 {
namespace = subject.Namespace
}
if len(namespace) == 0 {
continue
}
if user == serviceaccount.MakeUsername(namespace, subject.Name) {
return true
}
case UserKind, SystemUserKind:
if user == subject.Name {
return true
}
}
}
return false
}
// SubjectsContainAnyGroup returns true if the provided subjects any of the named groups.
func SubjectsContainAnyGroup(subjects []kapi.ObjectReference, groups []string) bool {
for _, subject := range subjects {
switch subject.Kind {
case GroupKind, SystemGroupKind:
for _, group := range groups {
if group == subject.Name {
return true
}
}
}
}
return false
}
func AddUserToSAR(user user.Info, sar *SubjectAccessReview) *SubjectAccessReview {
origScopes := user.GetExtra()[ScopesKey]
scopes := make([]string, len(origScopes), len(origScopes))
+7 -5
View File
@@ -6,6 +6,7 @@ import (
)
const GroupName = ""
const FutureGroupName = "authorization.openshift.io"
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}
@@ -20,13 +21,13 @@ func Resource(resource string) unversioned.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
func AddToScheme(scheme *runtime.Scheme) {
// Add the API to Scheme.
addKnownTypes(scheme)
}
var (
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
AddToScheme = SchemeBuilder.AddToScheme
)
// Adds the list of known types to api.Scheme.
func addKnownTypes(scheme *runtime.Scheme) {
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&Role{},
&RoleBinding{},
@@ -55,4 +56,5 @@ func addKnownTypes(scheme *runtime.Scheme) {
&ClusterRoleBindingList{},
&ClusterRoleList{},
)
return nil
}
+26 -10
View File
@@ -105,6 +105,8 @@ type RoleBinding struct {
RoleRef kapi.ObjectReference
}
type RolesByName map[string]*Role
// +genclient=true
// Policy is a object that holds all the Roles for a particular namespace. There is at most
@@ -117,9 +119,11 @@ type Policy struct {
LastModified unversioned.Time
// Roles holds all the Roles held by this Policy, mapped by Role.Name
Roles map[string]*Role
Roles RolesByName
}
type RoleBindingsByName map[string]*RoleBinding
// PolicyBinding is a object that holds all the RoleBindings for a particular namespace. There is
// one PolicyBinding document per referenced Policy namespace
type PolicyBinding struct {
@@ -133,7 +137,7 @@ type PolicyBinding struct {
// PolicyRef is a reference to the Policy that contains all the Roles that this PolicyBinding's RoleBindings may reference
PolicyRef kapi.ObjectReference
// RoleBindings holds all the RoleBindings held by this PolicyBinding, mapped by RoleBinding.Name
RoleBindings map[string]*RoleBinding
RoleBindings RoleBindingsByName
}
// SelfSubjectRulesReview is a resource you can create to determine which actions you can perform in a namespace
@@ -171,8 +175,10 @@ type ResourceAccessReviewResponse struct {
// Namespace is the namespace used for the access review
Namespace string
// Users is the list of users who can perform the action
// +k8s:conversion-gen=false
Users sets.String
// Groups is the list of groups who can perform the action
// +k8s:conversion-gen=false
Groups sets.String
// EvaluationError is an indication that some error occurred during resolution, but partial results can still be returned.
@@ -187,7 +193,7 @@ type ResourceAccessReview struct {
unversioned.TypeMeta
// Action describes the action being tested
Action AuthorizationAttributes
Action
}
// SubjectAccessReviewResponse describes whether or not a user or group can perform an action
@@ -200,6 +206,10 @@ type SubjectAccessReviewResponse struct {
Allowed bool
// Reason is optional. It indicates why a request was allowed or denied.
Reason string
// EvaluationError is an indication that some error occurred during the authorization check.
// It is entirely possible to get an error and be able to continue determine authorization status in spite of it. This is
// most common when a bound role is missing, but enough roles are still present and bound to reason about the request.
EvaluationError string
}
// SubjectAccessReview is an object for requesting information about whether a user or group can perform an action
@@ -207,10 +217,11 @@ type SubjectAccessReview struct {
unversioned.TypeMeta
// Action describes the action being tested
Action AuthorizationAttributes
Action
// User is optional. If both User and Groups are empty, the current authenticated user is used.
User string
// Groups is optional. Groups is the list of groups to which the User belongs.
// +k8s:conversion-gen=false
Groups sets.String
// Scopes to use for the evaluation. Empty means "use the unscoped (full) permissions of the user/groups".
// Nil for a self-SAR, means "use the scopes on this request".
@@ -223,7 +234,7 @@ type LocalResourceAccessReview struct {
unversioned.TypeMeta
// Action describes the action being tested
Action AuthorizationAttributes
Action
}
// LocalSubjectAccessReview is an object for requesting information about whether a user or group can perform an action in a particular namespace
@@ -231,10 +242,11 @@ type LocalSubjectAccessReview struct {
unversioned.TypeMeta
// Action describes the action being tested. The Namespace element is FORCED to the current namespace.
Action AuthorizationAttributes
Action
// User is optional. If both User and Groups are empty, the current authenticated user is used.
User string
// Groups is optional. Groups is the list of groups to which the User belongs.
// +k8s:conversion-gen=false
Groups sets.String
// Scopes to use for the evaluation. Empty means "use the unscoped (full) permissions of the user/groups".
// Nil for a self-SAR, means "use the scopes on this request".
@@ -242,8 +254,8 @@ type LocalSubjectAccessReview struct {
Scopes []string
}
// AuthorizationAttributes describes a request to be authorized
type AuthorizationAttributes struct {
// Action describes a request to be authorized
type Action struct {
// Namespace is the namespace of the action being requested. Currently, there is no distinction between no namespace and all namespaces
Namespace string
// Verb is one of: get, list, watch, create, update, delete
@@ -327,6 +339,8 @@ type ClusterRoleBinding struct {
RoleRef kapi.ObjectReference
}
type ClusterRolesByName map[string]*ClusterRole
// ClusterPolicy is a object that holds all the ClusterRoles for a particular namespace. There is at most
// one ClusterPolicy document per namespace.
type ClusterPolicy struct {
@@ -338,9 +352,11 @@ type ClusterPolicy struct {
LastModified unversioned.Time
// Roles holds all the ClusterRoles held by this ClusterPolicy, mapped by Role.Name
Roles map[string]*ClusterRole
Roles ClusterRolesByName
}
type ClusterRoleBindingsByName map[string]*ClusterRoleBinding
// ClusterPolicyBinding is a object that holds all the ClusterRoleBindings for a particular namespace. There is
// one ClusterPolicyBinding document per referenced ClusterPolicy namespace
type ClusterPolicyBinding struct {
@@ -354,7 +370,7 @@ type ClusterPolicyBinding struct {
// ClusterPolicyRef is a reference to the ClusterPolicy that contains all the ClusterRoles that this ClusterPolicyBinding's RoleBindings may reference
PolicyRef kapi.ObjectReference
// RoleBindings holds all the RoleBindings held by this ClusterPolicyBinding, mapped by RoleBinding.Name
RoleBindings map[string]*ClusterRoleBinding
RoleBindings ClusterRoleBindingsByName
}
// ClusterPolicyList is a collection of ClusterPolicies
@@ -0,0 +1,690 @@
// +build !ignore_autogenerated_openshift
// This file was autogenerated by deepcopy-gen. Do not edit it manually!
package api
import (
pkg_api "k8s.io/kubernetes/pkg/api"
conversion "k8s.io/kubernetes/pkg/conversion"
runtime "k8s.io/kubernetes/pkg/runtime"
sets "k8s.io/kubernetes/pkg/util/sets"
reflect "reflect"
)
func init() {
SchemeBuilder.Register(RegisterDeepCopies)
}
// RegisterDeepCopies adds deep-copy functions to the given scheme. Public
// to allow building arbitrary schemes.
func RegisterDeepCopies(scheme *runtime.Scheme) error {
return scheme.AddGeneratedDeepCopyFuncs(
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_Action, InType: reflect.TypeOf(&Action{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_ClusterPolicy, InType: reflect.TypeOf(&ClusterPolicy{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_ClusterPolicyBinding, InType: reflect.TypeOf(&ClusterPolicyBinding{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_ClusterPolicyBindingList, InType: reflect.TypeOf(&ClusterPolicyBindingList{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_ClusterPolicyList, InType: reflect.TypeOf(&ClusterPolicyList{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_ClusterRole, InType: reflect.TypeOf(&ClusterRole{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_ClusterRoleBinding, InType: reflect.TypeOf(&ClusterRoleBinding{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_ClusterRoleBindingList, InType: reflect.TypeOf(&ClusterRoleBindingList{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_ClusterRoleList, InType: reflect.TypeOf(&ClusterRoleList{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_IsPersonalSubjectAccessReview, InType: reflect.TypeOf(&IsPersonalSubjectAccessReview{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_LocalResourceAccessReview, InType: reflect.TypeOf(&LocalResourceAccessReview{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_LocalSubjectAccessReview, InType: reflect.TypeOf(&LocalSubjectAccessReview{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_Policy, InType: reflect.TypeOf(&Policy{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_PolicyBinding, InType: reflect.TypeOf(&PolicyBinding{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_PolicyBindingList, InType: reflect.TypeOf(&PolicyBindingList{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_PolicyList, InType: reflect.TypeOf(&PolicyList{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_PolicyRule, InType: reflect.TypeOf(&PolicyRule{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_PolicyRuleBuilder, InType: reflect.TypeOf(&PolicyRuleBuilder{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_ResourceAccessReview, InType: reflect.TypeOf(&ResourceAccessReview{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_ResourceAccessReviewResponse, InType: reflect.TypeOf(&ResourceAccessReviewResponse{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_Role, InType: reflect.TypeOf(&Role{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_RoleBinding, InType: reflect.TypeOf(&RoleBinding{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_RoleBindingList, InType: reflect.TypeOf(&RoleBindingList{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_RoleList, InType: reflect.TypeOf(&RoleList{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_SelfSubjectRulesReview, InType: reflect.TypeOf(&SelfSubjectRulesReview{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_SelfSubjectRulesReviewSpec, InType: reflect.TypeOf(&SelfSubjectRulesReviewSpec{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_SubjectAccessReview, InType: reflect.TypeOf(&SubjectAccessReview{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_SubjectAccessReviewResponse, InType: reflect.TypeOf(&SubjectAccessReviewResponse{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_SubjectRulesReviewStatus, InType: reflect.TypeOf(&SubjectRulesReviewStatus{})},
)
}
func DeepCopy_api_Action(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*Action)
out := out.(*Action)
out.Namespace = in.Namespace
out.Verb = in.Verb
out.Group = in.Group
out.Version = in.Version
out.Resource = in.Resource
out.ResourceName = in.ResourceName
if in.Content == nil {
out.Content = nil
} else if newVal, err := c.DeepCopy(&in.Content); err != nil {
return err
} else {
out.Content = *newVal.(*runtime.Object)
}
return nil
}
}
func DeepCopy_api_ClusterPolicy(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*ClusterPolicy)
out := out.(*ClusterPolicy)
out.TypeMeta = in.TypeMeta
if err := pkg_api.DeepCopy_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, c); err != nil {
return err
}
out.LastModified = in.LastModified.DeepCopy()
if in.Roles != nil {
in, out := &in.Roles, &out.Roles
*out = make(ClusterRolesByName)
for key, val := range *in {
if newVal, err := c.DeepCopy(&val); err != nil {
return err
} else {
(*out)[key] = *newVal.(**ClusterRole)
}
}
} else {
out.Roles = nil
}
return nil
}
}
func DeepCopy_api_ClusterPolicyBinding(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*ClusterPolicyBinding)
out := out.(*ClusterPolicyBinding)
out.TypeMeta = in.TypeMeta
if err := pkg_api.DeepCopy_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, c); err != nil {
return err
}
out.LastModified = in.LastModified.DeepCopy()
out.PolicyRef = in.PolicyRef
if in.RoleBindings != nil {
in, out := &in.RoleBindings, &out.RoleBindings
*out = make(ClusterRoleBindingsByName)
for key, val := range *in {
if newVal, err := c.DeepCopy(&val); err != nil {
return err
} else {
(*out)[key] = *newVal.(**ClusterRoleBinding)
}
}
} else {
out.RoleBindings = nil
}
return nil
}
}
func DeepCopy_api_ClusterPolicyBindingList(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*ClusterPolicyBindingList)
out := out.(*ClusterPolicyBindingList)
out.TypeMeta = in.TypeMeta
out.ListMeta = in.ListMeta
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]ClusterPolicyBinding, len(*in))
for i := range *in {
if err := DeepCopy_api_ClusterPolicyBinding(&(*in)[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Items = nil
}
return nil
}
}
func DeepCopy_api_ClusterPolicyList(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*ClusterPolicyList)
out := out.(*ClusterPolicyList)
out.TypeMeta = in.TypeMeta
out.ListMeta = in.ListMeta
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]ClusterPolicy, len(*in))
for i := range *in {
if err := DeepCopy_api_ClusterPolicy(&(*in)[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Items = nil
}
return nil
}
}
func DeepCopy_api_ClusterRole(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*ClusterRole)
out := out.(*ClusterRole)
out.TypeMeta = in.TypeMeta
if err := pkg_api.DeepCopy_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, c); err != nil {
return err
}
if in.Rules != nil {
in, out := &in.Rules, &out.Rules
*out = make([]PolicyRule, len(*in))
for i := range *in {
if err := DeepCopy_api_PolicyRule(&(*in)[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Rules = nil
}
return nil
}
}
func DeepCopy_api_ClusterRoleBinding(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*ClusterRoleBinding)
out := out.(*ClusterRoleBinding)
out.TypeMeta = in.TypeMeta
if err := pkg_api.DeepCopy_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, c); err != nil {
return err
}
if in.Subjects != nil {
in, out := &in.Subjects, &out.Subjects
*out = make([]pkg_api.ObjectReference, len(*in))
for i := range *in {
(*out)[i] = (*in)[i]
}
} else {
out.Subjects = nil
}
out.RoleRef = in.RoleRef
return nil
}
}
func DeepCopy_api_ClusterRoleBindingList(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*ClusterRoleBindingList)
out := out.(*ClusterRoleBindingList)
out.TypeMeta = in.TypeMeta
out.ListMeta = in.ListMeta
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]ClusterRoleBinding, len(*in))
for i := range *in {
if err := DeepCopy_api_ClusterRoleBinding(&(*in)[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Items = nil
}
return nil
}
}
func DeepCopy_api_ClusterRoleList(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*ClusterRoleList)
out := out.(*ClusterRoleList)
out.TypeMeta = in.TypeMeta
out.ListMeta = in.ListMeta
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]ClusterRole, len(*in))
for i := range *in {
if err := DeepCopy_api_ClusterRole(&(*in)[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Items = nil
}
return nil
}
}
func DeepCopy_api_IsPersonalSubjectAccessReview(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*IsPersonalSubjectAccessReview)
out := out.(*IsPersonalSubjectAccessReview)
out.TypeMeta = in.TypeMeta
return nil
}
}
func DeepCopy_api_LocalResourceAccessReview(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*LocalResourceAccessReview)
out := out.(*LocalResourceAccessReview)
out.TypeMeta = in.TypeMeta
if err := DeepCopy_api_Action(&in.Action, &out.Action, c); err != nil {
return err
}
return nil
}
}
func DeepCopy_api_LocalSubjectAccessReview(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*LocalSubjectAccessReview)
out := out.(*LocalSubjectAccessReview)
out.TypeMeta = in.TypeMeta
if err := DeepCopy_api_Action(&in.Action, &out.Action, c); err != nil {
return err
}
out.User = in.User
if in.Groups != nil {
in, out := &in.Groups, &out.Groups
*out = make(sets.String)
for key, val := range *in {
(*out)[key] = val
}
} else {
out.Groups = nil
}
if in.Scopes != nil {
in, out := &in.Scopes, &out.Scopes
*out = make([]string, len(*in))
copy(*out, *in)
} else {
out.Scopes = nil
}
return nil
}
}
func DeepCopy_api_Policy(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*Policy)
out := out.(*Policy)
out.TypeMeta = in.TypeMeta
if err := pkg_api.DeepCopy_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, c); err != nil {
return err
}
out.LastModified = in.LastModified.DeepCopy()
if in.Roles != nil {
in, out := &in.Roles, &out.Roles
*out = make(RolesByName)
for key, val := range *in {
if newVal, err := c.DeepCopy(&val); err != nil {
return err
} else {
(*out)[key] = *newVal.(**Role)
}
}
} else {
out.Roles = nil
}
return nil
}
}
func DeepCopy_api_PolicyBinding(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*PolicyBinding)
out := out.(*PolicyBinding)
out.TypeMeta = in.TypeMeta
if err := pkg_api.DeepCopy_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, c); err != nil {
return err
}
out.LastModified = in.LastModified.DeepCopy()
out.PolicyRef = in.PolicyRef
if in.RoleBindings != nil {
in, out := &in.RoleBindings, &out.RoleBindings
*out = make(RoleBindingsByName)
for key, val := range *in {
if newVal, err := c.DeepCopy(&val); err != nil {
return err
} else {
(*out)[key] = *newVal.(**RoleBinding)
}
}
} else {
out.RoleBindings = nil
}
return nil
}
}
func DeepCopy_api_PolicyBindingList(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*PolicyBindingList)
out := out.(*PolicyBindingList)
out.TypeMeta = in.TypeMeta
out.ListMeta = in.ListMeta
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]PolicyBinding, len(*in))
for i := range *in {
if err := DeepCopy_api_PolicyBinding(&(*in)[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Items = nil
}
return nil
}
}
func DeepCopy_api_PolicyList(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*PolicyList)
out := out.(*PolicyList)
out.TypeMeta = in.TypeMeta
out.ListMeta = in.ListMeta
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Policy, len(*in))
for i := range *in {
if err := DeepCopy_api_Policy(&(*in)[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Items = nil
}
return nil
}
}
func DeepCopy_api_PolicyRule(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*PolicyRule)
out := out.(*PolicyRule)
if in.Verbs != nil {
in, out := &in.Verbs, &out.Verbs
*out = make(sets.String)
for key, val := range *in {
(*out)[key] = val
}
} else {
out.Verbs = nil
}
if in.AttributeRestrictions == nil {
out.AttributeRestrictions = nil
} else if newVal, err := c.DeepCopy(&in.AttributeRestrictions); err != nil {
return err
} else {
out.AttributeRestrictions = *newVal.(*runtime.Object)
}
if in.APIGroups != nil {
in, out := &in.APIGroups, &out.APIGroups
*out = make([]string, len(*in))
copy(*out, *in)
} else {
out.APIGroups = nil
}
if in.Resources != nil {
in, out := &in.Resources, &out.Resources
*out = make(sets.String)
for key, val := range *in {
(*out)[key] = val
}
} else {
out.Resources = nil
}
if in.ResourceNames != nil {
in, out := &in.ResourceNames, &out.ResourceNames
*out = make(sets.String)
for key, val := range *in {
(*out)[key] = val
}
} else {
out.ResourceNames = nil
}
if in.NonResourceURLs != nil {
in, out := &in.NonResourceURLs, &out.NonResourceURLs
*out = make(sets.String)
for key, val := range *in {
(*out)[key] = val
}
} else {
out.NonResourceURLs = nil
}
return nil
}
}
func DeepCopy_api_PolicyRuleBuilder(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*PolicyRuleBuilder)
out := out.(*PolicyRuleBuilder)
if err := DeepCopy_api_PolicyRule(&in.PolicyRule, &out.PolicyRule, c); err != nil {
return err
}
return nil
}
}
func DeepCopy_api_ResourceAccessReview(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*ResourceAccessReview)
out := out.(*ResourceAccessReview)
out.TypeMeta = in.TypeMeta
if err := DeepCopy_api_Action(&in.Action, &out.Action, c); err != nil {
return err
}
return nil
}
}
func DeepCopy_api_ResourceAccessReviewResponse(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*ResourceAccessReviewResponse)
out := out.(*ResourceAccessReviewResponse)
out.TypeMeta = in.TypeMeta
out.Namespace = in.Namespace
if in.Users != nil {
in, out := &in.Users, &out.Users
*out = make(sets.String)
for key, val := range *in {
(*out)[key] = val
}
} else {
out.Users = nil
}
if in.Groups != nil {
in, out := &in.Groups, &out.Groups
*out = make(sets.String)
for key, val := range *in {
(*out)[key] = val
}
} else {
out.Groups = nil
}
out.EvaluationError = in.EvaluationError
return nil
}
}
func DeepCopy_api_Role(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*Role)
out := out.(*Role)
out.TypeMeta = in.TypeMeta
if err := pkg_api.DeepCopy_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, c); err != nil {
return err
}
if in.Rules != nil {
in, out := &in.Rules, &out.Rules
*out = make([]PolicyRule, len(*in))
for i := range *in {
if err := DeepCopy_api_PolicyRule(&(*in)[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Rules = nil
}
return nil
}
}
func DeepCopy_api_RoleBinding(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*RoleBinding)
out := out.(*RoleBinding)
out.TypeMeta = in.TypeMeta
if err := pkg_api.DeepCopy_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, c); err != nil {
return err
}
if in.Subjects != nil {
in, out := &in.Subjects, &out.Subjects
*out = make([]pkg_api.ObjectReference, len(*in))
for i := range *in {
(*out)[i] = (*in)[i]
}
} else {
out.Subjects = nil
}
out.RoleRef = in.RoleRef
return nil
}
}
func DeepCopy_api_RoleBindingList(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*RoleBindingList)
out := out.(*RoleBindingList)
out.TypeMeta = in.TypeMeta
out.ListMeta = in.ListMeta
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]RoleBinding, len(*in))
for i := range *in {
if err := DeepCopy_api_RoleBinding(&(*in)[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Items = nil
}
return nil
}
}
func DeepCopy_api_RoleList(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*RoleList)
out := out.(*RoleList)
out.TypeMeta = in.TypeMeta
out.ListMeta = in.ListMeta
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Role, len(*in))
for i := range *in {
if err := DeepCopy_api_Role(&(*in)[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Items = nil
}
return nil
}
}
func DeepCopy_api_SelfSubjectRulesReview(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*SelfSubjectRulesReview)
out := out.(*SelfSubjectRulesReview)
out.TypeMeta = in.TypeMeta
if err := DeepCopy_api_SelfSubjectRulesReviewSpec(&in.Spec, &out.Spec, c); err != nil {
return err
}
if err := DeepCopy_api_SubjectRulesReviewStatus(&in.Status, &out.Status, c); err != nil {
return err
}
return nil
}
}
func DeepCopy_api_SelfSubjectRulesReviewSpec(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*SelfSubjectRulesReviewSpec)
out := out.(*SelfSubjectRulesReviewSpec)
if in.Scopes != nil {
in, out := &in.Scopes, &out.Scopes
*out = make([]string, len(*in))
copy(*out, *in)
} else {
out.Scopes = nil
}
return nil
}
}
func DeepCopy_api_SubjectAccessReview(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*SubjectAccessReview)
out := out.(*SubjectAccessReview)
out.TypeMeta = in.TypeMeta
if err := DeepCopy_api_Action(&in.Action, &out.Action, c); err != nil {
return err
}
out.User = in.User
if in.Groups != nil {
in, out := &in.Groups, &out.Groups
*out = make(sets.String)
for key, val := range *in {
(*out)[key] = val
}
} else {
out.Groups = nil
}
if in.Scopes != nil {
in, out := &in.Scopes, &out.Scopes
*out = make([]string, len(*in))
copy(*out, *in)
} else {
out.Scopes = nil
}
return nil
}
}
func DeepCopy_api_SubjectAccessReviewResponse(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*SubjectAccessReviewResponse)
out := out.(*SubjectAccessReviewResponse)
out.TypeMeta = in.TypeMeta
out.Namespace = in.Namespace
out.Allowed = in.Allowed
out.Reason = in.Reason
out.EvaluationError = in.EvaluationError
return nil
}
}
func DeepCopy_api_SubjectRulesReviewStatus(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*SubjectRulesReviewStatus)
out := out.(*SubjectRulesReviewStatus)
if in.Rules != nil {
in, out := &in.Rules, &out.Rules
*out = make([]PolicyRule, len(*in))
for i := range *in {
if err := DeepCopy_api_PolicyRule(&(*in)[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Rules = nil
}
out.EvaluationError = in.EvaluationError
return nil
}
}
@@ -0,0 +1,60 @@
package reaper
import (
"time"
"github.com/golang/glog"
kapi "k8s.io/kubernetes/pkg/api"
kerrors "k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/kubectl"
"github.com/openshift/origin/pkg/client"
)
func NewClusterRoleReaper(roleClient client.ClusterRolesInterface, clusterBindingClient client.ClusterRoleBindingsInterface, bindingClient client.RoleBindingsNamespacer) kubectl.Reaper {
return &ClusterRoleReaper{
roleClient: roleClient,
clusterBindingClient: clusterBindingClient,
bindingClient: bindingClient,
}
}
type ClusterRoleReaper struct {
roleClient client.ClusterRolesInterface
clusterBindingClient client.ClusterRoleBindingsInterface
bindingClient client.RoleBindingsNamespacer
}
// Stop on a reaper is actually used for deletion. In this case, we'll delete referencing clusterroleclusterBindings
// then delete the clusterrole.
func (r *ClusterRoleReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *kapi.DeleteOptions) error {
clusterBindings, err := r.clusterBindingClient.ClusterRoleBindings().List(kapi.ListOptions{})
if err != nil {
return err
}
for _, clusterBinding := range clusterBindings.Items {
if clusterBinding.RoleRef.Name == name {
if err := r.clusterBindingClient.ClusterRoleBindings().Delete(clusterBinding.Name); err != nil && !kerrors.IsNotFound(err) {
glog.Infof("Cannot delete clusterrolebinding/%s: %v", clusterBinding.Name, err)
}
}
}
namespacedBindings, err := r.bindingClient.RoleBindings(kapi.NamespaceNone).List(kapi.ListOptions{})
if err != nil {
return err
}
for _, namespacedBinding := range namespacedBindings.Items {
if namespacedBinding.RoleRef.Namespace == kapi.NamespaceNone && namespacedBinding.RoleRef.Name == name {
if err := r.bindingClient.RoleBindings(namespacedBinding.Namespace).Delete(namespacedBinding.Name); err != nil && !kerrors.IsNotFound(err) {
glog.Infof("Cannot delete rolebinding/%s in %s: %v", namespacedBinding.Name, namespacedBinding.Namespace, err)
}
}
}
if err := r.roleClient.ClusterRoles().Delete(name); err != nil && !kerrors.IsNotFound(err) {
return err
}
return nil
}
+47
View File
@@ -0,0 +1,47 @@
package reaper
import (
"time"
"github.com/golang/glog"
kapi "k8s.io/kubernetes/pkg/api"
kerrors "k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/kubectl"
"github.com/openshift/origin/pkg/client"
)
func NewRoleReaper(roleClient client.RolesNamespacer, bindingClient client.RoleBindingsNamespacer) kubectl.Reaper {
return &RoleReaper{
roleClient: roleClient,
bindingClient: bindingClient,
}
}
type RoleReaper struct {
roleClient client.RolesNamespacer
bindingClient client.RoleBindingsNamespacer
}
// Stop on a reaper is actually used for deletion. In this case, we'll delete referencing rolebindings
// then delete the role.
func (r *RoleReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *kapi.DeleteOptions) error {
bindings, err := r.bindingClient.RoleBindings(namespace).List(kapi.ListOptions{})
if err != nil {
return err
}
for _, binding := range bindings.Items {
if binding.RoleRef.Namespace == namespace && binding.RoleRef.Name == name {
if err := r.bindingClient.RoleBindings(namespace).Delete(binding.Name); err != nil && !kerrors.IsNotFound(err) {
glog.Infof("Cannot delete rolebinding/%s: %v", binding.Name, err)
}
}
}
if err := r.roleClient.Roles(namespace).Delete(name); err != nil && !kerrors.IsNotFound(err) {
return err
}
return nil
}
-937
View File
@@ -1,937 +0,0 @@
// +build !ignore_autogenerated_openshift
// This file was autogenerated by deepcopy-gen. Do not edit it manually!
package api
import (
api "k8s.io/kubernetes/pkg/api"
unversioned "k8s.io/kubernetes/pkg/api/unversioned"
conversion "k8s.io/kubernetes/pkg/conversion"
)
func init() {
if err := api.Scheme.AddGeneratedDeepCopyFuncs(
DeepCopy_api_BinaryBuildRequestOptions,
DeepCopy_api_BinaryBuildSource,
DeepCopy_api_Build,
DeepCopy_api_BuildConfig,
DeepCopy_api_BuildConfigList,
DeepCopy_api_BuildConfigSpec,
DeepCopy_api_BuildConfigStatus,
DeepCopy_api_BuildList,
DeepCopy_api_BuildLog,
DeepCopy_api_BuildLogOptions,
DeepCopy_api_BuildOutput,
DeepCopy_api_BuildPostCommitSpec,
DeepCopy_api_BuildRequest,
DeepCopy_api_BuildSource,
DeepCopy_api_BuildSpec,
DeepCopy_api_BuildStatus,
DeepCopy_api_BuildStrategy,
DeepCopy_api_BuildTriggerCause,
DeepCopy_api_BuildTriggerPolicy,
DeepCopy_api_CommonSpec,
DeepCopy_api_CustomBuildStrategy,
DeepCopy_api_DockerBuildStrategy,
DeepCopy_api_GenericWebHookCause,
DeepCopy_api_GenericWebHookEvent,
DeepCopy_api_GitBuildSource,
DeepCopy_api_GitHubWebHookCause,
DeepCopy_api_GitInfo,
DeepCopy_api_GitRefInfo,
DeepCopy_api_GitSourceRevision,
DeepCopy_api_ImageChangeCause,
DeepCopy_api_ImageChangeTrigger,
DeepCopy_api_ImageSource,
DeepCopy_api_ImageSourcePath,
DeepCopy_api_JenkinsPipelineBuildStrategy,
DeepCopy_api_SecretBuildSource,
DeepCopy_api_SecretSpec,
DeepCopy_api_SourceBuildStrategy,
DeepCopy_api_SourceControlUser,
DeepCopy_api_SourceRevision,
DeepCopy_api_WebHookTrigger,
); err != nil {
// if one of the deep copy functions is malformed, detect it immediately.
panic(err)
}
}
func DeepCopy_api_BinaryBuildRequestOptions(in BinaryBuildRequestOptions, out *BinaryBuildRequestOptions, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
if err := api.DeepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil {
return err
}
out.AsFile = in.AsFile
out.Commit = in.Commit
out.Message = in.Message
out.AuthorName = in.AuthorName
out.AuthorEmail = in.AuthorEmail
out.CommitterName = in.CommitterName
out.CommitterEmail = in.CommitterEmail
return nil
}
func DeepCopy_api_BinaryBuildSource(in BinaryBuildSource, out *BinaryBuildSource, c *conversion.Cloner) error {
out.AsFile = in.AsFile
return nil
}
func DeepCopy_api_Build(in Build, out *Build, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
if err := api.DeepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil {
return err
}
if err := DeepCopy_api_BuildSpec(in.Spec, &out.Spec, c); err != nil {
return err
}
if err := DeepCopy_api_BuildStatus(in.Status, &out.Status, c); err != nil {
return err
}
return nil
}
func DeepCopy_api_BuildConfig(in BuildConfig, out *BuildConfig, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
if err := api.DeepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil {
return err
}
if err := DeepCopy_api_BuildConfigSpec(in.Spec, &out.Spec, c); err != nil {
return err
}
if err := DeepCopy_api_BuildConfigStatus(in.Status, &out.Status, c); err != nil {
return err
}
return nil
}
func DeepCopy_api_BuildConfigList(in BuildConfigList, out *BuildConfigList, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
if err := unversioned.DeepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil {
return err
}
if in.Items != nil {
in, out := in.Items, &out.Items
*out = make([]BuildConfig, len(in))
for i := range in {
if err := DeepCopy_api_BuildConfig(in[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Items = nil
}
return nil
}
func DeepCopy_api_BuildConfigSpec(in BuildConfigSpec, out *BuildConfigSpec, c *conversion.Cloner) error {
if in.Triggers != nil {
in, out := in.Triggers, &out.Triggers
*out = make([]BuildTriggerPolicy, len(in))
for i := range in {
if err := DeepCopy_api_BuildTriggerPolicy(in[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Triggers = nil
}
out.RunPolicy = in.RunPolicy
if err := DeepCopy_api_CommonSpec(in.CommonSpec, &out.CommonSpec, c); err != nil {
return err
}
return nil
}
func DeepCopy_api_BuildConfigStatus(in BuildConfigStatus, out *BuildConfigStatus, c *conversion.Cloner) error {
out.LastVersion = in.LastVersion
return nil
}
func DeepCopy_api_BuildList(in BuildList, out *BuildList, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
if err := unversioned.DeepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil {
return err
}
if in.Items != nil {
in, out := in.Items, &out.Items
*out = make([]Build, len(in))
for i := range in {
if err := DeepCopy_api_Build(in[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Items = nil
}
return nil
}
func DeepCopy_api_BuildLog(in BuildLog, out *BuildLog, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
return nil
}
func DeepCopy_api_BuildLogOptions(in BuildLogOptions, out *BuildLogOptions, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
out.Container = in.Container
out.Follow = in.Follow
out.Previous = in.Previous
if in.SinceSeconds != nil {
in, out := in.SinceSeconds, &out.SinceSeconds
*out = new(int64)
**out = *in
} else {
out.SinceSeconds = nil
}
if in.SinceTime != nil {
in, out := in.SinceTime, &out.SinceTime
*out = new(unversioned.Time)
if err := unversioned.DeepCopy_unversioned_Time(*in, *out, c); err != nil {
return err
}
} else {
out.SinceTime = nil
}
out.Timestamps = in.Timestamps
if in.TailLines != nil {
in, out := in.TailLines, &out.TailLines
*out = new(int64)
**out = *in
} else {
out.TailLines = nil
}
if in.LimitBytes != nil {
in, out := in.LimitBytes, &out.LimitBytes
*out = new(int64)
**out = *in
} else {
out.LimitBytes = nil
}
out.NoWait = in.NoWait
if in.Version != nil {
in, out := in.Version, &out.Version
*out = new(int64)
**out = *in
} else {
out.Version = nil
}
return nil
}
func DeepCopy_api_BuildOutput(in BuildOutput, out *BuildOutput, c *conversion.Cloner) error {
if in.To != nil {
in, out := in.To, &out.To
*out = new(api.ObjectReference)
if err := api.DeepCopy_api_ObjectReference(*in, *out, c); err != nil {
return err
}
} else {
out.To = nil
}
if in.PushSecret != nil {
in, out := in.PushSecret, &out.PushSecret
*out = new(api.LocalObjectReference)
if err := api.DeepCopy_api_LocalObjectReference(*in, *out, c); err != nil {
return err
}
} else {
out.PushSecret = nil
}
return nil
}
func DeepCopy_api_BuildPostCommitSpec(in BuildPostCommitSpec, out *BuildPostCommitSpec, c *conversion.Cloner) error {
if in.Command != nil {
in, out := in.Command, &out.Command
*out = make([]string, len(in))
copy(*out, in)
} else {
out.Command = nil
}
if in.Args != nil {
in, out := in.Args, &out.Args
*out = make([]string, len(in))
copy(*out, in)
} else {
out.Args = nil
}
out.Script = in.Script
return nil
}
func DeepCopy_api_BuildRequest(in BuildRequest, out *BuildRequest, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
if err := api.DeepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil {
return err
}
if in.Revision != nil {
in, out := in.Revision, &out.Revision
*out = new(SourceRevision)
if err := DeepCopy_api_SourceRevision(*in, *out, c); err != nil {
return err
}
} else {
out.Revision = nil
}
if in.TriggeredByImage != nil {
in, out := in.TriggeredByImage, &out.TriggeredByImage
*out = new(api.ObjectReference)
if err := api.DeepCopy_api_ObjectReference(*in, *out, c); err != nil {
return err
}
} else {
out.TriggeredByImage = nil
}
if in.From != nil {
in, out := in.From, &out.From
*out = new(api.ObjectReference)
if err := api.DeepCopy_api_ObjectReference(*in, *out, c); err != nil {
return err
}
} else {
out.From = nil
}
if in.Binary != nil {
in, out := in.Binary, &out.Binary
*out = new(BinaryBuildSource)
if err := DeepCopy_api_BinaryBuildSource(*in, *out, c); err != nil {
return err
}
} else {
out.Binary = nil
}
if in.LastVersion != nil {
in, out := in.LastVersion, &out.LastVersion
*out = new(int64)
**out = *in
} else {
out.LastVersion = nil
}
if in.Env != nil {
in, out := in.Env, &out.Env
*out = make([]api.EnvVar, len(in))
for i := range in {
if err := api.DeepCopy_api_EnvVar(in[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Env = nil
}
if in.TriggeredBy != nil {
in, out := in.TriggeredBy, &out.TriggeredBy
*out = make([]BuildTriggerCause, len(in))
for i := range in {
if err := DeepCopy_api_BuildTriggerCause(in[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.TriggeredBy = nil
}
return nil
}
func DeepCopy_api_BuildSource(in BuildSource, out *BuildSource, c *conversion.Cloner) error {
if in.Binary != nil {
in, out := in.Binary, &out.Binary
*out = new(BinaryBuildSource)
if err := DeepCopy_api_BinaryBuildSource(*in, *out, c); err != nil {
return err
}
} else {
out.Binary = nil
}
if in.Dockerfile != nil {
in, out := in.Dockerfile, &out.Dockerfile
*out = new(string)
**out = *in
} else {
out.Dockerfile = nil
}
if in.Git != nil {
in, out := in.Git, &out.Git
*out = new(GitBuildSource)
if err := DeepCopy_api_GitBuildSource(*in, *out, c); err != nil {
return err
}
} else {
out.Git = nil
}
if in.Images != nil {
in, out := in.Images, &out.Images
*out = make([]ImageSource, len(in))
for i := range in {
if err := DeepCopy_api_ImageSource(in[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Images = nil
}
out.ContextDir = in.ContextDir
if in.SourceSecret != nil {
in, out := in.SourceSecret, &out.SourceSecret
*out = new(api.LocalObjectReference)
if err := api.DeepCopy_api_LocalObjectReference(*in, *out, c); err != nil {
return err
}
} else {
out.SourceSecret = nil
}
if in.Secrets != nil {
in, out := in.Secrets, &out.Secrets
*out = make([]SecretBuildSource, len(in))
for i := range in {
if err := DeepCopy_api_SecretBuildSource(in[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Secrets = nil
}
return nil
}
func DeepCopy_api_BuildSpec(in BuildSpec, out *BuildSpec, c *conversion.Cloner) error {
if err := DeepCopy_api_CommonSpec(in.CommonSpec, &out.CommonSpec, c); err != nil {
return err
}
if in.TriggeredBy != nil {
in, out := in.TriggeredBy, &out.TriggeredBy
*out = make([]BuildTriggerCause, len(in))
for i := range in {
if err := DeepCopy_api_BuildTriggerCause(in[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.TriggeredBy = nil
}
return nil
}
func DeepCopy_api_BuildStatus(in BuildStatus, out *BuildStatus, c *conversion.Cloner) error {
out.Phase = in.Phase
out.Cancelled = in.Cancelled
out.Reason = in.Reason
out.Message = in.Message
if in.StartTimestamp != nil {
in, out := in.StartTimestamp, &out.StartTimestamp
*out = new(unversioned.Time)
if err := unversioned.DeepCopy_unversioned_Time(*in, *out, c); err != nil {
return err
}
} else {
out.StartTimestamp = nil
}
if in.CompletionTimestamp != nil {
in, out := in.CompletionTimestamp, &out.CompletionTimestamp
*out = new(unversioned.Time)
if err := unversioned.DeepCopy_unversioned_Time(*in, *out, c); err != nil {
return err
}
} else {
out.CompletionTimestamp = nil
}
out.Duration = in.Duration
out.OutputDockerImageReference = in.OutputDockerImageReference
if in.Config != nil {
in, out := in.Config, &out.Config
*out = new(api.ObjectReference)
if err := api.DeepCopy_api_ObjectReference(*in, *out, c); err != nil {
return err
}
} else {
out.Config = nil
}
return nil
}
func DeepCopy_api_BuildStrategy(in BuildStrategy, out *BuildStrategy, c *conversion.Cloner) error {
if in.DockerStrategy != nil {
in, out := in.DockerStrategy, &out.DockerStrategy
*out = new(DockerBuildStrategy)
if err := DeepCopy_api_DockerBuildStrategy(*in, *out, c); err != nil {
return err
}
} else {
out.DockerStrategy = nil
}
if in.SourceStrategy != nil {
in, out := in.SourceStrategy, &out.SourceStrategy
*out = new(SourceBuildStrategy)
if err := DeepCopy_api_SourceBuildStrategy(*in, *out, c); err != nil {
return err
}
} else {
out.SourceStrategy = nil
}
if in.CustomStrategy != nil {
in, out := in.CustomStrategy, &out.CustomStrategy
*out = new(CustomBuildStrategy)
if err := DeepCopy_api_CustomBuildStrategy(*in, *out, c); err != nil {
return err
}
} else {
out.CustomStrategy = nil
}
if in.JenkinsPipelineStrategy != nil {
in, out := in.JenkinsPipelineStrategy, &out.JenkinsPipelineStrategy
*out = new(JenkinsPipelineBuildStrategy)
if err := DeepCopy_api_JenkinsPipelineBuildStrategy(*in, *out, c); err != nil {
return err
}
} else {
out.JenkinsPipelineStrategy = nil
}
return nil
}
func DeepCopy_api_BuildTriggerCause(in BuildTriggerCause, out *BuildTriggerCause, c *conversion.Cloner) error {
out.Message = in.Message
if in.GenericWebHook != nil {
in, out := in.GenericWebHook, &out.GenericWebHook
*out = new(GenericWebHookCause)
if err := DeepCopy_api_GenericWebHookCause(*in, *out, c); err != nil {
return err
}
} else {
out.GenericWebHook = nil
}
if in.GitHubWebHook != nil {
in, out := in.GitHubWebHook, &out.GitHubWebHook
*out = new(GitHubWebHookCause)
if err := DeepCopy_api_GitHubWebHookCause(*in, *out, c); err != nil {
return err
}
} else {
out.GitHubWebHook = nil
}
if in.ImageChangeBuild != nil {
in, out := in.ImageChangeBuild, &out.ImageChangeBuild
*out = new(ImageChangeCause)
if err := DeepCopy_api_ImageChangeCause(*in, *out, c); err != nil {
return err
}
} else {
out.ImageChangeBuild = nil
}
return nil
}
func DeepCopy_api_BuildTriggerPolicy(in BuildTriggerPolicy, out *BuildTriggerPolicy, c *conversion.Cloner) error {
out.Type = in.Type
if in.GitHubWebHook != nil {
in, out := in.GitHubWebHook, &out.GitHubWebHook
*out = new(WebHookTrigger)
if err := DeepCopy_api_WebHookTrigger(*in, *out, c); err != nil {
return err
}
} else {
out.GitHubWebHook = nil
}
if in.GenericWebHook != nil {
in, out := in.GenericWebHook, &out.GenericWebHook
*out = new(WebHookTrigger)
if err := DeepCopy_api_WebHookTrigger(*in, *out, c); err != nil {
return err
}
} else {
out.GenericWebHook = nil
}
if in.ImageChange != nil {
in, out := in.ImageChange, &out.ImageChange
*out = new(ImageChangeTrigger)
if err := DeepCopy_api_ImageChangeTrigger(*in, *out, c); err != nil {
return err
}
} else {
out.ImageChange = nil
}
return nil
}
func DeepCopy_api_CommonSpec(in CommonSpec, out *CommonSpec, c *conversion.Cloner) error {
out.ServiceAccount = in.ServiceAccount
if err := DeepCopy_api_BuildSource(in.Source, &out.Source, c); err != nil {
return err
}
if in.Revision != nil {
in, out := in.Revision, &out.Revision
*out = new(SourceRevision)
if err := DeepCopy_api_SourceRevision(*in, *out, c); err != nil {
return err
}
} else {
out.Revision = nil
}
if err := DeepCopy_api_BuildStrategy(in.Strategy, &out.Strategy, c); err != nil {
return err
}
if err := DeepCopy_api_BuildOutput(in.Output, &out.Output, c); err != nil {
return err
}
if err := api.DeepCopy_api_ResourceRequirements(in.Resources, &out.Resources, c); err != nil {
return err
}
if err := DeepCopy_api_BuildPostCommitSpec(in.PostCommit, &out.PostCommit, c); err != nil {
return err
}
if in.CompletionDeadlineSeconds != nil {
in, out := in.CompletionDeadlineSeconds, &out.CompletionDeadlineSeconds
*out = new(int64)
**out = *in
} else {
out.CompletionDeadlineSeconds = nil
}
return nil
}
func DeepCopy_api_CustomBuildStrategy(in CustomBuildStrategy, out *CustomBuildStrategy, c *conversion.Cloner) error {
if err := api.DeepCopy_api_ObjectReference(in.From, &out.From, c); err != nil {
return err
}
if in.PullSecret != nil {
in, out := in.PullSecret, &out.PullSecret
*out = new(api.LocalObjectReference)
if err := api.DeepCopy_api_LocalObjectReference(*in, *out, c); err != nil {
return err
}
} else {
out.PullSecret = nil
}
if in.Env != nil {
in, out := in.Env, &out.Env
*out = make([]api.EnvVar, len(in))
for i := range in {
if err := api.DeepCopy_api_EnvVar(in[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Env = nil
}
out.ExposeDockerSocket = in.ExposeDockerSocket
out.ForcePull = in.ForcePull
if in.Secrets != nil {
in, out := in.Secrets, &out.Secrets
*out = make([]SecretSpec, len(in))
for i := range in {
if err := DeepCopy_api_SecretSpec(in[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Secrets = nil
}
out.BuildAPIVersion = in.BuildAPIVersion
return nil
}
func DeepCopy_api_DockerBuildStrategy(in DockerBuildStrategy, out *DockerBuildStrategy, c *conversion.Cloner) error {
if in.From != nil {
in, out := in.From, &out.From
*out = new(api.ObjectReference)
if err := api.DeepCopy_api_ObjectReference(*in, *out, c); err != nil {
return err
}
} else {
out.From = nil
}
if in.PullSecret != nil {
in, out := in.PullSecret, &out.PullSecret
*out = new(api.LocalObjectReference)
if err := api.DeepCopy_api_LocalObjectReference(*in, *out, c); err != nil {
return err
}
} else {
out.PullSecret = nil
}
out.NoCache = in.NoCache
if in.Env != nil {
in, out := in.Env, &out.Env
*out = make([]api.EnvVar, len(in))
for i := range in {
if err := api.DeepCopy_api_EnvVar(in[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Env = nil
}
out.ForcePull = in.ForcePull
out.DockerfilePath = in.DockerfilePath
return nil
}
func DeepCopy_api_GenericWebHookCause(in GenericWebHookCause, out *GenericWebHookCause, c *conversion.Cloner) error {
if in.Revision != nil {
in, out := in.Revision, &out.Revision
*out = new(SourceRevision)
if err := DeepCopy_api_SourceRevision(*in, *out, c); err != nil {
return err
}
} else {
out.Revision = nil
}
out.Secret = in.Secret
return nil
}
func DeepCopy_api_GenericWebHookEvent(in GenericWebHookEvent, out *GenericWebHookEvent, c *conversion.Cloner) error {
if in.Git != nil {
in, out := in.Git, &out.Git
*out = new(GitInfo)
if err := DeepCopy_api_GitInfo(*in, *out, c); err != nil {
return err
}
} else {
out.Git = nil
}
if in.Env != nil {
in, out := in.Env, &out.Env
*out = make([]api.EnvVar, len(in))
for i := range in {
if err := api.DeepCopy_api_EnvVar(in[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Env = nil
}
return nil
}
func DeepCopy_api_GitBuildSource(in GitBuildSource, out *GitBuildSource, c *conversion.Cloner) error {
out.URI = in.URI
out.Ref = in.Ref
if in.HTTPProxy != nil {
in, out := in.HTTPProxy, &out.HTTPProxy
*out = new(string)
**out = *in
} else {
out.HTTPProxy = nil
}
if in.HTTPSProxy != nil {
in, out := in.HTTPSProxy, &out.HTTPSProxy
*out = new(string)
**out = *in
} else {
out.HTTPSProxy = nil
}
return nil
}
func DeepCopy_api_GitHubWebHookCause(in GitHubWebHookCause, out *GitHubWebHookCause, c *conversion.Cloner) error {
if in.Revision != nil {
in, out := in.Revision, &out.Revision
*out = new(SourceRevision)
if err := DeepCopy_api_SourceRevision(*in, *out, c); err != nil {
return err
}
} else {
out.Revision = nil
}
out.Secret = in.Secret
return nil
}
func DeepCopy_api_GitInfo(in GitInfo, out *GitInfo, c *conversion.Cloner) error {
if err := DeepCopy_api_GitBuildSource(in.GitBuildSource, &out.GitBuildSource, c); err != nil {
return err
}
if err := DeepCopy_api_GitSourceRevision(in.GitSourceRevision, &out.GitSourceRevision, c); err != nil {
return err
}
if in.Refs != nil {
in, out := in.Refs, &out.Refs
*out = make([]GitRefInfo, len(in))
for i := range in {
if err := DeepCopy_api_GitRefInfo(in[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Refs = nil
}
return nil
}
func DeepCopy_api_GitRefInfo(in GitRefInfo, out *GitRefInfo, c *conversion.Cloner) error {
if err := DeepCopy_api_GitBuildSource(in.GitBuildSource, &out.GitBuildSource, c); err != nil {
return err
}
if err := DeepCopy_api_GitSourceRevision(in.GitSourceRevision, &out.GitSourceRevision, c); err != nil {
return err
}
return nil
}
func DeepCopy_api_GitSourceRevision(in GitSourceRevision, out *GitSourceRevision, c *conversion.Cloner) error {
out.Commit = in.Commit
if err := DeepCopy_api_SourceControlUser(in.Author, &out.Author, c); err != nil {
return err
}
if err := DeepCopy_api_SourceControlUser(in.Committer, &out.Committer, c); err != nil {
return err
}
out.Message = in.Message
return nil
}
func DeepCopy_api_ImageChangeCause(in ImageChangeCause, out *ImageChangeCause, c *conversion.Cloner) error {
out.ImageID = in.ImageID
if in.FromRef != nil {
in, out := in.FromRef, &out.FromRef
*out = new(api.ObjectReference)
if err := api.DeepCopy_api_ObjectReference(*in, *out, c); err != nil {
return err
}
} else {
out.FromRef = nil
}
return nil
}
func DeepCopy_api_ImageChangeTrigger(in ImageChangeTrigger, out *ImageChangeTrigger, c *conversion.Cloner) error {
out.LastTriggeredImageID = in.LastTriggeredImageID
if in.From != nil {
in, out := in.From, &out.From
*out = new(api.ObjectReference)
if err := api.DeepCopy_api_ObjectReference(*in, *out, c); err != nil {
return err
}
} else {
out.From = nil
}
return nil
}
func DeepCopy_api_ImageSource(in ImageSource, out *ImageSource, c *conversion.Cloner) error {
if err := api.DeepCopy_api_ObjectReference(in.From, &out.From, c); err != nil {
return err
}
if in.Paths != nil {
in, out := in.Paths, &out.Paths
*out = make([]ImageSourcePath, len(in))
for i := range in {
if err := DeepCopy_api_ImageSourcePath(in[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Paths = nil
}
if in.PullSecret != nil {
in, out := in.PullSecret, &out.PullSecret
*out = new(api.LocalObjectReference)
if err := api.DeepCopy_api_LocalObjectReference(*in, *out, c); err != nil {
return err
}
} else {
out.PullSecret = nil
}
return nil
}
func DeepCopy_api_ImageSourcePath(in ImageSourcePath, out *ImageSourcePath, c *conversion.Cloner) error {
out.SourcePath = in.SourcePath
out.DestinationDir = in.DestinationDir
return nil
}
func DeepCopy_api_JenkinsPipelineBuildStrategy(in JenkinsPipelineBuildStrategy, out *JenkinsPipelineBuildStrategy, c *conversion.Cloner) error {
out.JenkinsfilePath = in.JenkinsfilePath
out.Jenkinsfile = in.Jenkinsfile
return nil
}
func DeepCopy_api_SecretBuildSource(in SecretBuildSource, out *SecretBuildSource, c *conversion.Cloner) error {
if err := api.DeepCopy_api_LocalObjectReference(in.Secret, &out.Secret, c); err != nil {
return err
}
out.DestinationDir = in.DestinationDir
return nil
}
func DeepCopy_api_SecretSpec(in SecretSpec, out *SecretSpec, c *conversion.Cloner) error {
if err := api.DeepCopy_api_LocalObjectReference(in.SecretSource, &out.SecretSource, c); err != nil {
return err
}
out.MountPath = in.MountPath
return nil
}
func DeepCopy_api_SourceBuildStrategy(in SourceBuildStrategy, out *SourceBuildStrategy, c *conversion.Cloner) error {
if err := api.DeepCopy_api_ObjectReference(in.From, &out.From, c); err != nil {
return err
}
if in.PullSecret != nil {
in, out := in.PullSecret, &out.PullSecret
*out = new(api.LocalObjectReference)
if err := api.DeepCopy_api_LocalObjectReference(*in, *out, c); err != nil {
return err
}
} else {
out.PullSecret = nil
}
if in.Env != nil {
in, out := in.Env, &out.Env
*out = make([]api.EnvVar, len(in))
for i := range in {
if err := api.DeepCopy_api_EnvVar(in[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Env = nil
}
out.Scripts = in.Scripts
out.Incremental = in.Incremental
out.ForcePull = in.ForcePull
return nil
}
func DeepCopy_api_SourceControlUser(in SourceControlUser, out *SourceControlUser, c *conversion.Cloner) error {
out.Name = in.Name
out.Email = in.Email
return nil
}
func DeepCopy_api_SourceRevision(in SourceRevision, out *SourceRevision, c *conversion.Cloner) error {
if in.Git != nil {
in, out := in.Git, &out.Git
*out = new(GitSourceRevision)
if err := DeepCopy_api_GitSourceRevision(*in, *out, c); err != nil {
return err
}
} else {
out.Git = nil
}
return nil
}
func DeepCopy_api_WebHookTrigger(in WebHookTrigger, out *WebHookTrigger, c *conversion.Cloner) error {
out.Secret = in.Secret
out.AllowEnv = in.AllowEnv
return nil
}
+4
View File
@@ -0,0 +1,4 @@
// +k8s:deepcopy-gen=package,register
// Package api is the internal version of the API.
package api
+7 -5
View File
@@ -6,6 +6,7 @@ import (
)
const GroupName = ""
const FutureGroupName = "build.openshift.io"
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}
@@ -20,13 +21,13 @@ func Resource(resource string) unversioned.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
func AddToScheme(scheme *runtime.Scheme) {
// Add the API to Scheme.
addKnownTypes(scheme)
}
var (
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
AddToScheme = SchemeBuilder.AddToScheme
)
// Adds the list of known types to api.Scheme.
func addKnownTypes(scheme *runtime.Scheme) {
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&Build{},
&BuildList{},
@@ -37,6 +38,7 @@ func addKnownTypes(scheme *runtime.Scheme) {
&BuildLogOptions{},
&BinaryBuildRequestOptions{},
)
return nil
}
func (obj *Build) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
+18 -3
View File
@@ -424,7 +424,7 @@ type BuildStrategy struct {
CustomStrategy *CustomBuildStrategy
// JenkinsPipelineStrategy holds the parameters to the Jenkins Pipeline build strategy.
// This strategy is experimental.
// This strategy is in tech preview.
JenkinsPipelineStrategy *JenkinsPipelineBuildStrategy
}
@@ -512,14 +512,28 @@ type SourceBuildStrategy struct {
Scripts string
// Incremental flag forces the Source build to do incremental builds if true.
Incremental bool
Incremental *bool
// ForcePull describes if the builder should pull the images from registry prior to building.
ForcePull bool
// RuntimeImage is an optional image that is used to run an application
// without unneeded dependencies installed. The building of the application
// is still done in the builder image but, post build, you can copy the
// needed artifacts in the runtime image for use.
// This field and the feature it enables are in tech preview.
RuntimeImage *kapi.ObjectReference
// RuntimeArtifacts specifies a list of source/destination pairs that will be
// copied from the builder to a runtime image. sourcePath can be a file or
// directory. destinationDir must be a directory. destinationDir can also be
// empty or equal to ".", in this case it just refers to the root of WORKDIR.
// This field and the feature it enables are in tech preview.
RuntimeArtifacts []ImageSourcePath
}
// JenkinsPipelineStrategy holds parameters specific to a Jenkins Pipeline build.
// This strategy is experimental.
// This strategy is in tech preview.
type JenkinsPipelineBuildStrategy struct {
// JenkinsfilePath is the optional path of the Jenkinsfile that will be used to configure the pipeline
// relative to the root of the context (contextDir). If both JenkinsfilePath & Jenkinsfile are
@@ -792,6 +806,7 @@ type GitInfo struct {
// Refs is a list of GitRefs for the provided repo - generally sent
// when used from a post-receive hook. This field is optional and is
// used when sending multiple refs
// +k8s:conversion-gen=false
Refs []GitRefInfo
}
File diff suppressed because it is too large Load Diff
+109
View File
@@ -0,0 +1,109 @@
package client
import (
buildapi "github.com/openshift/origin/pkg/build/api"
osclient "github.com/openshift/origin/pkg/client"
kapi "k8s.io/kubernetes/pkg/api"
)
// BuildConfigGetter provides methods for getting BuildConfigs
type BuildConfigGetter interface {
Get(namespace, name string) (*buildapi.BuildConfig, error)
}
// BuildConfigUpdater provides methods for updating BuildConfigs
type BuildConfigUpdater interface {
Update(buildConfig *buildapi.BuildConfig) error
}
// OSClientBuildConfigClient delegates get and update operations to the OpenShift client interface
type OSClientBuildConfigClient struct {
Client osclient.Interface
}
// NewOSClientBuildConfigClient creates a new build config client that uses an openshift client to create and get BuildConfigs
func NewOSClientBuildConfigClient(client osclient.Interface) *OSClientBuildConfigClient {
return &OSClientBuildConfigClient{Client: client}
}
// Get returns a BuildConfig using the OpenShift client.
func (c OSClientBuildConfigClient) Get(namespace, name string) (*buildapi.BuildConfig, error) {
return c.Client.BuildConfigs(namespace).Get(name)
}
// Update updates a BuildConfig using the OpenShift client.
func (c OSClientBuildConfigClient) Update(buildConfig *buildapi.BuildConfig) error {
_, err := c.Client.BuildConfigs(buildConfig.Namespace).Update(buildConfig)
return err
}
// BuildUpdater provides methods for updating existing Builds.
type BuildUpdater interface {
Update(namespace string, build *buildapi.Build) error
}
// BuildLister provides methods for listing the Builds.
type BuildLister interface {
List(namespace string, opts kapi.ListOptions) (*buildapi.BuildList, error)
}
// OSClientBuildClient deletes build create and update operations to the OpenShift client interface
type OSClientBuildClient struct {
Client osclient.Interface
}
// NewOSClientBuildClient creates a new build client that uses an openshift client to update builds
func NewOSClientBuildClient(client osclient.Interface) *OSClientBuildClient {
return &OSClientBuildClient{Client: client}
}
// Update updates builds using the OpenShift client.
func (c OSClientBuildClient) Update(namespace string, build *buildapi.Build) error {
_, e := c.Client.Builds(namespace).Update(build)
return e
}
// List lists the builds using the OpenShift client.
func (c OSClientBuildClient) List(namespace string, opts kapi.ListOptions) (*buildapi.BuildList, error) {
return c.Client.Builds(namespace).List(opts)
}
// BuildCloner provides methods for cloning builds
type BuildCloner interface {
Clone(namespace string, request *buildapi.BuildRequest) (*buildapi.Build, error)
}
// OSClientBuildClonerClient creates a new build client that uses an openshift client to clone builds
type OSClientBuildClonerClient struct {
Client osclient.Interface
}
// NewOSClientBuildClonerClient creates a new build client that uses an openshift client to clone builds
func NewOSClientBuildClonerClient(client osclient.Interface) *OSClientBuildClonerClient {
return &OSClientBuildClonerClient{Client: client}
}
// Clone generates new build for given build name
func (c OSClientBuildClonerClient) Clone(namespace string, request *buildapi.BuildRequest) (*buildapi.Build, error) {
return c.Client.Builds(namespace).Clone(request)
}
// BuildConfigInstantiator provides methods for instantiating builds from build configs
type BuildConfigInstantiator interface {
Instantiate(namespace string, request *buildapi.BuildRequest) (*buildapi.Build, error)
}
// OSClientBuildConfigInstantiatorClient creates a new build client that uses an openshift client to create builds
type OSClientBuildConfigInstantiatorClient struct {
Client osclient.Interface
}
// NewOSClientBuildConfigInstantiatorClient creates a new build client that uses an openshift client to create builds
func NewOSClientBuildConfigInstantiatorClient(client osclient.Interface) *OSClientBuildConfigInstantiatorClient {
return &OSClientBuildConfigInstantiatorClient{Client: client}
}
// Instantiate generates new build for given buildConfig
func (c OSClientBuildConfigInstantiatorClient) Instantiate(namespace string, request *buildapi.BuildRequest) (*buildapi.Build, error) {
return c.Client.BuildConfigs(namespace).Instantiate(request)
}
+2
View File
@@ -0,0 +1,2 @@
// Package cmd provides command helpers for builds
package cmd
+150
View File
@@ -0,0 +1,150 @@
package cmd
import (
"sort"
"strings"
"time"
"github.com/golang/glog"
kapi "k8s.io/kubernetes/pkg/api"
kerrors "k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/kubectl"
ktypes "k8s.io/kubernetes/pkg/types"
kutilerrors "k8s.io/kubernetes/pkg/util/errors"
buildapi "github.com/openshift/origin/pkg/build/api"
buildutil "github.com/openshift/origin/pkg/build/util"
"github.com/openshift/origin/pkg/client"
"github.com/openshift/origin/pkg/util"
)
// NewBuildConfigReaper returns a new reaper for buildConfigs
func NewBuildConfigReaper(oc *client.Client) kubectl.Reaper {
return &BuildConfigReaper{oc: oc, pollInterval: kubectl.Interval, timeout: kubectl.Timeout}
}
// BuildConfigReaper implements the Reaper interface for buildConfigs
type BuildConfigReaper struct {
oc client.Interface
pollInterval, timeout time.Duration
}
// Stop deletes the build configuration and all of the associated builds.
func (reaper *BuildConfigReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *kapi.DeleteOptions) error {
_, err := reaper.oc.BuildConfigs(namespace).Get(name)
if err != nil {
return err
}
var bcPotentialBuilds []buildapi.Build
// Collect builds related to the config.
builds, err := reaper.oc.Builds(namespace).List(kapi.ListOptions{LabelSelector: buildutil.BuildConfigSelector(name)})
if err != nil {
return err
}
bcPotentialBuilds = append(bcPotentialBuilds, builds.Items...)
// Collect deprecated builds related to the config.
// TODO: Delete this block after BuildConfigLabelDeprecated is removed.
builds, err = reaper.oc.Builds(namespace).List(kapi.ListOptions{LabelSelector: buildutil.BuildConfigSelectorDeprecated(name)})
if err != nil {
return err
}
bcPotentialBuilds = append(bcPotentialBuilds, builds.Items...)
// A map of builds associated with this build configuration
bcBuilds := make(map[ktypes.UID]buildapi.Build)
// Because of name length limits in the BuildConfigSelector, annotations are used to ensure
// reliable selection of associated builds.
for _, build := range bcPotentialBuilds {
if build.Annotations != nil {
if bcName, ok := build.Annotations[buildapi.BuildConfigAnnotation]; ok {
// The annotation, if present, has the full build config name.
if bcName != name {
// If the name does not match exactly, the build is not truly associated with the build configuration
continue
}
}
}
// Note that if there is no annotation, this is a deprecated build spec
// and we choose to include it in the deletion having matched only the BuildConfigSelectorDeprecated
// Use a map to union the lists returned by the contemporary & deprecated build queries
// (there will be overlap between the lists, and we only want to try to delete each build once)
bcBuilds[build.UID] = build
}
// If there are builds associated with this build configuration, pause it before attempting the deletion
if len(bcBuilds) > 0 {
// Add paused annotation to the build config pending the deletion
err = unversioned.RetryOnConflict(unversioned.DefaultRetry, func() error {
bc, err := reaper.oc.BuildConfigs(namespace).Get(name)
if err != nil {
return err
}
// Ignore if the annotation already exists
if strings.ToLower(bc.Annotations[buildapi.BuildConfigPausedAnnotation]) == "true" {
return nil
}
// Set the annotation and update
if err := util.AddObjectAnnotations(bc, map[string]string{buildapi.BuildConfigPausedAnnotation: "true"}); err != nil {
return err
}
_, err = reaper.oc.BuildConfigs(namespace).Update(bc)
return err
})
if err != nil {
return err
}
}
// Warn the user if the BuildConfig won't get deleted after this point.
bcDeleted := false
defer func() {
if !bcDeleted {
glog.Warningf("BuildConfig %s/%s will not be deleted because not all associated builds could be deleted. You can try re-running the command or removing them manually", namespace, name)
}
}()
// For the benefit of test cases, sort the UIDs so that the deletion order is deterministic
buildUIDs := make([]string, 0, len(bcBuilds))
for buildUID := range bcBuilds {
buildUIDs = append(buildUIDs, string(buildUID))
}
sort.Strings(buildUIDs)
errList := []error{}
for _, buildUID := range buildUIDs {
build := bcBuilds[ktypes.UID(buildUID)]
if err := reaper.oc.Builds(namespace).Delete(build.Name); err != nil {
glog.Warningf("Cannot delete Build %s/%s: %v", build.Namespace, build.Name, err)
if !kerrors.IsNotFound(err) {
errList = append(errList, err)
}
}
}
// Aggregate all errors
if len(errList) > 0 {
return kutilerrors.NewAggregate(errList)
}
if err := reaper.oc.BuildConfigs(namespace).Delete(name); err != nil {
return err
}
bcDeleted = true
return nil
}
+351
View File
@@ -0,0 +1,351 @@
package analysis
import (
"fmt"
"strings"
"time"
"github.com/gonum/graph"
"github.com/gonum/graph/topo"
"k8s.io/kubernetes/pkg/api/unversioned"
osgraph "github.com/openshift/origin/pkg/api/graph"
buildapi "github.com/openshift/origin/pkg/build/api"
buildedges "github.com/openshift/origin/pkg/build/graph"
buildgraph "github.com/openshift/origin/pkg/build/graph/nodes"
imageapi "github.com/openshift/origin/pkg/image/api"
imageedges "github.com/openshift/origin/pkg/image/graph"
imagegraph "github.com/openshift/origin/pkg/image/graph/nodes"
)
const (
TagNotAvailableWarning = "ImageStreamTagNotAvailable"
LatestBuildFailedErr = "LatestBuildFailed"
MissingRequiredRegistryErr = "MissingRequiredRegistry"
MissingOutputImageStreamErr = "MissingOutputImageStream"
CyclicBuildConfigWarning = "CyclicBuildConfig"
MissingImageStreamTagWarning = "MissingImageStreamTag"
MissingImageStreamImageWarning = "MissingImageStreamImage"
)
// FindUnpushableBuildConfigs checks all build configs that will output to an IST backed by an ImageStream and checks to make sure their builds can push.
func FindUnpushableBuildConfigs(g osgraph.Graph, f osgraph.Namer) []osgraph.Marker {
markers := []osgraph.Marker{}
// note, unlike with Inputs, ImageStreamImage is not a valid type for build output
bc:
for _, bcNode := range g.NodesByKind(buildgraph.BuildConfigNodeKind) {
for _, istNode := range g.SuccessorNodesByEdgeKind(bcNode, buildedges.BuildOutputEdgeKind) {
for _, uncastImageStreamNode := range g.SuccessorNodesByEdgeKind(istNode, imageedges.ReferencedImageStreamGraphEdgeKind) {
imageStreamNode := uncastImageStreamNode.(*imagegraph.ImageStreamNode)
if !imageStreamNode.IsFound {
markers = append(markers, osgraph.Marker{
Node: bcNode,
RelatedNodes: []graph.Node{istNode},
Severity: osgraph.ErrorSeverity,
Key: MissingOutputImageStreamErr,
Message: fmt.Sprintf("%s is pushing to %s, but the image stream for that tag does not exist.",
f.ResourceName(bcNode), f.ResourceName(istNode)),
})
continue
}
if len(imageStreamNode.Status.DockerImageRepository) == 0 {
markers = append(markers, osgraph.Marker{
Node: bcNode,
RelatedNodes: []graph.Node{istNode},
Severity: osgraph.ErrorSeverity,
Key: MissingRequiredRegistryErr,
Message: fmt.Sprintf("%s is pushing to %s, but the administrator has not configured the integrated Docker registry.",
f.ResourceName(bcNode), f.ResourceName(istNode)),
Suggestion: osgraph.Suggestion("oc adm registry -h"),
})
continue bc
}
}
}
}
return markers
}
// FindMissingInputImageStreams checks all build configs and confirms that their From element exists
//
// Precedence of failures:
// 1. A build config's input points to an image stream that does not exist
// 2. A build config's input uses an image stream tag reference in an existing image stream, but no images within the image stream have that tag assigned
// 3. A build config's input uses an image stream image reference in an exisiting image stream, but no images within the image stream have the supplied image hexadecimal ID
func FindMissingInputImageStreams(g osgraph.Graph, f osgraph.Namer) []osgraph.Marker {
markers := []osgraph.Marker{}
for _, bcNode := range g.NodesByKind(buildgraph.BuildConfigNodeKind) {
for _, bcInputNode := range g.PredecessorNodesByEdgeKind(bcNode, buildedges.BuildInputImageEdgeKind) {
switch bcInputNode.(type) {
case *imagegraph.ImageStreamTagNode:
for _, uncastImageStreamNode := range g.SuccessorNodesByEdgeKind(bcInputNode, imageedges.ReferencedImageStreamGraphEdgeKind) {
imageStreamNode := uncastImageStreamNode.(*imagegraph.ImageStreamNode)
// note, BuildConfig.Spec.BuildSpec.Strategy.[Docker|Source|Custom]Stragegy.From Input of ImageStream has been converted to ImageStreamTag on the vX to api conversion
// prior to our reaching this point in the code; so there is not need to check for that type vs. ImageStreamTag or ImageStreamImage;
tagNode, _ := bcInputNode.(*imagegraph.ImageStreamTagNode)
imageStream := imageStreamNode.Object().(*imageapi.ImageStream)
if _, ok := imageStream.Status.Tags[tagNode.ImageTag()]; !ok {
markers = append(markers, getImageStreamTagMarker(g, f, bcInputNode, imageStreamNode, tagNode, bcNode))
}
}
case *imagegraph.ImageStreamImageNode:
for _, uncastImageStreamNode := range g.SuccessorNodesByEdgeKind(bcInputNode, imageedges.ReferencedImageStreamImageGraphEdgeKind) {
imageStreamNode := uncastImageStreamNode.(*imagegraph.ImageStreamNode)
imageNode, _ := bcInputNode.(*imagegraph.ImageStreamImageNode)
imageStream := imageStreamNode.Object().(*imageapi.ImageStream)
found, imageID := validImageStreamImage(imageNode, imageStream)
if !found {
markers = append(markers, getImageStreamImageMarker(g, f, bcNode, bcInputNode, imageStreamNode, imageNode, imageStream, imageID))
}
}
}
}
}
return markers
}
// FindCircularBuilds checks all build configs for cycles
func FindCircularBuilds(g osgraph.Graph, f osgraph.Namer) []osgraph.Marker {
// Filter out all but ImageStreamTag and BuildConfig nodes
nodeFn := osgraph.NodesOfKind(imagegraph.ImageStreamTagNodeKind, buildgraph.BuildConfigNodeKind)
// Filter out all but BuildInputImage and BuildOutput edges
edgeFn := osgraph.EdgesOfKind(buildedges.BuildInputImageEdgeKind, buildedges.BuildOutputEdgeKind)
// Create desired subgraph
sub := g.Subgraph(nodeFn, edgeFn)
markers := []osgraph.Marker{}
// Check for cycles
for _, cycle := range topo.CyclesIn(sub) {
nodeNames := []string{}
for _, node := range cycle {
nodeNames = append(nodeNames, f.ResourceName(node))
}
markers = append(markers, osgraph.Marker{
Node: cycle[0],
RelatedNodes: cycle,
Severity: osgraph.WarningSeverity,
Key: CyclicBuildConfigWarning,
Message: fmt.Sprintf("Cycle detected in build configurations: %s", strings.Join(nodeNames, " -> ")),
})
}
return markers
}
// multiBCStartBuildSuggestion builds the `oc start-build` suggestion string with multiple build configs
func multiBCStartBuildSuggestion(bcNodes []*buildgraph.BuildConfigNode) string {
var ret string
if len(bcNodes) > 1 {
ret = "Run one of the following commands: "
}
for i, bcNode := range bcNodes {
// use of f.ResourceName(bcNode) will produce a string like oc start-build BuildConfig|example/ruby-hello-world
ret = ret + fmt.Sprintf("oc start-build %s", bcNode.BuildConfig.GetName())
if i < (len(bcNodes) - 1) {
ret = ret + ", "
}
}
return ret
}
// bcNodesToRelatedNodes takes an array of BuildConfigNode's and returns an array of graph.Node's for the Marker.RelatedNodes field
func bcNodesToRelatedNodes(bcNodes []*buildgraph.BuildConfigNode) []graph.Node {
relatedNodes := []graph.Node{}
for _, bcNode := range bcNodes {
relatedNodes = append(relatedNodes, graph.Node(bcNode))
}
return relatedNodes
}
// findPendingTagMarkers is the guts behind FindPendingTags .... break out some of the content and reduce some indentation
func findPendingTagMarkers(istNode *imagegraph.ImageStreamTagNode, g osgraph.Graph, f osgraph.Namer) []osgraph.Marker {
markers := []osgraph.Marker{}
buildFound := false
bcNodes := buildedges.BuildConfigsForTag(g, graph.Node(istNode))
for _, bcNode := range bcNodes {
latestBuild := buildedges.GetLatestBuild(g, bcNode)
// A build config points to the non existent tag but no current build exists.
if latestBuild == nil {
continue
}
buildFound = true
// A build config points to the non existent tag but something is going on with
// the latest build.
// TODO: Handle other build phases.
switch latestBuild.Build.Status.Phase {
case buildapi.BuildPhaseCancelled:
// TODO: Add a warning here.
case buildapi.BuildPhaseError:
// TODO: Add a warning here.
case buildapi.BuildPhaseComplete:
// We should never hit this. The output of our build is missing but the build is complete.
// Most probably the user has messed up?
case buildapi.BuildPhaseFailed:
// Since the tag hasn't been populated yet, we assume there hasn't been a successful
// build so far.
markers = append(markers, osgraph.Marker{
Node: graph.Node(latestBuild),
RelatedNodes: []graph.Node{graph.Node(istNode), graph.Node(bcNode)},
Severity: osgraph.ErrorSeverity,
Key: LatestBuildFailedErr,
Message: fmt.Sprintf("%s has failed.", f.ResourceName(latestBuild)),
Suggestion: osgraph.Suggestion(fmt.Sprintf("Inspect the build failure with 'oc logs -f bc/%s'", bcNode.BuildConfig.GetName())),
})
default:
// Do nothing when latest build is new, pending, or running.
}
}
// if no current builds exist for any of the build configs, append marker for that
// but ignore ISTs which have no build configs
if !buildFound && len(bcNodes) > 0 {
markers = append(markers, osgraph.Marker{
Node: graph.Node(istNode),
RelatedNodes: bcNodesToRelatedNodes(bcNodes),
Severity: osgraph.WarningSeverity,
Key: TagNotAvailableWarning,
Message: fmt.Sprintf("%s needs to be imported or created by a build.", f.ResourceName(istNode)),
Suggestion: osgraph.Suggestion(multiBCStartBuildSuggestion(bcNodes)),
})
}
return markers
}
// FindPendingTags inspects all imageStreamTags that serve as outputs to builds.
//
// Precedence of failures:
// 1. A build config points to the non existent tag but no current build exists.
// 2. A build config points to the non existent tag but the latest build has failed.
func FindPendingTags(g osgraph.Graph, f osgraph.Namer) []osgraph.Marker {
markers := []osgraph.Marker{}
for _, uncastIstNode := range g.NodesByKind(imagegraph.ImageStreamTagNodeKind) {
istNode := uncastIstNode.(*imagegraph.ImageStreamTagNode)
if !istNode.Found() {
markers = append(markers, findPendingTagMarkers(istNode, g, f)...)
}
}
return markers
}
// getImageStreamTagMarker will return the appropriate marker for when a BuildConfig is missing its input ImageStreamTag
func getImageStreamTagMarker(g osgraph.Graph, f osgraph.Namer, bcInputNode graph.Node, imageStreamNode graph.Node, tagNode *imagegraph.ImageStreamTagNode, bcNode graph.Node) osgraph.Marker {
return osgraph.Marker{
Node: bcNode,
RelatedNodes: []graph.Node{bcInputNode,
imageStreamNode},
Severity: osgraph.WarningSeverity,
Key: MissingImageStreamImageWarning,
Message: fmt.Sprintf("%s builds from %s, but the image stream tag does not exist.", f.ResourceName(bcNode), f.ResourceName(bcInputNode)),
Suggestion: getImageStreamTagSuggestion(g, f, tagNode),
}
}
// getImageStreamTagSuggestion will return the appropriate marker Suggestion for when a BuildConfig is missing its input ImageStreamTag; in particular,
// it will determine whether or not another BuildConfig can produce the aforementioned ImageStreamTag
func getImageStreamTagSuggestion(g osgraph.Graph, f osgraph.Namer, tagNode *imagegraph.ImageStreamTagNode) osgraph.Suggestion {
bcs := []string{}
for _, bcNode := range g.PredecessorNodesByEdgeKind(tagNode, buildedges.BuildOutputEdgeKind) {
bcs = append(bcs, f.ResourceName(bcNode))
}
if len(bcs) == 1 {
return osgraph.Suggestion(fmt.Sprintf("oc start-build %s", bcs[0]))
}
if len(bcs) > 0 {
return osgraph.Suggestion(fmt.Sprintf("`oc start-build` with one of these: %s.", strings.Join(bcs[:], ",")))
}
return osgraph.Suggestion(fmt.Sprintf("%s needs to be imported.", f.ResourceName(tagNode)))
}
// getImageStreamImageMarker will return the appropriate marker for when a BuildConfig is missing its input ImageStreamImage
func getImageStreamImageMarker(g osgraph.Graph, f osgraph.Namer, bcNode graph.Node, bcInputNode graph.Node, imageStreamNode graph.Node, imageNode *imagegraph.ImageStreamImageNode, imageStream *imageapi.ImageStream, imageID string) osgraph.Marker {
return osgraph.Marker{
Node: bcNode,
RelatedNodes: []graph.Node{bcInputNode,
imageStreamNode},
Severity: osgraph.WarningSeverity,
Key: MissingImageStreamImageWarning,
Message: fmt.Sprintf("%s builds from %s, but the image stream image does not exist.", f.ResourceName(bcNode), f.ResourceName(bcInputNode)),
Suggestion: getImageStreamImageSuggestion(imageID, imageStream),
}
}
// getImageStreamImageSuggestion will return the appropriate marker Suggestion for when a BuildConfig is missing its input ImageStreamImage
func getImageStreamImageSuggestion(imageID string, imageStream *imageapi.ImageStream) osgraph.Suggestion {
// check the images stream to see if any import images are in flight or have failed
annotation, ok := imageStream.Annotations[imageapi.DockerImageRepositoryCheckAnnotation]
if !ok {
return osgraph.Suggestion(fmt.Sprintf("`oc import-image %s --from=` where `--from` specifies an image with hexadecimal ID %s", imageStream.GetName(), imageID))
}
if checkTime, err := time.Parse(time.RFC3339, annotation); err == nil {
// this time based annotation is set by pkg/image/controller/controller.go whenever import/tag operations are performed; unless
// in the midst of an import/tag operation, it stays set and serves as a timestamp for when the last operation occurred;
// so we will check if the image stream has been updated "recently";
// in case it is a slow link to the remote repo, see if if the check annotation occurred within the last 5 minutes; if so, consider that as potentially "in progress"
compareTime := checkTime.Add(5 * time.Minute)
currentTime, _ := time.Parse(time.RFC3339, unversioned.Now().UTC().Format(time.RFC3339))
if compareTime.Before(currentTime) {
return osgraph.Suggestion(fmt.Sprintf("`oc import-image %s --from=` where `--from` specifies an image with hexadecimal ID %s", imageStream.GetName(), imageID))
}
return osgraph.Suggestion(fmt.Sprintf("`oc import-image %s --from=` with hexadecimal ID %s possibly in progress", imageStream.GetName(), imageID))
}
return osgraph.Suggestion(fmt.Sprintf("Possible error occurred with `oc import-image %s --from=` with hexadecimal ID %s; inspect images stream annotations", imageStream.GetName(), imageID))
}
// validImageStreamImage will cycle through the imageStream.Status.Tags.[]TagEvent.DockerImageReference and determine whether an image with the hexadecimal image id
// associated with an ImageStreamImage reference in fact exists in a given ImageStream; on return, this method returns a true if does exist, and as well as the hexadecimal image
// id from the ImageStreamImage
func validImageStreamImage(imageNode *imagegraph.ImageStreamImageNode, imageStream *imageapi.ImageStream) (bool, string) {
dockerImageReference, err := imageapi.ParseDockerImageReference(imageNode.Name)
if err == nil {
for _, tagEventList := range imageStream.Status.Tags {
for _, tagEvent := range tagEventList.Items {
if strings.Contains(tagEvent.DockerImageReference, dockerImageReference.ID) {
return true, dockerImageReference.ID
}
}
}
return false, dockerImageReference.ID
}
return false, ""
}
+133
View File
@@ -0,0 +1,133 @@
package graph
import (
"github.com/gonum/graph"
kapi "k8s.io/kubernetes/pkg/api"
osgraph "github.com/openshift/origin/pkg/api/graph"
buildapi "github.com/openshift/origin/pkg/build/api"
buildgraph "github.com/openshift/origin/pkg/build/graph/nodes"
buildutil "github.com/openshift/origin/pkg/build/util"
imageapi "github.com/openshift/origin/pkg/image/api"
imagegraph "github.com/openshift/origin/pkg/image/graph/nodes"
)
const (
// BuildTriggerImageEdgeKind is an edge from an ImageStream to a BuildConfig that
// represents a trigger connection. Changes to the ImageStream will trigger a new build
// from the BuildConfig.
BuildTriggerImageEdgeKind = "BuildTriggerImage"
// BuildInputImageEdgeKind is an edge from an ImageStream to a BuildConfig, where the
// ImageStream is the source image for the build (builder in S2I builds, FROM in Docker builds,
// custom builder in Custom builds). The same ImageStream can also have a trigger
// relationship with the BuildConfig, but not necessarily.
BuildInputImageEdgeKind = "BuildInputImage"
// BuildOutputEdgeKind is an edge from a BuildConfig to an ImageStream. The ImageStream will hold
// the ouptut of the Builds created with that BuildConfig.
BuildOutputEdgeKind = "BuildOutput"
// BuildInputEdgeKind is an edge from a source repository to a BuildConfig. The source repository is the
// input source for the build.
BuildInputEdgeKind = "BuildInput"
// BuildEdgeKind goes from a BuildConfigNode to a BuildNode and indicates that the buildConfig owns the build
BuildEdgeKind = "Build"
)
// AddBuildEdges adds edges that connect a BuildConfig to Builds to the given graph
func AddBuildEdges(g osgraph.MutableUniqueGraph, node *buildgraph.BuildConfigNode) {
for _, n := range g.(graph.Graph).Nodes() {
if buildNode, ok := n.(*buildgraph.BuildNode); ok {
if buildNode.Build.Namespace != node.BuildConfig.Namespace {
continue
}
if belongsToBuildConfig(node.BuildConfig, buildNode.Build) {
g.AddEdge(node, buildNode, BuildEdgeKind)
}
}
}
}
// AddAllBuildEdges adds build edges to all BuildConfig nodes in the given graph
func AddAllBuildEdges(g osgraph.MutableUniqueGraph) {
for _, node := range g.(graph.Graph).Nodes() {
if bcNode, ok := node.(*buildgraph.BuildConfigNode); ok {
AddBuildEdges(g, bcNode)
}
}
}
func imageRefNode(g osgraph.MutableUniqueGraph, ref *kapi.ObjectReference, bc *buildapi.BuildConfig) graph.Node {
if ref == nil {
return nil
}
switch ref.Kind {
case "DockerImage":
if ref, err := imageapi.ParseDockerImageReference(ref.Name); err == nil {
tag := ref.Tag
ref.Tag = ""
return imagegraph.EnsureDockerRepositoryNode(g, ref.String(), tag)
}
case "ImageStream":
return imagegraph.FindOrCreateSyntheticImageStreamTagNode(g, imagegraph.MakeImageStreamTagObjectMeta(defaultNamespace(ref.Namespace, bc.Namespace), ref.Name, imageapi.DefaultImageTag))
case "ImageStreamTag":
return imagegraph.FindOrCreateSyntheticImageStreamTagNode(g, imagegraph.MakeImageStreamTagObjectMeta2(defaultNamespace(ref.Namespace, bc.Namespace), ref.Name))
case "ImageStreamImage":
return imagegraph.FindOrCreateSyntheticImageStreamImageNode(g, imagegraph.MakeImageStreamImageObjectMeta(defaultNamespace(ref.Namespace, bc.Namespace), ref.Name))
}
return nil
}
// AddOutputEdges links the build config to its output image node.
func AddOutputEdges(g osgraph.MutableUniqueGraph, node *buildgraph.BuildConfigNode) {
if node.BuildConfig.Spec.Output.To == nil {
return
}
out := imageRefNode(g, node.BuildConfig.Spec.Output.To, node.BuildConfig)
g.AddEdge(node, out, BuildOutputEdgeKind)
}
// AddInputEdges links the build config to its input image and source nodes.
func AddInputEdges(g osgraph.MutableUniqueGraph, node *buildgraph.BuildConfigNode) {
if in := buildgraph.EnsureSourceRepositoryNode(g, node.BuildConfig.Spec.Source); in != nil {
g.AddEdge(in, node, BuildInputEdgeKind)
}
inputImage := buildutil.GetInputReference(node.BuildConfig.Spec.Strategy)
if input := imageRefNode(g, inputImage, node.BuildConfig); input != nil {
g.AddEdge(input, node, BuildInputImageEdgeKind)
}
}
// AddTriggerEdges links the build config to its trigger input image nodes.
func AddTriggerEdges(g osgraph.MutableUniqueGraph, node *buildgraph.BuildConfigNode) {
for _, trigger := range node.BuildConfig.Spec.Triggers {
if trigger.Type != buildapi.ImageChangeBuildTriggerType {
continue
}
from := trigger.ImageChange.From
if trigger.ImageChange.From == nil {
from = buildutil.GetInputReference(node.BuildConfig.Spec.Strategy)
}
triggerNode := imageRefNode(g, from, node.BuildConfig)
g.AddEdge(triggerNode, node, BuildTriggerImageEdgeKind)
}
}
// AddInputOutputEdges links the build config to other nodes for the images and source repositories it depends on.
func AddInputOutputEdges(g osgraph.MutableUniqueGraph, node *buildgraph.BuildConfigNode) *buildgraph.BuildConfigNode {
AddInputEdges(g, node)
AddTriggerEdges(g, node)
AddOutputEdges(g, node)
return node
}
// AddAllInputOutputEdges adds input and output edges for all BuildConfigs in the given graph
func AddAllInputOutputEdges(g osgraph.MutableUniqueGraph) {
for _, node := range g.(graph.Graph).Nodes() {
if bcNode, ok := node.(*buildgraph.BuildConfigNode); ok {
AddInputOutputEdges(g, bcNode)
}
}
}
+111
View File
@@ -0,0 +1,111 @@
package graph
import (
"sort"
"github.com/gonum/graph"
osgraph "github.com/openshift/origin/pkg/api/graph"
buildapi "github.com/openshift/origin/pkg/build/api"
buildgraph "github.com/openshift/origin/pkg/build/graph/nodes"
)
// RelevantBuilds returns the lastSuccessful build, lastUnsuccessful build, and a list of active builds
func RelevantBuilds(g osgraph.Graph, bcNode *buildgraph.BuildConfigNode) (*buildgraph.BuildNode, *buildgraph.BuildNode, []*buildgraph.BuildNode) {
var (
lastSuccessfulBuild *buildgraph.BuildNode
lastUnsuccessfulBuild *buildgraph.BuildNode
)
activeBuilds := []*buildgraph.BuildNode{}
allBuilds := []*buildgraph.BuildNode{}
uncastBuilds := g.SuccessorNodesByEdgeKind(bcNode, BuildEdgeKind)
for i := range uncastBuilds {
buildNode := uncastBuilds[i].(*buildgraph.BuildNode)
if belongsToBuildConfig(bcNode.BuildConfig, buildNode.Build) {
allBuilds = append(allBuilds, buildNode)
}
}
if len(allBuilds) == 0 {
return nil, nil, []*buildgraph.BuildNode{}
}
sort.Sort(RecentBuildReferences(allBuilds))
for i := range allBuilds {
switch allBuilds[i].Build.Status.Phase {
case buildapi.BuildPhaseComplete:
if lastSuccessfulBuild == nil {
lastSuccessfulBuild = allBuilds[i]
}
case buildapi.BuildPhaseFailed, buildapi.BuildPhaseCancelled, buildapi.BuildPhaseError:
if lastUnsuccessfulBuild == nil {
lastUnsuccessfulBuild = allBuilds[i]
}
default:
activeBuilds = append(activeBuilds, allBuilds[i])
}
}
return lastSuccessfulBuild, lastUnsuccessfulBuild, activeBuilds
}
func belongsToBuildConfig(config *buildapi.BuildConfig, b *buildapi.Build) bool {
if b.Labels == nil {
return false
}
if b.Annotations != nil && b.Annotations[buildapi.BuildConfigAnnotation] == config.Name {
return true
}
if b.Labels[buildapi.BuildConfigLabel] == config.Name {
return true
}
if b.Labels[buildapi.BuildConfigLabelDeprecated] == config.Name {
return true
}
return false
}
type RecentBuildReferences []*buildgraph.BuildNode
func (m RecentBuildReferences) Len() int { return len(m) }
func (m RecentBuildReferences) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
func (m RecentBuildReferences) Less(i, j int) bool {
return m[i].Build.CreationTimestamp.After(m[j].Build.CreationTimestamp.Time)
}
func defaultNamespace(value, defaultValue string) string {
if len(value) == 0 {
return defaultValue
}
return value
}
// BuildConfigsForTag returns the buildConfig that points to the provided imageStreamTag.
func BuildConfigsForTag(g osgraph.Graph, istag graph.Node) []*buildgraph.BuildConfigNode {
bcs := []*buildgraph.BuildConfigNode{}
for _, bcNode := range g.PredecessorNodesByEdgeKind(istag, BuildOutputEdgeKind) {
bcs = append(bcs, bcNode.(*buildgraph.BuildConfigNode))
}
return bcs
}
// GetLatestBuild returns the latest build for the provided buildConfig.
func GetLatestBuild(g osgraph.Graph, bc graph.Node) *buildgraph.BuildNode {
builds := g.SuccessorNodesByEdgeKind(bc, BuildEdgeKind)
if len(builds) == 0 {
return nil
}
latestBuild := builds[0].(*buildgraph.BuildNode)
for _, buildNode := range builds[1:] {
if build, ok := buildNode.(*buildgraph.BuildNode); ok {
if latestBuild.Build.CreationTimestamp.Before(build.Build.CreationTimestamp) {
latestBuild = build
}
}
}
return latestBuild
}
+47
View File
@@ -0,0 +1,47 @@
package nodes
import (
"github.com/gonum/graph"
osgraph "github.com/openshift/origin/pkg/api/graph"
buildapi "github.com/openshift/origin/pkg/build/api"
)
// EnsureBuildConfigNode adds a graph node for the specific build config if it does not exist
func EnsureBuildConfigNode(g osgraph.MutableUniqueGraph, config *buildapi.BuildConfig) *BuildConfigNode {
return osgraph.EnsureUnique(
g,
BuildConfigNodeName(config),
func(node osgraph.Node) graph.Node {
return &BuildConfigNode{
Node: node,
BuildConfig: config,
}
},
).(*BuildConfigNode)
}
// EnsureSourceRepositoryNode adds the specific BuildSource to the graph if it does not already exist.
func EnsureSourceRepositoryNode(g osgraph.MutableUniqueGraph, source buildapi.BuildSource) *SourceRepositoryNode {
switch {
case source.Git != nil:
default:
return nil
}
return osgraph.EnsureUnique(g,
SourceRepositoryNodeName(source),
func(node osgraph.Node) graph.Node {
return &SourceRepositoryNode{node, source}
},
).(*SourceRepositoryNode)
}
// EnsureBuildNode adds a graph node for the build if it does not already exist.
func EnsureBuildNode(g osgraph.MutableUniqueGraph, build *buildapi.Build) *BuildNode {
return osgraph.EnsureUnique(g,
BuildNodeName(build),
func(node osgraph.Node) graph.Node {
return &BuildNode{node, build}
},
).(*BuildNode)
}
+90
View File
@@ -0,0 +1,90 @@
package nodes
import (
"fmt"
"reflect"
osgraph "github.com/openshift/origin/pkg/api/graph"
buildapi "github.com/openshift/origin/pkg/build/api"
)
var (
BuildConfigNodeKind = reflect.TypeOf(buildapi.BuildConfig{}).Name()
BuildNodeKind = reflect.TypeOf(buildapi.Build{}).Name()
// non-api types
SourceRepositoryNodeKind = reflect.TypeOf(buildapi.BuildSource{}).Name()
)
func BuildConfigNodeName(o *buildapi.BuildConfig) osgraph.UniqueName {
return osgraph.GetUniqueRuntimeObjectNodeName(BuildConfigNodeKind, o)
}
type BuildConfigNode struct {
osgraph.Node
BuildConfig *buildapi.BuildConfig
}
func (n BuildConfigNode) Object() interface{} {
return n.BuildConfig
}
func (n BuildConfigNode) String() string {
return string(BuildConfigNodeName(n.BuildConfig))
}
func (n BuildConfigNode) UniqueName() osgraph.UniqueName {
return BuildConfigNodeName(n.BuildConfig)
}
func (*BuildConfigNode) Kind() string {
return BuildConfigNodeKind
}
func SourceRepositoryNodeName(source buildapi.BuildSource) osgraph.UniqueName {
switch {
case source.Git != nil:
sourceType, uri, ref := "git", source.Git.URI, source.Git.Ref
return osgraph.UniqueName(fmt.Sprintf("%s|%s|%s#%s", SourceRepositoryNodeKind, sourceType, uri, ref))
default:
panic(fmt.Sprintf("invalid build source: %v", source))
}
}
type SourceRepositoryNode struct {
osgraph.Node
Source buildapi.BuildSource
}
func (n SourceRepositoryNode) String() string {
return string(SourceRepositoryNodeName(n.Source))
}
func (SourceRepositoryNode) Kind() string {
return SourceRepositoryNodeKind
}
func BuildNodeName(o *buildapi.Build) osgraph.UniqueName {
return osgraph.GetUniqueRuntimeObjectNodeName(BuildNodeKind, o)
}
type BuildNode struct {
osgraph.Node
Build *buildapi.Build
}
func (n BuildNode) Object() interface{} {
return n.Build
}
func (n BuildNode) String() string {
return string(BuildNodeName(n.Build))
}
func (n BuildNode) UniqueName() osgraph.UniqueName {
return BuildNodeName(n.Build)
}
func (*BuildNode) Kind() string {
return BuildNodeKind
}
+3
View File
@@ -0,0 +1,3 @@
// Package util contains common functions that are used
// by the rest of the OpenShift build system.
package util
+169
View File
@@ -0,0 +1,169 @@
package util
import (
"fmt"
"strconv"
"strings"
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/labels"
"github.com/golang/glog"
buildapi "github.com/openshift/origin/pkg/build/api"
buildclient "github.com/openshift/origin/pkg/build/client"
)
const (
// NoBuildLogsMessage reports that no build logs are available
NoBuildLogsMessage = "No logs are available."
)
// GetBuildName returns name of the build pod.
func GetBuildName(pod *kapi.Pod) string {
if pod == nil {
return ""
}
return pod.Annotations[buildapi.BuildAnnotation]
}
// GetInputReference returns the From ObjectReference associated with the
// BuildStrategy.
func GetInputReference(strategy buildapi.BuildStrategy) *kapi.ObjectReference {
switch {
case strategy.SourceStrategy != nil:
return &strategy.SourceStrategy.From
case strategy.DockerStrategy != nil:
return strategy.DockerStrategy.From
case strategy.CustomStrategy != nil:
return &strategy.CustomStrategy.From
default:
return nil
}
}
// NameFromImageStream returns a concatenated name representing an ImageStream[Tag/Image]
// reference. If the reference does not contain a Namespace, the namespace parameter
// is used instead.
func NameFromImageStream(namespace string, ref *kapi.ObjectReference, tag string) string {
var ret string
if ref.Namespace == "" {
ret = namespace
} else {
ret = ref.Namespace
}
ret = ret + "/" + ref.Name
if tag != "" && strings.Index(ref.Name, ":") == -1 && strings.Index(ref.Name, "@") == -1 {
ret = ret + ":" + tag
}
return ret
}
// IsBuildComplete returns whether the provided build is complete or not
func IsBuildComplete(build *buildapi.Build) bool {
return build.Status.Phase != buildapi.BuildPhaseRunning && build.Status.Phase != buildapi.BuildPhasePending && build.Status.Phase != buildapi.BuildPhaseNew
}
// IsPaused returns true if the provided BuildConfig is paused and cannot be used to create a new Build
func IsPaused(bc *buildapi.BuildConfig) bool {
return strings.ToLower(bc.Annotations[buildapi.BuildConfigPausedAnnotation]) == "true"
}
// BuildNumber returns the given build number.
func BuildNumber(build *buildapi.Build) (int64, error) {
annotations := build.GetAnnotations()
if stringNumber, ok := annotations[buildapi.BuildNumberAnnotation]; ok {
return strconv.ParseInt(stringNumber, 10, 64)
}
return 0, fmt.Errorf("build %s/%s does not have %s annotation", build.Namespace, build.Name, buildapi.BuildNumberAnnotation)
}
// BuildRunPolicy returns the scheduling policy for the build based on the
// "queued" label.
func BuildRunPolicy(build *buildapi.Build) buildapi.BuildRunPolicy {
labels := build.GetLabels()
if value, found := labels[buildapi.BuildRunPolicyLabel]; found {
switch value {
case "Parallel":
return buildapi.BuildRunPolicyParallel
case "Serial":
return buildapi.BuildRunPolicySerial
case "SerialLatestOnly":
return buildapi.BuildRunPolicySerialLatestOnly
}
}
glog.V(5).Infof("Build %s/%s does not have start policy label set, using default (Serial)")
return buildapi.BuildRunPolicySerial
}
// BuildNameForConfigVersion returns the name of the version-th build
// for the config that has the provided name.
func BuildNameForConfigVersion(name string, version int) string {
return fmt.Sprintf("%s-%d", name, version)
}
// BuildConfigSelector returns a label Selector which can be used to find all
// builds for a BuildConfig.
func BuildConfigSelector(name string) labels.Selector {
return labels.Set{buildapi.BuildConfigLabel: buildapi.LabelValue(name)}.AsSelector()
}
// BuildConfigSelectorDeprecated returns a label Selector which can be used to find
// all builds for a BuildConfig that use the deprecated labels.
func BuildConfigSelectorDeprecated(name string) labels.Selector {
return labels.Set{buildapi.BuildConfigLabelDeprecated: name}.AsSelector()
}
type buildFilter func(buildapi.Build) bool
// BuildConfigBuilds return a list of builds for the given build config.
// Optionally you can specify a filter function to select only builds that
// matches your criteria.
func BuildConfigBuilds(c buildclient.BuildLister, namespace, name string, filterFunc buildFilter) (*buildapi.BuildList, error) {
result, err := c.List(namespace, kapi.ListOptions{
LabelSelector: BuildConfigSelector(name),
})
if err != nil {
return nil, err
}
if filterFunc == nil {
return result, nil
}
filteredList := &buildapi.BuildList{TypeMeta: result.TypeMeta, ListMeta: result.ListMeta}
for _, b := range result.Items {
if filterFunc(b) {
filteredList.Items = append(filteredList.Items, b)
}
}
return filteredList, nil
}
// ConfigNameForBuild returns the name of the build config from a
// build name.
func ConfigNameForBuild(build *buildapi.Build) string {
if build == nil {
return ""
}
if build.Annotations != nil {
if _, exists := build.Annotations[buildapi.BuildConfigAnnotation]; exists {
return build.Annotations[buildapi.BuildConfigAnnotation]
}
}
if _, exists := build.Labels[buildapi.BuildConfigLabel]; exists {
return build.Labels[buildapi.BuildConfigLabel]
}
return build.Labels[buildapi.BuildConfigLabelDeprecated]
}
// VersionForBuild returns the version from the provided build name.
// If no version can be found, 0 is returned to indicate no version.
func VersionForBuild(build *buildapi.Build) int {
if build == nil {
return 0
}
versionString := build.Annotations[buildapi.BuildNumberAnnotation]
version, err := strconv.Atoi(versionString)
if err != nil {
return 0
}
return version
}
@@ -0,0 +1,46 @@
package client
import (
kapi "k8s.io/kubernetes/pkg/api"
quotaapi "github.com/openshift/origin/pkg/quota/api"
)
// AppliedClusterResourceQuotasNamespacer has methods to work with AppliedClusterResourceQuota resources in a namespace
type AppliedClusterResourceQuotasNamespacer interface {
AppliedClusterResourceQuotas(namespace string) AppliedClusterResourceQuotaInterface
}
// AppliedClusterResourceQuotaInterface exposes methods on AppliedClusterResourceQuota resources.
type AppliedClusterResourceQuotaInterface interface {
List(opts kapi.ListOptions) (*quotaapi.AppliedClusterResourceQuotaList, error)
Get(name string) (*quotaapi.AppliedClusterResourceQuota, error)
}
// appliedClusterResourceQuotas implements AppliedClusterResourceQuotasNamespacer interface
type appliedClusterResourceQuotas struct {
r *Client
ns string
}
// newAppliedClusterResourceQuotas returns a appliedClusterResourceQuotas
func newAppliedClusterResourceQuotas(c *Client, namespace string) *appliedClusterResourceQuotas {
return &appliedClusterResourceQuotas{
r: c,
ns: namespace,
}
}
// List returns a list of appliedClusterResourceQuotas that match the label and field selectors.
func (c *appliedClusterResourceQuotas) List(opts kapi.ListOptions) (result *quotaapi.AppliedClusterResourceQuotaList, err error) {
result = &quotaapi.AppliedClusterResourceQuotaList{}
err = c.r.Get().Namespace(c.ns).Resource("appliedclusterresourcequotas").VersionedParams(&opts, kapi.ParameterCodec).Do().Into(result)
return
}
// Get returns information about a particular appliedClusterResourceQuota and error if one occurs.
func (c *appliedClusterResourceQuotas) Get(name string) (result *quotaapi.AppliedClusterResourceQuota, err error) {
result = &quotaapi.AppliedClusterResourceQuota{}
err = c.r.Get().Namespace(c.ns).Resource("appliedclusterresourcequotas").Name(name).Do().Into(result)
return
}
+132
View File
@@ -0,0 +1,132 @@
package client
import (
"fmt"
"io"
"net/url"
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/watch"
buildapi "github.com/openshift/origin/pkg/build/api"
)
// ErrTriggerIsNotAWebHook is returned when a webhook URL is requested for a trigger
// that is not a webhook type.
var ErrTriggerIsNotAWebHook = fmt.Errorf("the specified trigger is not a webhook")
// BuildConfigsNamespacer has methods to work with BuildConfig resources in a namespace
type BuildConfigsNamespacer interface {
BuildConfigs(namespace string) BuildConfigInterface
}
// BuildConfigInterface exposes methods on BuildConfig resources
type BuildConfigInterface interface {
List(opts kapi.ListOptions) (*buildapi.BuildConfigList, error)
Get(name string) (*buildapi.BuildConfig, error)
Create(config *buildapi.BuildConfig) (*buildapi.BuildConfig, error)
Update(config *buildapi.BuildConfig) (*buildapi.BuildConfig, error)
Delete(name string) error
Watch(opts kapi.ListOptions) (watch.Interface, error)
Instantiate(request *buildapi.BuildRequest) (result *buildapi.Build, err error)
InstantiateBinary(request *buildapi.BinaryBuildRequestOptions, r io.Reader) (result *buildapi.Build, err error)
WebHookURL(name string, trigger *buildapi.BuildTriggerPolicy) (*url.URL, error)
}
// buildConfigs implements BuildConfigsNamespacer interface
type buildConfigs struct {
r *Client
ns string
}
// newBuildConfigs returns a buildConfigs
func newBuildConfigs(c *Client, namespace string) *buildConfigs {
return &buildConfigs{
r: c,
ns: namespace,
}
}
// List returns a list of buildconfigs that match the label and field selectors.
func (c *buildConfigs) List(opts kapi.ListOptions) (result *buildapi.BuildConfigList, err error) {
result = &buildapi.BuildConfigList{}
err = c.r.Get().
Namespace(c.ns).
Resource("buildConfigs").
VersionedParams(&opts, kapi.ParameterCodec).
Do().
Into(result)
return
}
// Get returns information about a particular buildconfig and error if one occurs.
func (c *buildConfigs) Get(name string) (result *buildapi.BuildConfig, err error) {
result = &buildapi.BuildConfig{}
err = c.r.Get().Namespace(c.ns).Resource("buildConfigs").Name(name).Do().Into(result)
return
}
// WebHookURL returns the URL for the provided build config name and trigger policy, or ErrTriggerIsNotAWebHook
// if the trigger is not a webhook type.
func (c *buildConfigs) WebHookURL(name string, trigger *buildapi.BuildTriggerPolicy) (*url.URL, error) {
switch {
case trigger.GenericWebHook != nil:
return c.r.Get().Namespace(c.ns).Resource("buildConfigs").Name(name).SubResource("webhooks").Suffix(trigger.GenericWebHook.Secret, "generic").URL(), nil
case trigger.GitHubWebHook != nil:
return c.r.Get().Namespace(c.ns).Resource("buildConfigs").Name(name).SubResource("webhooks").Suffix(trigger.GitHubWebHook.Secret, "github").URL(), nil
default:
return nil, ErrTriggerIsNotAWebHook
}
}
// Create creates a new buildconfig. Returns the server's representation of the buildconfig and error if one occurs.
func (c *buildConfigs) Create(build *buildapi.BuildConfig) (result *buildapi.BuildConfig, err error) {
result = &buildapi.BuildConfig{}
err = c.r.Post().Namespace(c.ns).Resource("buildConfigs").Body(build).Do().Into(result)
return
}
// Update updates the buildconfig on server. Returns the server's representation of the buildconfig and error if one occurs.
func (c *buildConfigs) Update(build *buildapi.BuildConfig) (result *buildapi.BuildConfig, err error) {
result = &buildapi.BuildConfig{}
err = c.r.Put().Namespace(c.ns).Resource("buildConfigs").Name(build.Name).Body(build).Do().Into(result)
return
}
// Delete deletes a BuildConfig, returns error if one occurs.
func (c *buildConfigs) Delete(name string) error {
return c.r.Delete().Namespace(c.ns).Resource("buildConfigs").Name(name).Do().Error()
}
// Watch returns a watch.Interface that watches the requested buildConfigs.
func (c *buildConfigs) Watch(opts kapi.ListOptions) (watch.Interface, error) {
return c.r.Get().
Prefix("watch").
Namespace(c.ns).
Resource("buildConfigs").
VersionedParams(&opts, kapi.ParameterCodec).
Watch()
}
// Instantiate instantiates a new build from build config returning new object or an error
func (c *buildConfigs) Instantiate(request *buildapi.BuildRequest) (result *buildapi.Build, err error) {
result = &buildapi.Build{}
err = c.r.Post().Namespace(c.ns).Resource("buildConfigs").Name(request.Name).SubResource("instantiate").Body(request).Do().Into(result)
return
}
// InstantiateBinary instantiates a new build from a build config, given a structured request and an input stream,
// and returns the created build or an error.
func (c *buildConfigs) InstantiateBinary(request *buildapi.BinaryBuildRequestOptions, r io.Reader) (result *buildapi.Build, err error) {
result = &buildapi.Build{}
err = c.r.Post().
Namespace(c.ns).
Resource("buildConfigs").
Name(request.Name).
SubResource("instantiatebinary").
VersionedParams(request, kapi.ParameterCodec).
Body(r).Do().Into(result)
return
}
+37
View File
@@ -0,0 +1,37 @@
package client
import (
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/client/restclient"
api "github.com/openshift/origin/pkg/build/api"
)
// BuildLogsNamespacer has methods to work with BuildLogs resources in a namespace
type BuildLogsNamespacer interface {
BuildLogs(namespace string) BuildLogsInterface
}
// BuildLogsInterface exposes methods on BuildLogs resources.
type BuildLogsInterface interface {
Get(name string, opts api.BuildLogOptions) *restclient.Request
}
// buildLogs implements BuildLogsNamespacer interface
type buildLogs struct {
r *Client
ns string
}
// newBuildLogs returns a buildLogs
func newBuildLogs(c *Client, namespace string) *buildLogs {
return &buildLogs{
r: c,
ns: namespace,
}
}
// Get builds and returns a buildLog request
func (c *buildLogs) Get(name string, opts api.BuildLogOptions) *restclient.Request {
return c.r.Get().Namespace(c.ns).Resource("builds").Name(name).SubResource("log").VersionedParams(&opts, kapi.ParameterCodec)
}
+104
View File
@@ -0,0 +1,104 @@
package client
import (
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/watch"
buildapi "github.com/openshift/origin/pkg/build/api"
)
// BuildsNamespacer has methods to work with Build resources in a namespace
type BuildsNamespacer interface {
Builds(namespace string) BuildInterface
}
// BuildInterface exposes methods on Build resources.
type BuildInterface interface {
List(opts kapi.ListOptions) (*buildapi.BuildList, error)
Get(name string) (*buildapi.Build, error)
Create(build *buildapi.Build) (*buildapi.Build, error)
Update(build *buildapi.Build) (*buildapi.Build, error)
Delete(name string) error
Watch(opts kapi.ListOptions) (watch.Interface, error)
Clone(request *buildapi.BuildRequest) (*buildapi.Build, error)
UpdateDetails(build *buildapi.Build) (*buildapi.Build, error)
}
// builds implements BuildsNamespacer interface
type builds struct {
r *Client
ns string
}
// newBuilds returns a builds
func newBuilds(c *Client, namespace string) *builds {
return &builds{
r: c,
ns: namespace,
}
}
// List returns a list of builds that match the label and field selectors.
func (c *builds) List(opts kapi.ListOptions) (result *buildapi.BuildList, err error) {
result = &buildapi.BuildList{}
err = c.r.Get().
Namespace(c.ns).
Resource("builds").
VersionedParams(&opts, kapi.ParameterCodec).
Do().
Into(result)
return
}
// Get returns information about a particular build and error if one occurs.
func (c *builds) Get(name string) (result *buildapi.Build, err error) {
result = &buildapi.Build{}
err = c.r.Get().Namespace(c.ns).Resource("builds").Name(name).Do().Into(result)
return
}
// Create creates new build. Returns the server's representation of the build and error if one occurs.
func (c *builds) Create(build *buildapi.Build) (result *buildapi.Build, err error) {
result = &buildapi.Build{}
err = c.r.Post().Namespace(c.ns).Resource("builds").Body(build).Do().Into(result)
return
}
// Update updates the build on server. Returns the server's representation of the build and error if one occurs.
func (c *builds) Update(build *buildapi.Build) (result *buildapi.Build, err error) {
result = &buildapi.Build{}
err = c.r.Put().Namespace(c.ns).Resource("builds").Name(build.Name).Body(build).Do().Into(result)
return
}
// Delete deletes a build, returns error if one occurs.
func (c *builds) Delete(name string) (err error) {
err = c.r.Delete().Namespace(c.ns).Resource("builds").Name(name).Do().Error()
return
}
// Watch returns a watch.Interface that watches the requested builds
func (c *builds) Watch(opts kapi.ListOptions) (watch.Interface, error) {
return c.r.Get().
Prefix("watch").
Namespace(c.ns).
Resource("builds").
VersionedParams(&opts, kapi.ParameterCodec).
Watch()
}
// Clone creates a clone of a build returning new object or an error
func (c *builds) Clone(request *buildapi.BuildRequest) (result *buildapi.Build, err error) {
result = &buildapi.Build{}
err = c.r.Post().Namespace(c.ns).Resource("builds").Name(request.Name).SubResource("clone").Body(request).Do().Into(result)
return
}
// UpdateDetails updates the build details for a given build.
// Currently only the Spec.Revision is allowed to be updated.
// Returns the server's representation of the build and error if one occurs.
func (c *builds) UpdateDetails(build *buildapi.Build) (result *buildapi.Build, err error) {
result = &buildapi.Build{}
err = c.r.Put().Namespace(c.ns).Resource("builds").Name(build.Name).SubResource("details").Body(build).Do().Into(result)
return
}
+367
View File
@@ -0,0 +1,367 @@
package client
import (
"fmt"
"os"
"path"
"runtime"
"strings"
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/client/restclient"
"k8s.io/kubernetes/pkg/client/typed/discovery"
"github.com/openshift/origin/pkg/api/latest"
"github.com/openshift/origin/pkg/version"
)
// Interface exposes methods on OpenShift resources.
type Interface interface {
BuildsNamespacer
BuildConfigsNamespacer
BuildLogsNamespacer
ImagesInterfacer
ImageSignaturesInterfacer
ImageStreamsNamespacer
ImageStreamMappingsNamespacer
ImageStreamTagsNamespacer
ImageStreamImagesNamespacer
ImageStreamSecretsNamespacer
DeploymentConfigsNamespacer
DeploymentLogsNamespacer
RoutesNamespacer
HostSubnetsInterface
NetNamespacesInterface
ClusterNetworkingInterface
EgressNetworkPoliciesNamespacer
IdentitiesInterface
UsersInterface
GroupsInterface
UserIdentityMappingsInterface
ProjectsInterface
ProjectRequestsInterface
LocalSubjectAccessReviewsImpersonator
SubjectAccessReviewsImpersonator
LocalResourceAccessReviewsNamespacer
ResourceAccessReviews
SubjectAccessReviews
LocalSubjectAccessReviewsNamespacer
SelfSubjectRulesReviewsNamespacer
TemplatesNamespacer
TemplateConfigsNamespacer
OAuthClientsInterface
OAuthClientAuthorizationsInterface
OAuthAccessTokensInterface
OAuthAuthorizeTokensInterface
PoliciesNamespacer
PolicyBindingsNamespacer
RolesNamespacer
RoleBindingsNamespacer
ClusterPoliciesInterface
ClusterPolicyBindingsInterface
ClusterRolesInterface
ClusterRoleBindingsInterface
ClusterResourceQuotasInterface
AppliedClusterResourceQuotasNamespacer
}
// Builds provides a REST client for Builds
func (c *Client) Builds(namespace string) BuildInterface {
return newBuilds(c, namespace)
}
// BuildConfigs provides a REST client for BuildConfigs
func (c *Client) BuildConfigs(namespace string) BuildConfigInterface {
return newBuildConfigs(c, namespace)
}
// BuildLogs provides a REST client for BuildLogs
func (c *Client) BuildLogs(namespace string) BuildLogsInterface {
return newBuildLogs(c, namespace)
}
// Images provides a REST client for Images
func (c *Client) Images() ImageInterface {
return newImages(c)
}
// ImageSignatures provides a REST client for ImageSignatures
func (c *Client) ImageSignatures() ImageSignatureInterface {
return newImageSignatures(c)
}
// ImageStreamImages provides a REST client for retrieving image secrets in a namespace
func (c *Client) ImageStreamSecrets(namespace string) ImageStreamSecretInterface {
return newImageStreamSecrets(c, namespace)
}
// ImageStreams provides a REST client for ImageStream
func (c *Client) ImageStreams(namespace string) ImageStreamInterface {
return newImageStreams(c, namespace)
}
// ImageStreamMappings provides a REST client for ImageStreamMapping
func (c *Client) ImageStreamMappings(namespace string) ImageStreamMappingInterface {
return newImageStreamMappings(c, namespace)
}
// ImageStreamTags provides a REST client for ImageStreamTag
func (c *Client) ImageStreamTags(namespace string) ImageStreamTagInterface {
return newImageStreamTags(c, namespace)
}
// ImageStreamImages provides a REST client for ImageStreamImage
func (c *Client) ImageStreamImages(namespace string) ImageStreamImageInterface {
return newImageStreamImages(c, namespace)
}
// DeploymentConfigs provides a REST client for DeploymentConfig
func (c *Client) DeploymentConfigs(namespace string) DeploymentConfigInterface {
return newDeploymentConfigs(c, namespace)
}
// DeploymentLogs provides a REST client for DeploymentLog
func (c *Client) DeploymentLogs(namespace string) DeploymentLogInterface {
return newDeploymentLogs(c, namespace)
}
// Routes provides a REST client for Route
func (c *Client) Routes(namespace string) RouteInterface {
return newRoutes(c, namespace)
}
// HostSubnets provides a REST client for HostSubnet
func (c *Client) HostSubnets() HostSubnetInterface {
return newHostSubnet(c)
}
// NetNamespaces provides a REST client for NetNamespace
func (c *Client) NetNamespaces() NetNamespaceInterface {
return newNetNamespace(c)
}
// ClusterNetwork provides a REST client for ClusterNetworking
func (c *Client) ClusterNetwork() ClusterNetworkInterface {
return newClusterNetwork(c)
}
// EgressNetworkPolicies provides a REST client for EgressNetworkPolicy
func (c *Client) EgressNetworkPolicies(namespace string) EgressNetworkPolicyInterface {
return newEgressNetworkPolicy(c, namespace)
}
// Users provides a REST client for User
func (c *Client) Users() UserInterface {
return newUsers(c)
}
// Identities provides a REST client for Identity
func (c *Client) Identities() IdentityInterface {
return newIdentities(c)
}
// UserIdentityMappings provides a REST client for UserIdentityMapping
func (c *Client) UserIdentityMappings() UserIdentityMappingInterface {
return newUserIdentityMappings(c)
}
// Groups provides a REST client for Groups
func (c *Client) Groups() GroupInterface {
return newGroups(c)
}
// Projects provides a REST client for Projects
func (c *Client) Projects() ProjectInterface {
return newProjects(c)
}
// ProjectRequests provides a REST client for Projects
func (c *Client) ProjectRequests() ProjectRequestInterface {
return newProjectRequests(c)
}
// TemplateConfigs provides a REST client for TemplateConfig
func (c *Client) TemplateConfigs(namespace string) TemplateConfigInterface {
return newTemplateConfigs(c, namespace)
}
// Templates provides a REST client for Templates
func (c *Client) Templates(namespace string) TemplateInterface {
return newTemplates(c, namespace)
}
// Policies provides a REST client for Policies
func (c *Client) Policies(namespace string) PolicyInterface {
return newPolicies(c, namespace)
}
// PolicyBindings provides a REST client for PolicyBindings
func (c *Client) PolicyBindings(namespace string) PolicyBindingInterface {
return newPolicyBindings(c, namespace)
}
// Roles provides a REST client for Roles
func (c *Client) Roles(namespace string) RoleInterface {
return newRoles(c, namespace)
}
// RoleBindings provides a REST client for RoleBindings
func (c *Client) RoleBindings(namespace string) RoleBindingInterface {
return newRoleBindings(c, namespace)
}
// LocalResourceAccessReviews provides a REST client for LocalResourceAccessReviews
func (c *Client) LocalResourceAccessReviews(namespace string) LocalResourceAccessReviewInterface {
return newLocalResourceAccessReviews(c, namespace)
}
// ClusterResourceAccessReviews provides a REST client for ClusterResourceAccessReviews
func (c *Client) ResourceAccessReviews() ResourceAccessReviewInterface {
return newResourceAccessReviews(c)
}
// ImpersonateSubjectAccessReviews provides a REST client for SubjectAccessReviews
func (c *Client) ImpersonateSubjectAccessReviews(token string) SubjectAccessReviewInterface {
return newImpersonatingSubjectAccessReviews(c, token)
}
// ImpersonateLocalSubjectAccessReviews provides a REST client for SubjectAccessReviews
func (c *Client) ImpersonateLocalSubjectAccessReviews(namespace, token string) LocalSubjectAccessReviewInterface {
return newImpersonatingLocalSubjectAccessReviews(c, namespace, token)
}
// LocalSubjectAccessReviews provides a REST client for LocalSubjectAccessReviews
func (c *Client) LocalSubjectAccessReviews(namespace string) LocalSubjectAccessReviewInterface {
return newLocalSubjectAccessReviews(c, namespace)
}
// SubjectAccessReviews provides a REST client for SubjectAccessReviews
func (c *Client) SubjectAccessReviews() SubjectAccessReviewInterface {
return newSubjectAccessReviews(c)
}
func (c *Client) SelfSubjectRulesReviews(namespace string) SelfSubjectRulesReviewInterface {
return newSelfSubjectRulesReviews(c, namespace)
}
func (c *Client) OAuthClients() OAuthClientInterface {
return newOAuthClients(c)
}
func (c *Client) OAuthClientAuthorizations() OAuthClientAuthorizationInterface {
return newOAuthClientAuthorizations(c)
}
func (c *Client) OAuthAccessTokens() OAuthAccessTokenInterface {
return newOAuthAccessTokens(c)
}
func (c *Client) OAuthAuthorizeTokens() OAuthAuthorizeTokenInterface {
return newOAuthAuthorizeTokens(c)
}
func (c *Client) ClusterPolicies() ClusterPolicyInterface {
return newClusterPolicies(c)
}
func (c *Client) ClusterPolicyBindings() ClusterPolicyBindingInterface {
return newClusterPolicyBindings(c)
}
func (c *Client) ClusterRoles() ClusterRoleInterface {
return newClusterRoles(c)
}
func (c *Client) ClusterRoleBindings() ClusterRoleBindingInterface {
return newClusterRoleBindings(c)
}
func (c *Client) ClusterResourceQuotas() ClusterResourceQuotaInterface {
return newClusterResourceQuotas(c)
}
func (c *Client) AppliedClusterResourceQuotas(namespace string) AppliedClusterResourceQuotaInterface {
return newAppliedClusterResourceQuotas(c, namespace)
}
// Client is an OpenShift client object
type Client struct {
*restclient.RESTClient
}
// New creates an OpenShift client for the given config. This client works with builds, deployments,
// templates, routes, and images. It allows operations such as list, get, update and delete on these
// objects. An error is returned if the provided configuration is not valid.
func New(c *restclient.Config) (*Client, error) {
config := *c
if err := SetOpenShiftDefaults(&config); err != nil {
return nil, err
}
client, err := restclient.RESTClientFor(&config)
if err != nil {
return nil, err
}
return &Client{client}, nil
}
// DiscoveryClient returns a discovery client.
func (c *Client) Discovery() discovery.DiscoveryInterface {
d := NewDiscoveryClient(c.RESTClient)
return d
}
// SetOpenShiftDefaults sets the default settings on the passed
// client configuration
func SetOpenShiftDefaults(config *restclient.Config) error {
if len(config.UserAgent) == 0 {
config.UserAgent = DefaultOpenShiftUserAgent()
}
if config.GroupVersion == nil {
// Clients default to the preferred code API version
groupVersionCopy := latest.Version
config.GroupVersion = &groupVersionCopy
}
if config.APIPath == "" {
config.APIPath = "/oapi"
}
if config.NegotiatedSerializer == nil {
config.NegotiatedSerializer = kapi.Codecs
}
return nil
}
// NewOrDie creates an OpenShift client and panics if the provided API version is not recognized.
func NewOrDie(c *restclient.Config) *Client {
client, err := New(c)
if err != nil {
panic(err)
}
return client
}
// DefaultOpenShiftUserAgent returns the default user agent that clients can use.
func DefaultOpenShiftUserAgent() string {
commit := version.Get().GitCommit
if len(commit) > 7 {
commit = commit[:7]
}
if len(commit) == 0 {
commit = "unknown"
}
version := version.Get().GitVersion
seg := strings.SplitN(version, "-", 2)
version = seg[0]
return fmt.Sprintf("%s/%s (%s/%s) openshift/%s", path.Base(os.Args[0]), version, runtime.GOOS, runtime.GOARCH, commit)
}
// IsStatusErrorKind returns true if this error describes the provided kind.
func IsStatusErrorKind(err error, kind string) bool {
if s, ok := err.(errors.APIStatus); ok {
if details := s.Status().Details; details != nil {
return kind == details.Kind
}
}
return false
}
+73
View File
@@ -0,0 +1,73 @@
package client
import (
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/watch"
quotaapi "github.com/openshift/origin/pkg/quota/api"
)
type ClusterResourceQuotasInterface interface {
ClusterResourceQuotas() ClusterResourceQuotaInterface
}
type ClusterResourceQuotaInterface interface {
List(opts kapi.ListOptions) (*quotaapi.ClusterResourceQuotaList, error)
Get(name string) (*quotaapi.ClusterResourceQuota, error)
Create(resourceQuota *quotaapi.ClusterResourceQuota) (*quotaapi.ClusterResourceQuota, error)
Update(resourceQuota *quotaapi.ClusterResourceQuota) (*quotaapi.ClusterResourceQuota, error)
Delete(name string) error
Watch(opts kapi.ListOptions) (watch.Interface, error)
UpdateStatus(resourceQuota *quotaapi.ClusterResourceQuota) (*quotaapi.ClusterResourceQuota, error)
}
type clusterResourceQuotas struct {
r *Client
}
// newClusterResourceQuotas returns a clusterResourceQuotas
func newClusterResourceQuotas(c *Client) *clusterResourceQuotas {
return &clusterResourceQuotas{
r: c,
}
}
func (c *clusterResourceQuotas) List(opts kapi.ListOptions) (result *quotaapi.ClusterResourceQuotaList, err error) {
result = &quotaapi.ClusterResourceQuotaList{}
err = c.r.Get().Resource("clusterresourcequotas").VersionedParams(&opts, kapi.ParameterCodec).Do().Into(result)
return
}
func (c *clusterResourceQuotas) Get(name string) (result *quotaapi.ClusterResourceQuota, err error) {
result = &quotaapi.ClusterResourceQuota{}
err = c.r.Get().Resource("clusterresourcequotas").Name(name).Do().Into(result)
return
}
func (c *clusterResourceQuotas) Create(resourceQuota *quotaapi.ClusterResourceQuota) (result *quotaapi.ClusterResourceQuota, err error) {
result = &quotaapi.ClusterResourceQuota{}
err = c.r.Post().Resource("clusterresourcequotas").Body(resourceQuota).Do().Into(result)
return
}
func (c *clusterResourceQuotas) Update(resourceQuota *quotaapi.ClusterResourceQuota) (result *quotaapi.ClusterResourceQuota, err error) {
result = &quotaapi.ClusterResourceQuota{}
err = c.r.Put().Resource("clusterresourcequotas").Name(resourceQuota.Name).Body(resourceQuota).Do().Into(result)
return
}
func (c *clusterResourceQuotas) Delete(name string) (err error) {
err = c.r.Delete().Resource("clusterresourcequotas").Name(name).Do().Error()
return
}
func (c *clusterResourceQuotas) Watch(opts kapi.ListOptions) (watch.Interface, error) {
return c.r.Get().Prefix("watch").Resource("clusterresourcequotas").VersionedParams(&opts, kapi.ParameterCodec).Watch()
}
func (c *clusterResourceQuotas) UpdateStatus(resourceQuota *quotaapi.ClusterResourceQuota) (result *quotaapi.ClusterResourceQuota, err error) {
result = &quotaapi.ClusterResourceQuota{}
err = c.r.Put().Resource("clusterresourcequotas").Name(resourceQuota.Name).SubResource("status").Body(resourceQuota).Do().Into(result)
return
}
+50
View File
@@ -0,0 +1,50 @@
package client
import (
sdnapi "github.com/openshift/origin/pkg/sdn/api"
)
// ClusterNetworkingInterface has methods to work with ClusterNetwork resources
type ClusterNetworkingInterface interface {
ClusterNetwork() ClusterNetworkInterface
}
// ClusterNetworkInterface exposes methods on clusterNetwork resources.
type ClusterNetworkInterface interface {
Get(name string) (*sdnapi.ClusterNetwork, error)
Create(sub *sdnapi.ClusterNetwork) (*sdnapi.ClusterNetwork, error)
Update(sub *sdnapi.ClusterNetwork) (*sdnapi.ClusterNetwork, error)
}
// clusterNetwork implements ClusterNetworkInterface interface
type clusterNetwork struct {
r *Client
}
// newClusterNetwork returns a clusterNetwork
func newClusterNetwork(c *Client) *clusterNetwork {
return &clusterNetwork{
r: c,
}
}
// Get returns information about a particular network
func (c *clusterNetwork) Get(networkName string) (result *sdnapi.ClusterNetwork, err error) {
result = &sdnapi.ClusterNetwork{}
err = c.r.Get().Resource("clusterNetworks").Name(networkName).Do().Into(result)
return
}
// Create creates a new ClusterNetwork. Returns the server's representation of ClusterNetwork and error if one occurs.
func (c *clusterNetwork) Create(cn *sdnapi.ClusterNetwork) (result *sdnapi.ClusterNetwork, err error) {
result = &sdnapi.ClusterNetwork{}
err = c.r.Post().Resource("clusterNetworks").Body(cn).Do().Into(result)
return
}
// Update updates the ClusterNetwork on the server. Returns the server's representation of the ClusterNetwork and error if one occurs.
func (c *clusterNetwork) Update(cn *sdnapi.ClusterNetwork) (result *sdnapi.ClusterNetwork, err error) {
result = &sdnapi.ClusterNetwork{}
err = c.r.Put().Resource("clusterNetworks").Name(cn.Name).Body(cn).Do().Into(result)
return
}
+68
View File
@@ -0,0 +1,68 @@
package client
import (
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/watch"
authorizationapi "github.com/openshift/origin/pkg/authorization/api"
)
// ClusterPoliciesInterface has methods to work with ClusterPolicies resources in a namespace
type ClusterPoliciesInterface interface {
ClusterPolicies() ClusterPolicyInterface
}
// ClusterPolicyInterface exposes methods on ClusterPolicies resources
type ClusterPolicyInterface interface {
List(opts kapi.ListOptions) (*authorizationapi.ClusterPolicyList, error)
Get(name string) (*authorizationapi.ClusterPolicy, error)
Delete(name string) error
Watch(opts kapi.ListOptions) (watch.Interface, error)
}
type ClusterPoliciesListerInterface interface {
ClusterPolicies() ClusterPolicyLister
}
type ClusterPolicyLister interface {
List(options kapi.ListOptions) (*authorizationapi.ClusterPolicyList, error)
Get(name string) (*authorizationapi.ClusterPolicy, error)
}
type SyncedClusterPoliciesListerInterface interface {
ClusterPoliciesListerInterface
LastSyncResourceVersion() string
}
type clusterPolicies struct {
r *Client
}
func newClusterPolicies(c *Client) *clusterPolicies {
return &clusterPolicies{
r: c,
}
}
// List returns a list of policies that match the label and field selectors.
func (c *clusterPolicies) List(opts kapi.ListOptions) (result *authorizationapi.ClusterPolicyList, err error) {
result = &authorizationapi.ClusterPolicyList{}
err = c.r.Get().Resource("clusterPolicies").VersionedParams(&opts, kapi.ParameterCodec).Do().Into(result)
return
}
// Get returns information about a particular policy and error if one occurs.
func (c *clusterPolicies) Get(name string) (result *authorizationapi.ClusterPolicy, err error) {
result = &authorizationapi.ClusterPolicy{}
err = c.r.Get().Resource("clusterPolicies").Name(name).Do().Into(result)
return
}
// Delete deletes a policy, returns error if one occurs.
func (c *clusterPolicies) Delete(name string) (err error) {
err = c.r.Delete().Resource("clusterPolicies").Name(name).Do().Error()
return
}
// Watch returns a watch.Interface that watches the requested clusterPolicies
func (c *clusterPolicies) Watch(opts kapi.ListOptions) (watch.Interface, error) {
return c.r.Get().Prefix("watch").Resource("clusterPolicies").VersionedParams(&opts, kapi.ParameterCodec).Watch()
}
+77
View File
@@ -0,0 +1,77 @@
package client
import (
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/watch"
authorizationapi "github.com/openshift/origin/pkg/authorization/api"
)
// ClusterPolicyBindingsInterface has methods to work with ClusterPolicyBindings resources in a namespace
type ClusterPolicyBindingsInterface interface {
ClusterPolicyBindings() ClusterPolicyBindingInterface
}
// ClusterPolicyBindingInterface exposes methods on ClusterPolicyBindings resources
type ClusterPolicyBindingInterface interface {
List(opts kapi.ListOptions) (*authorizationapi.ClusterPolicyBindingList, error)
Get(name string) (*authorizationapi.ClusterPolicyBinding, error)
Create(policyBinding *authorizationapi.ClusterPolicyBinding) (*authorizationapi.ClusterPolicyBinding, error)
Delete(name string) error
Watch(opts kapi.ListOptions) (watch.Interface, error)
}
type ClusterPolicyBindingsListerInterface interface {
ClusterPolicyBindings() ClusterPolicyBindingLister
}
type ClusterPolicyBindingLister interface {
List(options kapi.ListOptions) (*authorizationapi.ClusterPolicyBindingList, error)
Get(name string) (*authorizationapi.ClusterPolicyBinding, error)
}
type SyncedClusterPolicyBindingsListerInterface interface {
ClusterPolicyBindingsListerInterface
LastSyncResourceVersion() string
}
type clusterPolicyBindings struct {
r *Client
}
// newClusterPolicyBindings returns a clusterPolicyBindings
func newClusterPolicyBindings(c *Client) *clusterPolicyBindings {
return &clusterPolicyBindings{
r: c,
}
}
// List returns a list of clusterPolicyBindings that match the label and field selectors.
func (c *clusterPolicyBindings) List(opts kapi.ListOptions) (result *authorizationapi.ClusterPolicyBindingList, err error) {
result = &authorizationapi.ClusterPolicyBindingList{}
err = c.r.Get().Resource("clusterPolicyBindings").VersionedParams(&opts, kapi.ParameterCodec).Do().Into(result)
return
}
// Get returns information about a particular clusterPolicyBindings and error if one occurs.
func (c *clusterPolicyBindings) Get(name string) (result *authorizationapi.ClusterPolicyBinding, err error) {
result = &authorizationapi.ClusterPolicyBinding{}
err = c.r.Get().Resource("clusterPolicyBindings").Name(name).Do().Into(result)
return
}
// Create creates new policyBinding. Returns the server's representation of the clusterPolicyBindings and error if one occurs.
func (c *clusterPolicyBindings) Create(policyBinding *authorizationapi.ClusterPolicyBinding) (result *authorizationapi.ClusterPolicyBinding, err error) {
result = &authorizationapi.ClusterPolicyBinding{}
err = c.r.Post().Resource("clusterPolicyBindings").Body(policyBinding).Do().Into(result)
return
}
// Delete deletes a policyBinding, returns error if one occurs.
func (c *clusterPolicyBindings) Delete(name string) (err error) {
err = c.r.Delete().Resource("clusterPolicyBindings").Name(name).Do().Error()
return
}
// Watch returns a watch.Interface that watches the requested clusterPolicyBindings
func (c *clusterPolicyBindings) Watch(opts kapi.ListOptions) (watch.Interface, error) {
return c.r.Get().Prefix("watch").Resource("clusterPolicyBindings").VersionedParams(&opts, kapi.ParameterCodec).Watch()
}
+66
View File
@@ -0,0 +1,66 @@
package client
import (
kapi "k8s.io/kubernetes/pkg/api"
authorizationapi "github.com/openshift/origin/pkg/authorization/api"
)
// ClusterRoleBindingsInterface has methods to work with ClusterRoleBindings resources in a namespace
type ClusterRoleBindingsInterface interface {
ClusterRoleBindings() ClusterRoleBindingInterface
}
// ClusterRoleBindingInterface exposes methods on ClusterRoleBindings resources
type ClusterRoleBindingInterface interface {
List(opts kapi.ListOptions) (*authorizationapi.ClusterRoleBindingList, error)
Get(name string) (*authorizationapi.ClusterRoleBinding, error)
Update(roleBinding *authorizationapi.ClusterRoleBinding) (*authorizationapi.ClusterRoleBinding, error)
Create(roleBinding *authorizationapi.ClusterRoleBinding) (*authorizationapi.ClusterRoleBinding, error)
Delete(name string) error
}
type clusterRoleBindings struct {
r *Client
}
// newClusterRoleBindings returns a clusterRoleBindings
func newClusterRoleBindings(c *Client) *clusterRoleBindings {
return &clusterRoleBindings{
r: c,
}
}
// List returns a list of clusterRoleBindings that match the label and field selectors.
func (c *clusterRoleBindings) List(opts kapi.ListOptions) (result *authorizationapi.ClusterRoleBindingList, err error) {
result = &authorizationapi.ClusterRoleBindingList{}
err = c.r.Get().Resource("clusterRoleBindings").VersionedParams(&opts, kapi.ParameterCodec).Do().Into(result)
return
}
// Get returns information about a particular roleBinding and error if one occurs.
func (c *clusterRoleBindings) Get(name string) (result *authorizationapi.ClusterRoleBinding, err error) {
result = &authorizationapi.ClusterRoleBinding{}
err = c.r.Get().Resource("clusterRoleBindings").Name(name).Do().Into(result)
return
}
// Create creates new roleBinding. Returns the server's representation of the roleBinding and error if one occurs.
func (c *clusterRoleBindings) Create(roleBinding *authorizationapi.ClusterRoleBinding) (result *authorizationapi.ClusterRoleBinding, err error) {
result = &authorizationapi.ClusterRoleBinding{}
err = c.r.Post().Resource("clusterRoleBindings").Body(roleBinding).Do().Into(result)
return
}
// Update updates the roleBinding on server. Returns the server's representation of the roleBinding and error if one occurs.
func (c *clusterRoleBindings) Update(roleBinding *authorizationapi.ClusterRoleBinding) (result *authorizationapi.ClusterRoleBinding, err error) {
result = &authorizationapi.ClusterRoleBinding{}
err = c.r.Put().Resource("clusterRoleBindings").Name(roleBinding.Name).Body(roleBinding).Do().Into(result)
return
}
// Delete deletes a roleBinding, returns error if one occurs.
func (c *clusterRoleBindings) Delete(name string) (err error) {
err = c.r.Delete().Resource("clusterRoleBindings").Name(name).Do().Error()
return
}
+66
View File
@@ -0,0 +1,66 @@
package client
import (
kapi "k8s.io/kubernetes/pkg/api"
authorizationapi "github.com/openshift/origin/pkg/authorization/api"
)
// ClusterRolesInterface has methods to work with ClusterRoles resources in a namespace
type ClusterRolesInterface interface {
ClusterRoles() ClusterRoleInterface
}
// ClusterRoleInterface exposes methods on ClusterRoles resources
type ClusterRoleInterface interface {
List(opts kapi.ListOptions) (*authorizationapi.ClusterRoleList, error)
Get(name string) (*authorizationapi.ClusterRole, error)
Create(role *authorizationapi.ClusterRole) (*authorizationapi.ClusterRole, error)
Update(role *authorizationapi.ClusterRole) (*authorizationapi.ClusterRole, error)
Delete(name string) error
}
type clusterRoles struct {
r *Client
}
// newClusterRoles returns a clusterRoles
func newClusterRoles(c *Client) *clusterRoles {
return &clusterRoles{
r: c,
}
}
// List returns a list of clusterRoles that match the label and field selectors.
func (c *clusterRoles) List(opts kapi.ListOptions) (result *authorizationapi.ClusterRoleList, err error) {
result = &authorizationapi.ClusterRoleList{}
err = c.r.Get().Resource("clusterRoles").VersionedParams(&opts, kapi.ParameterCodec).Do().Into(result)
return
}
// Get returns information about a particular role and error if one occurs.
func (c *clusterRoles) Get(name string) (result *authorizationapi.ClusterRole, err error) {
result = &authorizationapi.ClusterRole{}
err = c.r.Get().Resource("clusterRoles").Name(name).Do().Into(result)
return
}
// Create creates new role. Returns the server's representation of the role and error if one occurs.
func (c *clusterRoles) Create(role *authorizationapi.ClusterRole) (result *authorizationapi.ClusterRole, err error) {
result = &authorizationapi.ClusterRole{}
err = c.r.Post().Resource("clusterRoles").Body(role).Do().Into(result)
return
}
// Update updates the roleBinding on server. Returns the server's representation of the roleBinding and error if one occurs.
func (c *clusterRoles) Update(role *authorizationapi.ClusterRole) (result *authorizationapi.ClusterRole, err error) {
result = &authorizationapi.ClusterRole{}
err = c.r.Put().Resource("clusterRoles").Name(role.Name).Body(role).Do().Into(result)
return
}
// Delete deletes a role, returns error if one occurs.
func (c *clusterRoles) Delete(name string) (err error) {
err = c.r.Delete().Resource("clusterRoles").Name(name).Do().Error()
return
}
+176
View File
@@ -0,0 +1,176 @@
package client
import (
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
extensionsv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
kclient "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/watch"
deployapi "github.com/openshift/origin/pkg/deploy/api"
)
// DeploymentConfigsNamespacer has methods to work with DeploymentConfig resources in a namespace
type DeploymentConfigsNamespacer interface {
DeploymentConfigs(namespace string) DeploymentConfigInterface
}
// DeploymentConfigInterface contains methods for working with DeploymentConfigs
type DeploymentConfigInterface interface {
List(opts kapi.ListOptions) (*deployapi.DeploymentConfigList, error)
Get(name string) (*deployapi.DeploymentConfig, error)
Create(config *deployapi.DeploymentConfig) (*deployapi.DeploymentConfig, error)
Update(config *deployapi.DeploymentConfig) (*deployapi.DeploymentConfig, error)
Delete(name string) error
Watch(opts kapi.ListOptions) (watch.Interface, error)
Generate(name string) (*deployapi.DeploymentConfig, error)
Rollback(config *deployapi.DeploymentConfigRollback) (*deployapi.DeploymentConfig, error)
RollbackDeprecated(config *deployapi.DeploymentConfigRollback) (*deployapi.DeploymentConfig, error)
GetScale(name string) (*extensions.Scale, error)
UpdateScale(scale *extensions.Scale) (*extensions.Scale, error)
UpdateStatus(config *deployapi.DeploymentConfig) (*deployapi.DeploymentConfig, error)
}
// deploymentConfigs implements DeploymentConfigsNamespacer interface
type deploymentConfigs struct {
r *Client
ns string
}
// newDeploymentConfigs returns a deploymentConfigs
func newDeploymentConfigs(c *Client, namespace string) *deploymentConfigs {
return &deploymentConfigs{
r: c,
ns: namespace,
}
}
// List takes a label and field selectors, and returns the list of deploymentConfigs that match that selectors
func (c *deploymentConfigs) List(opts kapi.ListOptions) (result *deployapi.DeploymentConfigList, err error) {
result = &deployapi.DeploymentConfigList{}
err = c.r.Get().
Namespace(c.ns).
Resource("deploymentConfigs").
VersionedParams(&opts, kapi.ParameterCodec).
Do().
Into(result)
return
}
// Get returns information about a particular deploymentConfig
func (c *deploymentConfigs) Get(name string) (result *deployapi.DeploymentConfig, err error) {
result = &deployapi.DeploymentConfig{}
err = c.r.Get().Namespace(c.ns).Resource("deploymentConfigs").Name(name).Do().Into(result)
return
}
// Create creates a new deploymentConfig
func (c *deploymentConfigs) Create(deploymentConfig *deployapi.DeploymentConfig) (result *deployapi.DeploymentConfig, err error) {
result = &deployapi.DeploymentConfig{}
err = c.r.Post().Namespace(c.ns).Resource("deploymentConfigs").Body(deploymentConfig).Do().Into(result)
return
}
// Update updates an existing deploymentConfig
func (c *deploymentConfigs) Update(deploymentConfig *deployapi.DeploymentConfig) (result *deployapi.DeploymentConfig, err error) {
result = &deployapi.DeploymentConfig{}
err = c.r.Put().Namespace(c.ns).Resource("deploymentConfigs").Name(deploymentConfig.Name).Body(deploymentConfig).Do().Into(result)
return
}
// Delete deletes an existing deploymentConfig.
func (c *deploymentConfigs) Delete(name string) error {
return c.r.Delete().Namespace(c.ns).Resource("deploymentConfigs").Name(name).Do().Error()
}
// Watch returns a watch.Interface that watches the requested deploymentConfigs.
func (c *deploymentConfigs) Watch(opts kapi.ListOptions) (watch.Interface, error) {
return c.r.Get().
Prefix("watch").
Namespace(c.ns).
Resource("deploymentConfigs").
VersionedParams(&opts, kapi.ParameterCodec).
Watch()
}
// Generate generates a new deploymentConfig for the given name.
func (c *deploymentConfigs) Generate(name string) (result *deployapi.DeploymentConfig, err error) {
result = &deployapi.DeploymentConfig{}
err = c.r.Get().Namespace(c.ns).Resource("generateDeploymentConfigs").Name(name).Do().Into(result)
return
}
// Rollback rolls a deploymentConfig back to a previous configuration
func (c *deploymentConfigs) Rollback(config *deployapi.DeploymentConfigRollback) (result *deployapi.DeploymentConfig, err error) {
result = &deployapi.DeploymentConfig{}
err = c.r.Post().
Namespace(c.ns).
Resource("deploymentConfigs").
Name(config.Name).
SubResource("rollback").
Body(config).
Do().
Into(result)
return
}
// RollbackDeprecated rolls a deploymentConfig back to a previous configuration
func (c *deploymentConfigs) RollbackDeprecated(config *deployapi.DeploymentConfigRollback) (result *deployapi.DeploymentConfig, err error) {
result = &deployapi.DeploymentConfig{}
err = c.r.Post().
Namespace(c.ns).
Resource("deploymentConfigRollbacks").
Body(config).
Do().
Into(result)
return
}
// GetScale returns information about a particular deploymentConfig via its scale subresource
func (c *deploymentConfigs) GetScale(name string) (result *extensions.Scale, err error) {
result = &extensions.Scale{}
err = c.r.Get().Namespace(c.ns).Resource("deploymentConfigs").Name(name).SubResource("scale").Do().Into(result)
return
}
// UpdateScale scales an existing deploymentConfig via its scale subresource
func (c *deploymentConfigs) UpdateScale(scale *extensions.Scale) (result *extensions.Scale, err error) {
result = &extensions.Scale{}
// TODO fix by making the client understand how to encode using different codecs for different resources
encodedBytes, err := runtime.Encode(kapi.Codecs.LegacyCodec(extensionsv1beta1.SchemeGroupVersion), scale)
if err != nil {
return result, err
}
err = c.r.Put().Namespace(c.ns).Resource("deploymentConfigs").Name(scale.Name).SubResource("scale").Body(encodedBytes).Do().Into(result)
return
}
// UpdateStatus updates the status for an existing deploymentConfig.
func (c *deploymentConfigs) UpdateStatus(deploymentConfig *deployapi.DeploymentConfig) (result *deployapi.DeploymentConfig, err error) {
result = &deployapi.DeploymentConfig{}
err = c.r.Put().Namespace(c.ns).Resource("deploymentConfigs").Name(deploymentConfig.Name).SubResource("status").Body(deploymentConfig).Do().Into(result)
return
}
type updateConfigFunc func(d *deployapi.DeploymentConfig)
// UpdateConfigWithRetries will try to update a deployment config and ignore any update conflicts.
func UpdateConfigWithRetries(dn DeploymentConfigsNamespacer, namespace, name string, applyUpdate updateConfigFunc) (*deployapi.DeploymentConfig, error) {
var config *deployapi.DeploymentConfig
resultErr := kclient.RetryOnConflict(kclient.DefaultBackoff, func() error {
var err error
config, err = dn.DeploymentConfigs(namespace).Get(name)
if err != nil {
return err
}
// Apply the update, then attempt to push it to the apiserver.
applyUpdate(config)
config, err = dn.DeploymentConfigs(namespace).Update(config)
return err
})
return config, resultErr
}
+37
View File
@@ -0,0 +1,37 @@
package client
import (
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/client/restclient"
"github.com/openshift/origin/pkg/deploy/api"
)
// DeploymentLogsNamespacer has methods to work with DeploymentLogs resources in a namespace
type DeploymentLogsNamespacer interface {
DeploymentLogs(namespace string) DeploymentLogInterface
}
// DeploymentLogInterface exposes methods on DeploymentLogs resources.
type DeploymentLogInterface interface {
Get(name string, opts api.DeploymentLogOptions) *restclient.Request
}
// deploymentLogs implements DeploymentLogsNamespacer interface
type deploymentLogs struct {
r *Client
ns string
}
// newDeploymentLogs returns a deploymentLogs
func newDeploymentLogs(c *Client, namespace string) *deploymentLogs {
return &deploymentLogs{
r: c,
ns: namespace,
}
}
// Get gets the deploymentlogs and return a deploymentLog request
func (c *deploymentLogs) Get(name string, opts api.DeploymentLogOptions) *restclient.Request {
return c.r.Get().Namespace(c.ns).Resource("deploymentConfigs").Name(name).SubResource("log").VersionedParams(&opts, kapi.ParameterCodec)
}
+68
View File
@@ -0,0 +1,68 @@
package client
import (
"net/url"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/client/restclient"
"k8s.io/kubernetes/pkg/client/typed/discovery"
)
// DiscoveryClient implements the functions that discovery server-supported API groups,
// versions and resources.
type DiscoveryClient struct {
*discovery.DiscoveryClient
}
// ServerResourcesForGroupVersion returns the supported resources for a group and version.
func (d *DiscoveryClient) ServerResourcesForGroupVersion(groupVersion string) (resources *unversioned.APIResourceList, err error) {
parentList, err := d.DiscoveryClient.ServerResourcesForGroupVersion(groupVersion)
if err != nil {
return parentList, err
}
if groupVersion != "v1" {
return parentList, nil
}
// we request v1, we must combine the parent list with the list from /oapi
url := url.URL{}
url.Path = "/oapi/" + groupVersion
originResources := &unversioned.APIResourceList{}
err = d.Get().AbsPath(url.String()).Do().Into(originResources)
if err != nil {
// ignore 403 or 404 error to be compatible with an v1.0 server.
if groupVersion == "v1" && (errors.IsNotFound(err) || errors.IsForbidden(err)) {
return parentList, nil
}
return nil, err
}
parentList.APIResources = append(parentList.APIResources, originResources.APIResources...)
return parentList, nil
}
// ServerResources returns the supported resources for all groups and versions.
func (d *DiscoveryClient) ServerResources() (map[string]*unversioned.APIResourceList, error) {
apiGroups, err := d.ServerGroups()
if err != nil {
return nil, err
}
groupVersions := unversioned.ExtractGroupVersions(apiGroups)
result := map[string]*unversioned.APIResourceList{}
for _, groupVersion := range groupVersions {
resources, err := d.ServerResourcesForGroupVersion(groupVersion)
if err != nil {
return nil, err
}
result[groupVersion] = resources
}
return result, nil
}
// New creates a new DiscoveryClient for the given RESTClient.
func NewDiscoveryClient(c *restclient.RESTClient) *DiscoveryClient {
return &DiscoveryClient{discovery.NewDiscoveryClient(c)}
}
+85
View File
@@ -0,0 +1,85 @@
package client
import (
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/watch"
sdnapi "github.com/openshift/origin/pkg/sdn/api"
)
// EgressNetworkPoliciesNamespacer has methods to work with EgressNetworkPolicy resources in a namespace
type EgressNetworkPoliciesNamespacer interface {
EgressNetworkPolicies(namespace string) EgressNetworkPolicyInterface
}
// EgressNetworkPolicyInterface exposes methods on egressNetworkPolicy resources.
type EgressNetworkPolicyInterface interface {
List(opts kapi.ListOptions) (*sdnapi.EgressNetworkPolicyList, error)
Get(name string) (*sdnapi.EgressNetworkPolicy, error)
Create(sub *sdnapi.EgressNetworkPolicy) (*sdnapi.EgressNetworkPolicy, error)
Update(sub *sdnapi.EgressNetworkPolicy) (*sdnapi.EgressNetworkPolicy, error)
Delete(name string) error
Watch(opts kapi.ListOptions) (watch.Interface, error)
}
// egressNetworkPolicy implements EgressNetworkPolicyInterface interface
type egressNetworkPolicy struct {
r *Client
ns string
}
// newEgressNetworkPolicy returns a egressNetworkPolicy
func newEgressNetworkPolicy(c *Client, namespace string) *egressNetworkPolicy {
return &egressNetworkPolicy{
r: c,
ns: namespace,
}
}
// List returns a list of EgressNetworkPolicy that match the label and field selectors.
func (c *egressNetworkPolicy) List(opts kapi.ListOptions) (result *sdnapi.EgressNetworkPolicyList, err error) {
result = &sdnapi.EgressNetworkPolicyList{}
err = c.r.Get().
Namespace(c.ns).
Resource("egressNetworkPolicies").
VersionedParams(&opts, kapi.ParameterCodec).
Do().
Into(result)
return
}
// Get returns information about a particular firewall
func (c *egressNetworkPolicy) Get(name string) (result *sdnapi.EgressNetworkPolicy, err error) {
result = &sdnapi.EgressNetworkPolicy{}
err = c.r.Get().Namespace(c.ns).Resource("egressNetworkPolicies").Name(name).Do().Into(result)
return
}
// Create creates a new EgressNetworkPolicy. Returns the server's representation of EgressNetworkPolicy and error if one occurs.
func (c *egressNetworkPolicy) Create(fw *sdnapi.EgressNetworkPolicy) (result *sdnapi.EgressNetworkPolicy, err error) {
result = &sdnapi.EgressNetworkPolicy{}
err = c.r.Post().Namespace(c.ns).Resource("egressNetworkPolicies").Body(fw).Do().Into(result)
return
}
// Update updates the EgressNetworkPolicy on the server. Returns the server's representation of the EgressNetworkPolicy and error if one occurs.
func (c *egressNetworkPolicy) Update(fw *sdnapi.EgressNetworkPolicy) (result *sdnapi.EgressNetworkPolicy, err error) {
result = &sdnapi.EgressNetworkPolicy{}
err = c.r.Put().Namespace(c.ns).Resource("egressNetworkPolicies").Name(fw.Name).Body(fw).Do().Into(result)
return
}
// Delete takes the name of the EgressNetworkPolicy, and returns an error if one occurs during deletion of the EgressNetworkPolicy
func (c *egressNetworkPolicy) Delete(name string) error {
return c.r.Delete().Namespace(c.ns).Resource("egressNetworkPolicies").Name(name).Do().Error()
}
// Watch returns a watch.Interface that watches the requested EgressNetworkPolicies
func (c *egressNetworkPolicy) Watch(opts kapi.ListOptions) (watch.Interface, error) {
return c.r.Get().
Prefix("watch").
Namespace(c.ns).
Resource("egressNetworkPolicies").
VersionedParams(&opts, kapi.ParameterCodec).
Watch()
}
+81
View File
@@ -0,0 +1,81 @@
package client
import (
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/watch"
userapi "github.com/openshift/origin/pkg/user/api"
)
// GroupsInterface has methods to work with Group resources
type GroupsInterface interface {
Groups() GroupInterface
}
// GroupInterface exposes methods on group resources.
type GroupInterface interface {
List(opts kapi.ListOptions) (*userapi.GroupList, error)
Get(name string) (*userapi.Group, error)
Create(group *userapi.Group) (*userapi.Group, error)
Update(group *userapi.Group) (*userapi.Group, error)
Delete(name string) error
Watch(opts kapi.ListOptions) (watch.Interface, error)
}
// groups implements GroupInterface interface
type groups struct {
r *Client
}
// newGroups returns a groups
func newGroups(c *Client) *groups {
return &groups{
r: c,
}
}
// List returns a list of groups that match the label and field selectors.
func (c *groups) List(opts kapi.ListOptions) (result *userapi.GroupList, err error) {
result = &userapi.GroupList{}
err = c.r.Get().
Resource("groups").
VersionedParams(&opts, kapi.ParameterCodec).
Do().
Into(result)
return
}
// Get returns information about a particular group or an error
func (c *groups) Get(name string) (result *userapi.Group, err error) {
result = &userapi.Group{}
err = c.r.Get().Resource("groups").Name(name).Do().Into(result)
return
}
// Create creates a new group. Returns the server's representation of the group and error if one occurs.
func (c *groups) Create(group *userapi.Group) (result *userapi.Group, err error) {
result = &userapi.Group{}
err = c.r.Post().Resource("groups").Body(group).Do().Into(result)
return
}
// Update updates the group on server. Returns the server's representation of the group and error if one occurs.
func (c *groups) Update(group *userapi.Group) (result *userapi.Group, err error) {
result = &userapi.Group{}
err = c.r.Put().Resource("groups").Name(group.Name).Body(group).Do().Into(result)
return
}
// Delete takes the name of the groups, and returns an error if one occurs during deletion of the groups
func (c *groups) Delete(name string) error {
return c.r.Delete().Resource("groups").Name(name).Do().Error()
}
// Watch returns a watch.Interface that watches the requested groups.
func (c *groups) Watch(opts kapi.ListOptions) (watch.Interface, error) {
return c.r.Get().
Prefix("watch").
Resource("groups").
VersionedParams(&opts, kapi.ParameterCodec).
Watch()
}
+81
View File
@@ -0,0 +1,81 @@
package client
import (
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/watch"
sdnapi "github.com/openshift/origin/pkg/sdn/api"
)
// HostSubnetInterface has methods to work with HostSubnet resources
type HostSubnetsInterface interface {
HostSubnets() HostSubnetInterface
}
// HostSubnetInterface exposes methods on HostSubnet resources.
type HostSubnetInterface interface {
List(opts kapi.ListOptions) (*sdnapi.HostSubnetList, error)
Get(name string) (*sdnapi.HostSubnet, error)
Create(sub *sdnapi.HostSubnet) (*sdnapi.HostSubnet, error)
Update(sub *sdnapi.HostSubnet) (*sdnapi.HostSubnet, error)
Delete(name string) error
Watch(opts kapi.ListOptions) (watch.Interface, error)
}
// hostSubnet implements HostSubnetInterface interface
type hostSubnet struct {
r *Client
}
// newHostSubnet returns a hostsubnet
func newHostSubnet(c *Client) *hostSubnet {
return &hostSubnet{
r: c,
}
}
// List returns a list of hostsubnets that match the label and field selectors.
func (c *hostSubnet) List(opts kapi.ListOptions) (result *sdnapi.HostSubnetList, err error) {
result = &sdnapi.HostSubnetList{}
err = c.r.Get().
Resource("hostSubnets").
VersionedParams(&opts, kapi.ParameterCodec).
Do().
Into(result)
return
}
// Get returns host subnet information for a given host or an error
func (c *hostSubnet) Get(hostName string) (result *sdnapi.HostSubnet, err error) {
result = &sdnapi.HostSubnet{}
err = c.r.Get().Resource("hostSubnets").Name(hostName).Do().Into(result)
return
}
// Create creates a new host subnet. Returns the server's representation of the host subnet and error if one occurs.
func (c *hostSubnet) Create(hostSubnet *sdnapi.HostSubnet) (result *sdnapi.HostSubnet, err error) {
result = &sdnapi.HostSubnet{}
err = c.r.Post().Resource("hostSubnets").Body(hostSubnet).Do().Into(result)
return
}
// Update updates existing host subnet. Returns the server's representation of the host subnet and error if one occurs.
func (c *hostSubnet) Update(hostSubnet *sdnapi.HostSubnet) (result *sdnapi.HostSubnet, err error) {
result = &sdnapi.HostSubnet{}
err = c.r.Put().Resource("hostSubnets").Name(hostSubnet.Name).Body(hostSubnet).Do().Into(result)
return
}
// Delete takes the name of the host, and returns an error if one occurs during deletion of the subnet
func (c *hostSubnet) Delete(name string) error {
return c.r.Delete().Resource("hostSubnets").Name(name).Do().Error()
}
// Watch returns a watch.Interface that watches the requested subnets
func (c *hostSubnet) Watch(opts kapi.ListOptions) (watch.Interface, error) {
return c.r.Get().
Prefix("watch").
Resource("hostSubnets").
VersionedParams(&opts, kapi.ParameterCodec).
Watch()
}
+70
View File
@@ -0,0 +1,70 @@
package client
import (
kapi "k8s.io/kubernetes/pkg/api"
userapi "github.com/openshift/origin/pkg/user/api"
)
// IdentitiesInterface has methods to work with Identity resources
type IdentitiesInterface interface {
Identities() IdentityInterface
}
// IdentityInterface exposes methods on identity resources.
type IdentityInterface interface {
List(opts kapi.ListOptions) (*userapi.IdentityList, error)
Get(name string) (*userapi.Identity, error)
Create(identity *userapi.Identity) (*userapi.Identity, error)
Update(identity *userapi.Identity) (*userapi.Identity, error)
Delete(name string) error
}
// identities implements IdentityInterface interface
type identities struct {
r *Client
}
// newIdentities returns an identities client
func newIdentities(c *Client) *identities {
return &identities{
r: c,
}
}
// List returns a list of identities that match the label and field selectors.
func (c *identities) List(opts kapi.ListOptions) (result *userapi.IdentityList, err error) {
result = &userapi.IdentityList{}
err = c.r.Get().
Resource("identities").
VersionedParams(&opts, kapi.ParameterCodec).
Do().
Into(result)
return
}
// Get returns information about a particular identity or an error
func (c *identities) Get(name string) (result *userapi.Identity, err error) {
result = &userapi.Identity{}
err = c.r.Get().Resource("identities").Name(name).Do().Into(result)
return
}
// Create creates a new identity. Returns the server's representation of the identity and error if one occurs.
func (c *identities) Create(identity *userapi.Identity) (result *userapi.Identity, err error) {
result = &userapi.Identity{}
err = c.r.Post().Resource("identities").Body(identity).Do().Into(result)
return
}
// Update updates the identity on server. Returns the server's representation of the identity and error if one occurs.
func (c *identities) Update(identity *userapi.Identity) (result *userapi.Identity, err error) {
result = &userapi.Identity{}
err = c.r.Put().Resource("identities").Name(identity.Name).Body(identity).Do().Into(result)
return
}
// Delete deletes the identity on server. Returns an error if one occurs.
func (c *identities) Delete(name string) (err error) {
return c.r.Delete().Resource("identities").Name(name).Do().Error()
}
+72
View File
@@ -0,0 +1,72 @@
package client
import (
kapi "k8s.io/kubernetes/pkg/api"
imageapi "github.com/openshift/origin/pkg/image/api"
)
// ImagesInterfacer has methods to work with Image resources
type ImagesInterfacer interface {
Images() ImageInterface
}
// ImageInterface exposes methods on Image resources.
type ImageInterface interface {
List(opts kapi.ListOptions) (*imageapi.ImageList, error)
Get(name string) (*imageapi.Image, error)
Create(image *imageapi.Image) (*imageapi.Image, error)
Update(image *imageapi.Image) (*imageapi.Image, error)
Delete(name string) error
}
// images implements ImagesInterface.
type images struct {
r *Client
}
// newImages returns an images
func newImages(c *Client) ImageInterface {
return &images{
r: c,
}
}
// List returns a list of images that match the label and field selectors.
func (c *images) List(opts kapi.ListOptions) (result *imageapi.ImageList, err error) {
result = &imageapi.ImageList{}
err = c.r.Get().
Resource("images").
VersionedParams(&opts, kapi.ParameterCodec).
Do().
Into(result)
return
}
// Get returns information about a particular image and error if one occurs.
func (c *images) Get(name string) (result *imageapi.Image, err error) {
result = &imageapi.Image{}
err = c.r.Get().Resource("images").Name(name).Do().Into(result)
return
}
// Create creates a new image. Returns the server's representation of the image and error if one occurs.
func (c *images) Create(image *imageapi.Image) (result *imageapi.Image, err error) {
result = &imageapi.Image{}
err = c.r.Post().Resource("images").Body(image).Do().Into(result)
return
}
// Update allows to modify existing image. Since most of image's attributes are immutable, this call allows
// mainly for updating image signatures.
func (c *images) Update(image *imageapi.Image) (result *imageapi.Image, err error) {
result = &imageapi.Image{}
err = c.r.Put().Resource("images").Name(image.Name).Body(image).Do().Into(result)
return
}
// Delete deletes an image, returns error if one occurs.
func (c *images) Delete(name string) (err error) {
err = c.r.Delete().Resource("images").Name(name).Do().Error()
return
}
+41
View File
@@ -0,0 +1,41 @@
package client
import (
imageapi "github.com/openshift/origin/pkg/image/api"
)
// ImageSignaturesInterfacer has methods to work with ImageSignature resource.
type ImageSignaturesInterfacer interface {
ImageSignatures() ImageSignatureInterface
}
// ImageSignatureInterface exposes methods on ImageSignature virtual resource.
type ImageSignatureInterface interface {
Create(signature *imageapi.ImageSignature) (*imageapi.ImageSignature, error)
Delete(name string) error
}
// imageSignatures implements ImageSignatureInterface.
type imageSignatures struct {
r *Client
}
// newImageSignatures returns imageSignatures
func newImageSignatures(c *Client) ImageSignatureInterface {
return &imageSignatures{
r: c,
}
}
// Create creates a new ImageSignature. Returns the server's representation of the signature and error if one
// occurs.
func (c *imageSignatures) Create(signature *imageapi.ImageSignature) (result *imageapi.ImageSignature, err error) {
result = &imageapi.ImageSignature{}
err = c.r.Post().Resource("imageSignatures").Body(signature).Do().Into(result)
return
}
// Delete deletes an ImageSignature, returns error if one occurs.
func (c *imageSignatures) Delete(name string) error {
return c.r.Delete().Resource("imageSignatures").Name(name).Do().Error()
}
+36
View File
@@ -0,0 +1,36 @@
package client
import (
"github.com/openshift/origin/pkg/image/api"
)
// ImageStreamImagesNamespacer has methods to work with ImageStreamImage resources in a namespace
type ImageStreamImagesNamespacer interface {
ImageStreamImages(namespace string) ImageStreamImageInterface
}
// ImageStreamImageInterface exposes methods on ImageStreamImage resources.
type ImageStreamImageInterface interface {
Get(name, id string) (*api.ImageStreamImage, error)
}
// imageStreamImages implements ImageStreamImagesNamespacer interface
type imageStreamImages struct {
r *Client
ns string
}
// newImageStreamImages returns an imageStreamImages
func newImageStreamImages(c *Client, namespace string) *imageStreamImages {
return &imageStreamImages{
r: c,
ns: namespace,
}
}
// Get finds the specified image by name of an image repository and id.
func (c *imageStreamImages) Get(name, id string) (result *api.ImageStreamImage, err error) {
result = &api.ImageStreamImage{}
err = c.r.Get().Namespace(c.ns).Resource("imageStreamImages").Name(api.MakeImageStreamImageName(name, id)).Do().Into(result)
return
}
+34
View File
@@ -0,0 +1,34 @@
package client
import (
imageapi "github.com/openshift/origin/pkg/image/api"
)
// ImageStreamMappingsNamespacer has methods to work with ImageStreamMapping resources in a namespace
type ImageStreamMappingsNamespacer interface {
ImageStreamMappings(namespace string) ImageStreamMappingInterface
}
// ImageStreamMappingInterface exposes methods on ImageStreamMapping resources.
type ImageStreamMappingInterface interface {
Create(mapping *imageapi.ImageStreamMapping) error
}
// imageStreamMappings implements ImageStreamMappingsNamespacer interface
type imageStreamMappings struct {
r *Client
ns string
}
// newImageStreamMappings returns an imageStreamMappings
func newImageStreamMappings(c *Client, namespace string) *imageStreamMappings {
return &imageStreamMappings{
r: c,
ns: namespace,
}
}
// Create creates a new image stream mapping on the server. Returns error if one occurs.
func (c *imageStreamMappings) Create(mapping *imageapi.ImageStreamMapping) error {
return c.r.Post().Namespace(c.ns).Resource("imageStreamMappings").Body(mapping).Do().Error()
}
+147
View File
@@ -0,0 +1,147 @@
package client
import (
"errors"
kapi "k8s.io/kubernetes/pkg/api"
apierrs "k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/watch"
imageapi "github.com/openshift/origin/pkg/image/api"
quotautil "github.com/openshift/origin/pkg/quota/util"
)
var ErrImageStreamImportUnsupported = errors.New("the server does not support directly importing images - create an image stream with tags or the dockerImageRepository field set")
// ImageStreamsNamespacer has methods to work with ImageStream resources in a namespace
type ImageStreamsNamespacer interface {
ImageStreams(namespace string) ImageStreamInterface
}
// ImageStreamInterface exposes methods on ImageStream resources.
type ImageStreamInterface interface {
List(opts kapi.ListOptions) (*imageapi.ImageStreamList, error)
Get(name string) (*imageapi.ImageStream, error)
Create(stream *imageapi.ImageStream) (*imageapi.ImageStream, error)
Update(stream *imageapi.ImageStream) (*imageapi.ImageStream, error)
Delete(name string) error
Watch(opts kapi.ListOptions) (watch.Interface, error)
UpdateStatus(stream *imageapi.ImageStream) (*imageapi.ImageStream, error)
Import(isi *imageapi.ImageStreamImport) (*imageapi.ImageStreamImport, error)
}
// ImageStreamNamespaceGetter exposes methods to get ImageStreams by Namespace
type ImageStreamNamespaceGetter interface {
GetByNamespace(namespace, name string) (*imageapi.ImageStream, error)
}
// imageStreams implements ImageStreamsNamespacer interface
type imageStreams struct {
r *Client
ns string
}
// newImageStreams returns an imageStreams
func newImageStreams(c *Client, namespace string) *imageStreams {
return &imageStreams{
r: c,
ns: namespace,
}
}
// List returns a list of image streams that match the label and field selectors.
func (c *imageStreams) List(opts kapi.ListOptions) (result *imageapi.ImageStreamList, err error) {
result = &imageapi.ImageStreamList{}
err = c.r.Get().
Namespace(c.ns).
Resource("imageStreams").
VersionedParams(&opts, kapi.ParameterCodec).
Do().
Into(result)
return
}
// Get returns information about a particular image stream and error if one occurs.
func (c *imageStreams) Get(name string) (result *imageapi.ImageStream, err error) {
result = &imageapi.ImageStream{}
err = c.r.Get().Namespace(c.ns).Resource("imageStreams").Name(name).Do().Into(result)
return
}
// GetByNamespace returns information about a particular image stream in a particular namespace and error if one occurs.
func (c *imageStreams) GetByNamespace(namespace, name string) (result *imageapi.ImageStream, err error) {
result = &imageapi.ImageStream{}
c.r.Get().Namespace(namespace).Resource("imageStreams").Name(name).Do().Into(result)
return
}
// Create create a new image stream. Returns the server's representation of the image stream and error if one occurs.
func (c *imageStreams) Create(stream *imageapi.ImageStream) (result *imageapi.ImageStream, err error) {
result = &imageapi.ImageStream{}
err = c.r.Post().Namespace(c.ns).Resource("imageStreams").Body(stream).Do().Into(result)
return
}
// Update updates the image stream on the server. Returns the server's representation of the image stream and error if one occurs.
func (c *imageStreams) Update(stream *imageapi.ImageStream) (result *imageapi.ImageStream, err error) {
result = &imageapi.ImageStream{}
err = c.r.Put().Namespace(c.ns).Resource("imageStreams").Name(stream.Name).Body(stream).Do().Into(result)
return
}
// Delete deletes an image stream, returns error if one occurs.
func (c *imageStreams) Delete(name string) (err error) {
err = c.r.Delete().Namespace(c.ns).Resource("imageStreams").Name(name).Do().Error()
return
}
// Watch returns a watch.Interface that watches the requested image streams.
func (c *imageStreams) Watch(opts kapi.ListOptions) (watch.Interface, error) {
return c.r.Get().
Prefix("watch").
Namespace(c.ns).
Resource("imageStreams").
VersionedParams(&opts, kapi.ParameterCodec).
Watch()
}
// UpdateStatus updates the image stream's status. Returns the server's representation of the image stream, and an error, if it occurs.
func (c *imageStreams) UpdateStatus(stream *imageapi.ImageStream) (result *imageapi.ImageStream, err error) {
result = &imageapi.ImageStream{}
err = c.r.Put().Namespace(c.ns).Resource("imageStreams").Name(stream.Name).SubResource("status").Body(stream).Do().Into(result)
return
}
// Import makes a call to the server to retrieve information about the requested images or to perform an import. ImageStreamImport
// will be returned if no actual import was requested (the to fields were not set), or an ImageStream if import was requested.
func (c *imageStreams) Import(isi *imageapi.ImageStreamImport) (*imageapi.ImageStreamImport, error) {
result := &imageapi.ImageStreamImport{}
if err := c.r.Post().Namespace(c.ns).Resource("imageStreamImports").Body(isi).Do().Into(result); err != nil {
return nil, transformUnsupported(err)
}
return result, nil
}
// transformUnsupported converts specific error conditions to unsupported
func transformUnsupported(err error) error {
if err == nil {
return nil
}
if apierrs.IsNotFound(err) {
status, ok := err.(apierrs.APIStatus)
if !ok {
return ErrImageStreamImportUnsupported
}
if status.Status().Details == nil || status.Status().Details.Kind == "" {
return ErrImageStreamImportUnsupported
}
}
// The ImageStreamImport resource exists in v1.1.1 of origin but is not yet
// enabled by policy. A create request will return a Forbidden(403) error.
// We want to return ErrImageStreamImportUnsupported to allow fallback behavior
// in clients.
if apierrs.IsForbidden(err) && !quotautil.IsErrorQuotaExceeded(err) {
return ErrImageStreamImportUnsupported
}
return err
}
+44
View File
@@ -0,0 +1,44 @@
package client
import (
kapi "k8s.io/kubernetes/pkg/api"
)
// ImageStreamSecretsNamespacer has methods to work with ImageStreamSecret resources in a namespace
type ImageStreamSecretsNamespacer interface {
ImageStreamSecrets(namespace string) ImageStreamSecretInterface
}
// ImageStreamSecretInterface exposes methods on ImageStreamSecret resources.
type ImageStreamSecretInterface interface {
// Secrets retrieves the secrets for a named image stream with the provided list options.
Secrets(name string, options kapi.ListOptions) (*kapi.SecretList, error)
}
// imageStreamSecrets implements ImageStreamSecretsNamespacer interface
type imageStreamSecrets struct {
r *Client
ns string
}
// newImageStreamSecrets returns an imageStreamSecrets
func newImageStreamSecrets(c *Client, namespace string) *imageStreamSecrets {
return &imageStreamSecrets{
r: c,
ns: namespace,
}
}
// GetSecrets returns a list of secrets for the named image stream
func (c *imageStreamSecrets) Secrets(name string, options kapi.ListOptions) (result *kapi.SecretList, err error) {
result = &kapi.SecretList{}
err = c.r.Get().
Namespace(c.ns).
Resource("imageStreams").
Name(name).
SubResource("secrets").
VersionedParams(&options, kapi.ParameterCodec).
Do().
Into(result)
return
}
+57
View File
@@ -0,0 +1,57 @@
package client
import (
"github.com/openshift/origin/pkg/image/api"
)
// ImageStreamTagsNamespacer has methods to work with ImageStreamTag resources in a namespace
type ImageStreamTagsNamespacer interface {
ImageStreamTags(namespace string) ImageStreamTagInterface
}
// ImageStreamTagInterface exposes methods on ImageStreamTag resources.
type ImageStreamTagInterface interface {
Get(name, tag string) (*api.ImageStreamTag, error)
Create(tag *api.ImageStreamTag) (*api.ImageStreamTag, error)
Update(tag *api.ImageStreamTag) (*api.ImageStreamTag, error)
Delete(name, tag string) error
}
// imageStreamTags implements ImageStreamTagsNamespacer interface
type imageStreamTags struct {
r *Client
ns string
}
// newImageStreamTags returns an imageStreamTags
func newImageStreamTags(c *Client, namespace string) *imageStreamTags {
return &imageStreamTags{
r: c,
ns: namespace,
}
}
// Get finds the specified image by name of an image stream and tag.
func (c *imageStreamTags) Get(name, tag string) (result *api.ImageStreamTag, err error) {
result = &api.ImageStreamTag{}
err = c.r.Get().Namespace(c.ns).Resource("imageStreamTags").Name(api.JoinImageStreamTag(name, tag)).Do().Into(result)
return
}
// Update updates an image stream tag (creating it if it does not exist).
func (c *imageStreamTags) Update(tag *api.ImageStreamTag) (result *api.ImageStreamTag, err error) {
result = &api.ImageStreamTag{}
err = c.r.Put().Namespace(c.ns).Resource("imageStreamTags").Name(tag.Name).Body(tag).Do().Into(result)
return
}
func (c *imageStreamTags) Create(tag *api.ImageStreamTag) (result *api.ImageStreamTag, err error) {
result = &api.ImageStreamTag{}
err = c.r.Post().Namespace(c.ns).Resource("imageStreamTags").Body(tag).Do().Into(result)
return
}
// Delete deletes the specified tag from the image stream.
func (c *imageStreamTags) Delete(name, tag string) error {
return c.r.Delete().Namespace(c.ns).Resource("imageStreamTags").Name(api.JoinImageStreamTag(name, tag)).Do().Error()
}
@@ -0,0 +1,51 @@
package client
import (
kapierrors "k8s.io/kubernetes/pkg/api/errors"
authorizationapi "github.com/openshift/origin/pkg/authorization/api"
)
// LocalResourceAccessReviewsNamespacer has methods to work with LocalResourceAccessReview resources in a namespace
type LocalResourceAccessReviewsNamespacer interface {
LocalResourceAccessReviews(namespace string) LocalResourceAccessReviewInterface
}
// LocalResourceAccessReviewInterface exposes methods on LocalResourceAccessReview resources.
type LocalResourceAccessReviewInterface interface {
Create(policy *authorizationapi.LocalResourceAccessReview) (*authorizationapi.ResourceAccessReviewResponse, error)
}
// localResourceAccessReviews implements ResourceAccessReviewsNamespacer interface
type localResourceAccessReviews struct {
r *Client
ns string
}
// newLocalResourceAccessReviews returns a localLocalResourceAccessReviews
func newLocalResourceAccessReviews(c *Client, namespace string) *localResourceAccessReviews {
return &localResourceAccessReviews{
r: c,
ns: namespace,
}
}
func (c *localResourceAccessReviews) Create(rar *authorizationapi.LocalResourceAccessReview) (result *authorizationapi.ResourceAccessReviewResponse, err error) {
result = &authorizationapi.ResourceAccessReviewResponse{}
err = c.r.Post().Namespace(c.ns).Resource("localResourceAccessReviews").Body(rar).Do().Into(result)
// if we get one of these failures, we may be talking to an older openshift. In that case, we need to try hitting ns/namespace-name/subjectaccessreview
if kapierrors.IsForbidden(err) || kapierrors.IsNotFound(err) {
deprecatedRAR := &authorizationapi.ResourceAccessReview{
Action: rar.Action,
}
deprecatedResponse := &authorizationapi.ResourceAccessReviewResponse{}
deprecatedAttemptErr := c.r.Post().Namespace(c.ns).Resource("resourceAccessReviews").Body(deprecatedRAR).Do().Into(deprecatedResponse)
if deprecatedAttemptErr == nil {
err = nil
result = deprecatedResponse
}
}
return
}
@@ -0,0 +1,78 @@
package client
import (
kapierrors "k8s.io/kubernetes/pkg/api/errors"
authorizationapi "github.com/openshift/origin/pkg/authorization/api"
)
type LocalSubjectAccessReviewsImpersonator interface {
ImpersonateLocalSubjectAccessReviews(namespace, token string) LocalSubjectAccessReviewInterface
}
// LocalSubjectAccessReviewsNamespacer has methods to work with LocalSubjectAccessReview resources in a namespace
type LocalSubjectAccessReviewsNamespacer interface {
LocalSubjectAccessReviews(namespace string) LocalSubjectAccessReviewInterface
}
// LocalSubjectAccessReviewInterface exposes methods on LocalSubjectAccessReview resources.
type LocalSubjectAccessReviewInterface interface {
Create(policy *authorizationapi.LocalSubjectAccessReview) (*authorizationapi.SubjectAccessReviewResponse, error)
}
// localSubjectAccessReviews implements LocalSubjectAccessReviewsNamespacer interface
type localSubjectAccessReviews struct {
r *Client
ns string
token *string
}
// newImpersonatingLocalSubjectAccessReviews returns a subjectAccessReviews
func newImpersonatingLocalSubjectAccessReviews(c *Client, namespace, token string) *localSubjectAccessReviews {
return &localSubjectAccessReviews{
r: c,
ns: namespace,
token: &token,
}
}
// newLocalSubjectAccessReviews returns a localSubjectAccessReviews
func newLocalSubjectAccessReviews(c *Client, namespace string) *localSubjectAccessReviews {
return &localSubjectAccessReviews{
r: c,
ns: namespace,
}
}
func (c *localSubjectAccessReviews) Create(sar *authorizationapi.LocalSubjectAccessReview) (*authorizationapi.SubjectAccessReviewResponse, error) {
result := &authorizationapi.SubjectAccessReviewResponse{}
req, err := overrideAuth(c.token, c.r.Post().Namespace(c.ns).Resource("localSubjectAccessReviews"))
if err != nil {
return &authorizationapi.SubjectAccessReviewResponse{}, err
}
err = req.Body(sar).Do().Into(result)
// if we get one of these failures, we may be talking to an older openshift. In that case, we need to try hitting ns/namespace-name/subjectaccessreview
if kapierrors.IsForbidden(err) || kapierrors.IsNotFound(err) {
deprecatedSAR := &authorizationapi.SubjectAccessReview{
Action: sar.Action,
User: sar.User,
Groups: sar.Groups,
}
deprecatedResponse := &authorizationapi.SubjectAccessReviewResponse{}
deprecatedReq, deprecatedAttemptErr := overrideAuth(c.token, c.r.Post().Namespace(c.ns).Resource("subjectAccessReviews"))
if deprecatedAttemptErr != nil {
return &authorizationapi.SubjectAccessReviewResponse{}, deprecatedAttemptErr
}
deprecatedAttemptErr = deprecatedReq.Body(deprecatedSAR).Do().Into(deprecatedResponse)
if deprecatedAttemptErr == nil {
err = nil
result = deprecatedResponse
}
}
return result, err
}
+26
View File
@@ -0,0 +1,26 @@
package client
import (
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/apimachinery/registered"
"k8s.io/kubernetes/pkg/util/sets"
)
// DefaultMultiRESTMapper returns the multi REST mapper with all OpenShift and
// Kubernetes objects already registered.
func DefaultMultiRESTMapper() meta.MultiRESTMapper {
var restMapper meta.MultiRESTMapper
seenGroups := sets.String{}
for _, gv := range registered.EnabledVersions() {
if seenGroups.Has(gv.Group) {
continue
}
seenGroups.Insert(gv.Group)
groupMeta, err := registered.Group(gv.Group)
if err != nil {
continue
}
restMapper = meta.MultiRESTMapper(append(restMapper, groupMeta.RESTMapper))
}
return restMapper
}
+81
View File
@@ -0,0 +1,81 @@
package client
import (
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/watch"
sdnapi "github.com/openshift/origin/pkg/sdn/api"
)
// NetNamespaceInterface has methods to work with NetNamespace resources
type NetNamespacesInterface interface {
NetNamespaces() NetNamespaceInterface
}
// NetNamespaceInterface exposes methods on NetNamespace resources.
type NetNamespaceInterface interface {
List(opts kapi.ListOptions) (*sdnapi.NetNamespaceList, error)
Get(name string) (*sdnapi.NetNamespace, error)
Create(sub *sdnapi.NetNamespace) (*sdnapi.NetNamespace, error)
Update(sub *sdnapi.NetNamespace) (*sdnapi.NetNamespace, error)
Delete(name string) error
Watch(opts kapi.ListOptions) (watch.Interface, error)
}
// netNamespace implements NetNamespaceInterface interface
type netNamespace struct {
r *Client
}
// newNetNamespace returns a NetNamespace
func newNetNamespace(c *Client) *netNamespace {
return &netNamespace{
r: c,
}
}
// List returns a list of NetNamespaces that match the label and field selectors.
func (c *netNamespace) List(opts kapi.ListOptions) (result *sdnapi.NetNamespaceList, err error) {
result = &sdnapi.NetNamespaceList{}
err = c.r.Get().
Resource("netNamespaces").
VersionedParams(&opts, kapi.ParameterCodec).
Do().
Into(result)
return
}
// Get returns information about a particular NetNamespace or an error
func (c *netNamespace) Get(netname string) (result *sdnapi.NetNamespace, err error) {
result = &sdnapi.NetNamespace{}
err = c.r.Get().Resource("netNamespaces").Name(netname).Do().Into(result)
return
}
// Create creates a new NetNamespace. Returns the server's representation of the NetNamespace and error if one occurs.
func (c *netNamespace) Create(netNamespace *sdnapi.NetNamespace) (result *sdnapi.NetNamespace, err error) {
result = &sdnapi.NetNamespace{}
err = c.r.Post().Resource("netNamespaces").Body(netNamespace).Do().Into(result)
return
}
// Update updates the NetNamespace. Returns the server's representation of the NetNamespace and error if one occurs.
func (c *netNamespace) Update(netNamespace *sdnapi.NetNamespace) (result *sdnapi.NetNamespace, err error) {
result = &sdnapi.NetNamespace{}
err = c.r.Put().Resource("netNamespaces").Name(netNamespace.Name).Body(netNamespace).Do().Into(result)
return
}
// Delete takes the name of the NetNamespace, and returns an error if one occurs during deletion of the NetNamespace
func (c *netNamespace) Delete(name string) error {
return c.r.Delete().Resource("netNamespaces").Name(name).Do().Error()
}
// Watch returns a watch.Interface that watches the requested NetNamespaces
func (c *netNamespace) Watch(opts kapi.ListOptions) (watch.Interface, error) {
return c.r.Get().
Prefix("watch").
Resource("netNamespaces").
VersionedParams(&opts, kapi.ParameterCodec).
Watch()
}
+46
View File
@@ -0,0 +1,46 @@
package client
import (
oauthapi "github.com/openshift/origin/pkg/oauth/api"
)
// OAuthAccessTokensInterface has methods to work with OAuthAccessTokens resources in a namespace
type OAuthAccessTokensInterface interface {
OAuthAccessTokens() OAuthAccessTokenInterface
}
// OAuthAccessTokenInterface exposes methods on OAuthAccessTokens resources.
type OAuthAccessTokenInterface interface {
Create(token *oauthapi.OAuthAccessToken) (*oauthapi.OAuthAccessToken, error)
Get(name string) (*oauthapi.OAuthAccessToken, error)
Delete(name string) error
}
type oauthAccessTokenInterface struct {
r *Client
}
func newOAuthAccessTokens(c *Client) *oauthAccessTokenInterface {
return &oauthAccessTokenInterface{
r: c,
}
}
// Get returns information about a particular image and error if one occurs.
func (c *oauthAccessTokenInterface) Get(name string) (result *oauthapi.OAuthAccessToken, err error) {
result = &oauthapi.OAuthAccessToken{}
err = c.r.Get().Resource("oAuthAccessTokens").Name(name).Do().Into(result)
return
}
// Delete removes the OAuthAccessToken on server
func (c *oauthAccessTokenInterface) Delete(name string) (err error) {
err = c.r.Delete().Resource("oAuthAccessTokens").Name(name).Do().Error()
return
}
func (c *oauthAccessTokenInterface) Create(token *oauthapi.OAuthAccessToken) (result *oauthapi.OAuthAccessToken, err error) {
result = &oauthapi.OAuthAccessToken{}
err = c.r.Post().Resource("oAuthAccessTokens").Body(token).Do().Into(result)
return
}
+35
View File
@@ -0,0 +1,35 @@
package client
import (
oauthapi "github.com/openshift/origin/pkg/oauth/api"
)
type OAuthAuthorizeTokensInterface interface {
OAuthAuthorizeTokens() OAuthAuthorizeTokenInterface
}
type OAuthAuthorizeTokenInterface interface {
Create(token *oauthapi.OAuthAuthorizeToken) (*oauthapi.OAuthAuthorizeToken, error)
Delete(name string) error
}
type oauthAuthorizeTokenInterface struct {
r *Client
}
func newOAuthAuthorizeTokens(c *Client) *oauthAuthorizeTokenInterface {
return &oauthAuthorizeTokenInterface{
r: c,
}
}
func (c *oauthAuthorizeTokenInterface) Delete(name string) (err error) {
err = c.r.Delete().Resource("oAuthAuthorizeTokens").Name(name).Do().Error()
return
}
func (c *oauthAuthorizeTokenInterface) Create(token *oauthapi.OAuthAuthorizeToken) (result *oauthapi.OAuthAuthorizeToken, err error) {
result = &oauthapi.OAuthAuthorizeToken{}
err = c.r.Post().Resource("oAuthAuthorizeTokens").Body(token).Do().Into(result)
return
}
+57
View File
@@ -0,0 +1,57 @@
package client
import (
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/watch"
oauthapi "github.com/openshift/origin/pkg/oauth/api"
)
type OAuthClientsInterface interface {
OAuthClients() OAuthClientInterface
}
type OAuthClientInterface interface {
Create(obj *oauthapi.OAuthClient) (*oauthapi.OAuthClient, error)
List(opts kapi.ListOptions) (*oauthapi.OAuthClientList, error)
Get(name string) (*oauthapi.OAuthClient, error)
Delete(name string) error
Watch(opts kapi.ListOptions) (watch.Interface, error)
}
type oauthClients struct {
r *Client
}
func newOAuthClients(c *Client) *oauthClients {
return &oauthClients{
r: c,
}
}
func (c *oauthClients) Create(obj *oauthapi.OAuthClient) (result *oauthapi.OAuthClient, err error) {
result = &oauthapi.OAuthClient{}
err = c.r.Post().Resource("oAuthClients").Body(obj).Do().Into(result)
return
}
func (c *oauthClients) List(opts kapi.ListOptions) (result *oauthapi.OAuthClientList, err error) {
result = &oauthapi.OAuthClientList{}
err = c.r.Get().Resource("oAuthClients").VersionedParams(&opts, kapi.ParameterCodec).Do().Into(result)
return
}
func (c *oauthClients) Get(name string) (result *oauthapi.OAuthClient, err error) {
result = &oauthapi.OAuthClient{}
err = c.r.Get().Resource("oAuthClients").Name(name).Do().Into(result)
return
}
func (c *oauthClients) Delete(name string) (err error) {
err = c.r.Delete().Resource("oAuthClients").Name(name).Do().Error()
return
}
func (c *oauthClients) Watch(opts kapi.ListOptions) (watch.Interface, error) {
return c.r.Get().Prefix("watch").Resource("oAuthClients").VersionedParams(&opts, kapi.ParameterCodec).Watch()
}
@@ -0,0 +1,64 @@
package client
import (
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/watch"
oauthapi "github.com/openshift/origin/pkg/oauth/api"
)
type OAuthClientAuthorizationsInterface interface {
OAuthClientAuthorizations() OAuthClientAuthorizationInterface
}
type OAuthClientAuthorizationInterface interface {
Create(obj *oauthapi.OAuthClientAuthorization) (*oauthapi.OAuthClientAuthorization, error)
List(opts kapi.ListOptions) (*oauthapi.OAuthClientAuthorizationList, error)
Get(name string) (*oauthapi.OAuthClientAuthorization, error)
Update(obj *oauthapi.OAuthClientAuthorization) (*oauthapi.OAuthClientAuthorization, error)
Delete(name string) error
Watch(opts kapi.ListOptions) (watch.Interface, error)
}
type oauthClientAuthorizations struct {
r *Client
}
func newOAuthClientAuthorizations(c *Client) *oauthClientAuthorizations {
return &oauthClientAuthorizations{
r: c,
}
}
func (c *oauthClientAuthorizations) Create(obj *oauthapi.OAuthClientAuthorization) (result *oauthapi.OAuthClientAuthorization, err error) {
result = &oauthapi.OAuthClientAuthorization{}
err = c.r.Post().Resource("oAuthClientAuthorizations").Body(obj).Do().Into(result)
return
}
func (c *oauthClientAuthorizations) Update(obj *oauthapi.OAuthClientAuthorization) (result *oauthapi.OAuthClientAuthorization, err error) {
result = &oauthapi.OAuthClientAuthorization{}
err = c.r.Put().Resource("oAuthClientAuthorizations").Name(obj.Name).Body(obj).Do().Into(result)
return
}
func (c *oauthClientAuthorizations) List(opts kapi.ListOptions) (result *oauthapi.OAuthClientAuthorizationList, err error) {
result = &oauthapi.OAuthClientAuthorizationList{}
err = c.r.Get().Resource("oAuthClientAuthorizations").VersionedParams(&opts, kapi.ParameterCodec).Do().Into(result)
return
}
func (c *oauthClientAuthorizations) Get(name string) (result *oauthapi.OAuthClientAuthorization, err error) {
result = &oauthapi.OAuthClientAuthorization{}
err = c.r.Get().Resource("oAuthClientAuthorizations").Name(name).Do().Into(result)
return
}
func (c *oauthClientAuthorizations) Delete(name string) (err error) {
err = c.r.Delete().Resource("oAuthClientAuthorizations").Name(name).Do().Error()
return
}
func (c *oauthClientAuthorizations) Watch(opts kapi.ListOptions) (watch.Interface, error) {
return c.r.Get().Prefix("watch").Resource("oAuthClientAuthorizations").VersionedParams(&opts, kapi.ParameterCodec).Watch()
}
+72
View File
@@ -0,0 +1,72 @@
package client
import (
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/watch"
authorizationapi "github.com/openshift/origin/pkg/authorization/api"
)
// PoliciesNamespacer has methods to work with Policy resources in a namespace
type PoliciesNamespacer interface {
Policies(namespace string) PolicyInterface
}
// PolicyInterface exposes methods on Policy resources.
type PolicyInterface interface {
List(opts kapi.ListOptions) (*authorizationapi.PolicyList, error)
Get(name string) (*authorizationapi.Policy, error)
Delete(name string) error
Watch(opts kapi.ListOptions) (watch.Interface, error)
}
type PoliciesListerNamespacer interface {
Policies(namespace string) PolicyLister
}
type SyncedPoliciesListerNamespacer interface {
PoliciesListerNamespacer
LastSyncResourceVersion() string
}
type PolicyLister interface {
List(options kapi.ListOptions) (*authorizationapi.PolicyList, error)
Get(name string) (*authorizationapi.Policy, error)
}
// policies implements PoliciesNamespacer interface
type policies struct {
r *Client
ns string
}
// newPolicies returns a policies
func newPolicies(c *Client, namespace string) *policies {
return &policies{
r: c,
ns: namespace,
}
}
// List returns a list of policies that match the label and field selectors.
func (c *policies) List(opts kapi.ListOptions) (result *authorizationapi.PolicyList, err error) {
result = &authorizationapi.PolicyList{}
err = c.r.Get().Namespace(c.ns).Resource("policies").VersionedParams(&opts, kapi.ParameterCodec).Do().Into(result)
return
}
// Get returns information about a particular policy and error if one occurs.
func (c *policies) Get(name string) (result *authorizationapi.Policy, err error) {
result = &authorizationapi.Policy{}
err = c.r.Get().Namespace(c.ns).Resource("policies").Name(name).Do().Into(result)
return
}
// Delete deletes a policy, returns error if one occurs.
func (c *policies) Delete(name string) (err error) {
err = c.r.Delete().Namespace(c.ns).Resource("policies").Name(name).Do().Error()
return
}
// Watch returns a watch.Interface that watches the requested policies
func (c *policies) Watch(opts kapi.ListOptions) (watch.Interface, error) {
return c.r.Get().Prefix("watch").Namespace(c.ns).Resource("policies").VersionedParams(&opts, kapi.ParameterCodec).Watch()
}
+80
View File
@@ -0,0 +1,80 @@
package client
import (
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/watch"
authorizationapi "github.com/openshift/origin/pkg/authorization/api"
)
// PolicyBindingsNamespacer has methods to work with PolicyBinding resources in a namespace
type PolicyBindingsNamespacer interface {
PolicyBindings(namespace string) PolicyBindingInterface
}
// PolicyBindingInterface exposes methods on PolicyBinding resources.
type PolicyBindingInterface interface {
List(opts kapi.ListOptions) (*authorizationapi.PolicyBindingList, error)
Get(name string) (*authorizationapi.PolicyBinding, error)
Create(policyBinding *authorizationapi.PolicyBinding) (*authorizationapi.PolicyBinding, error)
Delete(name string) error
Watch(opts kapi.ListOptions) (watch.Interface, error)
}
type PolicyBindingsListerNamespacer interface {
PolicyBindings(namespace string) PolicyBindingLister
}
type SyncedPolicyBindingsListerNamespacer interface {
PolicyBindingsListerNamespacer
LastSyncResourceVersion() string
}
type PolicyBindingLister interface {
List(options kapi.ListOptions) (*authorizationapi.PolicyBindingList, error)
Get(name string) (*authorizationapi.PolicyBinding, error)
}
// policyBindings implements PolicyBindingsNamespacer interface
type policyBindings struct {
r *Client
ns string
}
// newPolicyBindings returns a policyBindings
func newPolicyBindings(c *Client, namespace string) *policyBindings {
return &policyBindings{
r: c,
ns: namespace,
}
}
// List returns a list of policyBindings that match the label and field selectors.
func (c *policyBindings) List(opts kapi.ListOptions) (result *authorizationapi.PolicyBindingList, err error) {
result = &authorizationapi.PolicyBindingList{}
err = c.r.Get().Namespace(c.ns).Resource("policyBindings").VersionedParams(&opts, kapi.ParameterCodec).Do().Into(result)
return
}
// Get returns information about a particular policyBinding and error if one occurs.
func (c *policyBindings) Get(name string) (result *authorizationapi.PolicyBinding, err error) {
result = &authorizationapi.PolicyBinding{}
err = c.r.Get().Namespace(c.ns).Resource("policyBindings").Name(name).Do().Into(result)
return
}
// Create creates new policyBinding. Returns the server's representation of the policyBinding and error if one occurs.
func (c *policyBindings) Create(policyBinding *authorizationapi.PolicyBinding) (result *authorizationapi.PolicyBinding, err error) {
result = &authorizationapi.PolicyBinding{}
err = c.r.Post().Namespace(c.ns).Resource("policyBindings").Body(policyBinding).Do().Into(result)
return
}
// Delete deletes a policyBinding, returns error if one occurs.
func (c *policyBindings) Delete(name string) (err error) {
err = c.r.Delete().Namespace(c.ns).Resource("policyBindings").Name(name).Do().Error()
return
}
// Watch returns a watch.Interface that watches the requested policyBindings
func (c *policyBindings) Watch(opts kapi.ListOptions) (watch.Interface, error) {
return c.r.Get().Prefix("watch").Namespace(c.ns).Resource("policyBindings").VersionedParams(&opts, kapi.ParameterCodec).Watch()
}
+44
View File
@@ -0,0 +1,44 @@
package client
import (
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
projectapi "github.com/openshift/origin/pkg/project/api"
)
// ProjectRequestsInterface has methods to work with ProjectRequest resources in a namespace
type ProjectRequestsInterface interface {
ProjectRequests() ProjectRequestInterface
}
// ProjectRequestInterface exposes methods on projectRequest resources.
type ProjectRequestInterface interface {
Create(p *projectapi.ProjectRequest) (*projectapi.Project, error)
List(opts kapi.ListOptions) (*unversioned.Status, error)
}
type projectRequests struct {
r *Client
}
// newUsers returns a users
func newProjectRequests(c *Client) *projectRequests {
return &projectRequests{
r: c,
}
}
// Create creates a new Project
func (c *projectRequests) Create(p *projectapi.ProjectRequest) (result *projectapi.Project, err error) {
result = &projectapi.Project{}
err = c.r.Post().Resource("projectRequests").Body(p).Do().Into(result)
return
}
// List returns a status object indicating that a user can call the Create or an error indicating why not
func (c *projectRequests) List(opts kapi.ListOptions) (result *unversioned.Status, err error) {
result = &unversioned.Status{}
err = c.r.Get().Resource("projectRequests").VersionedParams(&opts, kapi.ParameterCodec).Do().Into(result)
return result, err
}
+81
View File
@@ -0,0 +1,81 @@
package client
import (
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/watch"
projectapi "github.com/openshift/origin/pkg/project/api"
)
// ProjectsInterface has methods to work with Project resources in a namespace
type ProjectsInterface interface {
Projects() ProjectInterface
}
// ProjectInterface exposes methods on project resources.
type ProjectInterface interface {
Create(p *projectapi.Project) (*projectapi.Project, error)
Update(p *projectapi.Project) (*projectapi.Project, error)
Delete(name string) error
Get(name string) (*projectapi.Project, error)
List(opts kapi.ListOptions) (*projectapi.ProjectList, error)
Watch(opts kapi.ListOptions) (watch.Interface, error)
}
type projects struct {
r *Client
}
// newUsers returns a project
func newProjects(c *Client) *projects {
return &projects{
r: c,
}
}
// Get returns information about a particular project or an error
func (c *projects) Get(name string) (result *projectapi.Project, err error) {
result = &projectapi.Project{}
err = c.r.Get().Resource("projects").Name(name).Do().Into(result)
return
}
// List returns all projects matching the label selector
func (c *projects) List(opts kapi.ListOptions) (result *projectapi.ProjectList, err error) {
result = &projectapi.ProjectList{}
err = c.r.Get().
Resource("projects").
VersionedParams(&opts, kapi.ParameterCodec).
Do().
Into(result)
return
}
// Create creates a new Project
func (c *projects) Create(p *projectapi.Project) (result *projectapi.Project, err error) {
result = &projectapi.Project{}
err = c.r.Post().Resource("projects").Body(p).Do().Into(result)
return
}
// Update updates the project on server
func (c *projects) Update(p *projectapi.Project) (result *projectapi.Project, err error) {
result = &projectapi.Project{}
err = c.r.Put().Resource("projects").Name(p.Name).Body(p).Do().Into(result)
return
}
// Delete removes the project on server
func (c *projects) Delete(name string) (err error) {
err = c.r.Delete().Resource("projects").Name(name).Do().Error()
return
}
// Watch returns a watch.Interface that watches the requested namespaces.
func (c *projects) Watch(opts kapi.ListOptions) (watch.Interface, error) {
return c.r.Get().
Prefix("watch").
Resource("projects").
VersionedParams(&opts, kapi.ParameterCodec).
Watch()
}
+61
View File
@@ -0,0 +1,61 @@
package client
import (
kapierrors "k8s.io/kubernetes/pkg/api/errors"
authorizationapi "github.com/openshift/origin/pkg/authorization/api"
)
// ResourceAccessReviews has methods to work with ResourceAccessReview resources in the cluster scope
type ResourceAccessReviews interface {
ResourceAccessReviews() ResourceAccessReviewInterface
}
// ResourceAccessReviewInterface exposes methods on ResourceAccessReview resources.
type ResourceAccessReviewInterface interface {
Create(policy *authorizationapi.ResourceAccessReview) (*authorizationapi.ResourceAccessReviewResponse, error)
}
// resourceAccessReviews implements ResourceAccessReviews interface
type resourceAccessReviews struct {
r *Client
}
// newResourceAccessReviews returns a resourceAccessReviews
func newResourceAccessReviews(c *Client) *resourceAccessReviews {
return &resourceAccessReviews{
r: c,
}
}
func (c *resourceAccessReviews) Create(rar *authorizationapi.ResourceAccessReview) (result *authorizationapi.ResourceAccessReviewResponse, err error) {
result = &authorizationapi.ResourceAccessReviewResponse{}
// if this a cluster RAR, then no special handling
if len(rar.Action.Namespace) == 0 {
err = c.r.Post().Resource("resourceAccessReviews").Body(rar).Do().Into(result)
return
}
err = c.r.Post().Resource("resourceAccessReviews").Body(rar).Do().Into(result)
// if the namespace values don't match then we definitely hit an old server. If we got a forbidden, then we might have hit an old server
// and should try the old endpoint
if (rar.Action.Namespace != result.Namespace) || kapierrors.IsForbidden(err) {
deprecatedResponse := &authorizationapi.ResourceAccessReviewResponse{}
deprecatedAttemptErr := c.r.Post().Namespace(rar.Action.Namespace).Resource("resourceAccessReviews").Body(rar).Do().Into(deprecatedResponse)
// if we definitely hit an old server, then return the error and result you get from the older server.
if rar.Action.Namespace != result.Namespace {
return deprecatedResponse, deprecatedAttemptErr
}
// if we're not certain it was an old server, success overwrites the previous error, but failure doesn't overwrite the previous error
if deprecatedAttemptErr == nil {
err = nil
result = deprecatedResponse
}
}
return
}
+69
View File
@@ -0,0 +1,69 @@
package client
import (
kapi "k8s.io/kubernetes/pkg/api"
authorizationapi "github.com/openshift/origin/pkg/authorization/api"
)
// RoleBindingsNamespacer has methods to work with RoleBinding resources in a namespace
type RoleBindingsNamespacer interface {
RoleBindings(namespace string) RoleBindingInterface
}
// RoleBindingInterface exposes methods on RoleBinding resources.
type RoleBindingInterface interface {
List(opts kapi.ListOptions) (*authorizationapi.RoleBindingList, error)
Get(name string) (*authorizationapi.RoleBinding, error)
Create(roleBinding *authorizationapi.RoleBinding) (*authorizationapi.RoleBinding, error)
Update(roleBinding *authorizationapi.RoleBinding) (*authorizationapi.RoleBinding, error)
Delete(name string) error
}
// roleBindings implements RoleBindingsNamespacer interface
type roleBindings struct {
r *Client
ns string
}
// newRoleBindings returns a roleBindings
func newRoleBindings(c *Client, namespace string) *roleBindings {
return &roleBindings{
r: c,
ns: namespace,
}
}
// List returns a list of roleBindings that match the label and field selectors.
func (c *roleBindings) List(opts kapi.ListOptions) (result *authorizationapi.RoleBindingList, err error) {
result = &authorizationapi.RoleBindingList{}
err = c.r.Get().Namespace(c.ns).Resource("roleBindings").VersionedParams(&opts, kapi.ParameterCodec).Do().Into(result)
return
}
// Get returns information about a particular roleBinding and error if one occurs.
func (c *roleBindings) Get(name string) (result *authorizationapi.RoleBinding, err error) {
result = &authorizationapi.RoleBinding{}
err = c.r.Get().Namespace(c.ns).Resource("roleBindings").Name(name).Do().Into(result)
return
}
// Create creates new roleBinding. Returns the server's representation of the roleBinding and error if one occurs.
func (c *roleBindings) Create(roleBinding *authorizationapi.RoleBinding) (result *authorizationapi.RoleBinding, err error) {
result = &authorizationapi.RoleBinding{}
err = c.r.Post().Namespace(c.ns).Resource("roleBindings").Body(roleBinding).Do().Into(result)
return
}
// Update updates the roleBinding on server. Returns the server's representation of the roleBinding and error if one occurs.
func (c *roleBindings) Update(roleBinding *authorizationapi.RoleBinding) (result *authorizationapi.RoleBinding, err error) {
result = &authorizationapi.RoleBinding{}
err = c.r.Put().Namespace(c.ns).Resource("roleBindings").Name(roleBinding.Name).Body(roleBinding).Do().Into(result)
return
}
// Delete deletes a roleBinding, returns error if one occurs.
func (c *roleBindings) Delete(name string) (err error) {
err = c.r.Delete().Namespace(c.ns).Resource("roleBindings").Name(name).Do().Error()
return
}
+69
View File
@@ -0,0 +1,69 @@
package client
import (
kapi "k8s.io/kubernetes/pkg/api"
authorizationapi "github.com/openshift/origin/pkg/authorization/api"
)
// RolesNamespacer has methods to work with Role resources in a namespace
type RolesNamespacer interface {
Roles(namespace string) RoleInterface
}
// RoleInterface exposes methods on Role resources.
type RoleInterface interface {
List(opts kapi.ListOptions) (*authorizationapi.RoleList, error)
Get(name string) (*authorizationapi.Role, error)
Create(role *authorizationapi.Role) (*authorizationapi.Role, error)
Update(role *authorizationapi.Role) (*authorizationapi.Role, error)
Delete(name string) error
}
// roles implements RolesNamespacer interface
type roles struct {
r *Client
ns string
}
// newRoles returns a roles
func newRoles(c *Client, namespace string) *roles {
return &roles{
r: c,
ns: namespace,
}
}
// List returns a list of roles that match the label and field selectors.
func (c *roles) List(opts kapi.ListOptions) (result *authorizationapi.RoleList, err error) {
result = &authorizationapi.RoleList{}
err = c.r.Get().Namespace(c.ns).Resource("roles").VersionedParams(&opts, kapi.ParameterCodec).Do().Into(result)
return
}
// Get returns information about a particular role and error if one occurs.
func (c *roles) Get(name string) (result *authorizationapi.Role, err error) {
result = &authorizationapi.Role{}
err = c.r.Get().Namespace(c.ns).Resource("roles").Name(name).Do().Into(result)
return
}
// Create creates new role. Returns the server's representation of the role and error if one occurs.
func (c *roles) Create(role *authorizationapi.Role) (result *authorizationapi.Role, err error) {
result = &authorizationapi.Role{}
err = c.r.Post().Namespace(c.ns).Resource("roles").Body(role).Do().Into(result)
return
}
// Update updates the role on server. Returns the server's representation of the role and error if one occurs.
func (c *roles) Update(role *authorizationapi.Role) (result *authorizationapi.Role, err error) {
result = &authorizationapi.Role{}
err = c.r.Put().Namespace(c.ns).Resource("roles").Name(role.Name).Body(role).Do().Into(result)
return
}
// Delete deletes a role, returns error if one occurs.
func (c *roles) Delete(name string) (err error) {
err = c.r.Delete().Namespace(c.ns).Resource("roles").Name(name).Do().Error()
return
}
+93
View File
@@ -0,0 +1,93 @@
package client
import (
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/watch"
routeapi "github.com/openshift/origin/pkg/route/api"
)
// RoutesNamespacer has methods to work with Route resources in a namespace
type RoutesNamespacer interface {
Routes(namespace string) RouteInterface
}
// RouteInterface exposes methods on Route resources
type RouteInterface interface {
List(opts kapi.ListOptions) (*routeapi.RouteList, error)
Get(name string) (*routeapi.Route, error)
Create(route *routeapi.Route) (*routeapi.Route, error)
Update(route *routeapi.Route) (*routeapi.Route, error)
UpdateStatus(route *routeapi.Route) (*routeapi.Route, error)
Delete(name string) error
Watch(opts kapi.ListOptions) (watch.Interface, error)
}
// routes implements RouteInterface interface
type routes struct {
r *Client
ns string
}
// newRoutes returns a routes
func newRoutes(c *Client, namespace string) *routes {
return &routes{
r: c,
ns: namespace,
}
}
// List takes a label and field selector, and returns the list of routes that match that selectors
func (c *routes) List(opts kapi.ListOptions) (result *routeapi.RouteList, err error) {
result = &routeapi.RouteList{}
err = c.r.Get().
Namespace(c.ns).
Resource("routes").
VersionedParams(&opts, kapi.ParameterCodec).
Do().
Into(result)
return
}
// Get takes the name of the route, and returns the corresponding Route object, and an error if it occurs
func (c *routes) Get(name string) (result *routeapi.Route, err error) {
result = &routeapi.Route{}
err = c.r.Get().Namespace(c.ns).Resource("routes").Name(name).Do().Into(result)
return
}
// Delete takes the name of the route, and returns an error if one occurs
func (c *routes) Delete(name string) error {
return c.r.Delete().Namespace(c.ns).Resource("routes").Name(name).Do().Error()
}
// Create takes the representation of a route. Returns the server's representation of the route, and an error, if it occurs
func (c *routes) Create(route *routeapi.Route) (result *routeapi.Route, err error) {
result = &routeapi.Route{}
err = c.r.Post().Namespace(c.ns).Resource("routes").Body(route).Do().Into(result)
return
}
// Update takes the representation of a route to update. Returns the server's representation of the route, and an error, if it occurs
func (c *routes) Update(route *routeapi.Route) (result *routeapi.Route, err error) {
result = &routeapi.Route{}
err = c.r.Put().Namespace(c.ns).Resource("routes").Name(route.Name).Body(route).Do().Into(result)
return
}
// UpdateStatus takes the route with altered status. Returns the server's representation of the route, and an error, if it occurs.
func (c *routes) UpdateStatus(route *routeapi.Route) (result *routeapi.Route, err error) {
result = &routeapi.Route{}
err = c.r.Put().Namespace(c.ns).Resource("routes").Name(route.Name).SubResource("status").Body(route).Do().Into(result)
return
}
// Watch returns a watch.Interface that watches the requested routes.
func (c *routes) Watch(opts kapi.ListOptions) (watch.Interface, error) {
return c.r.Get().
Prefix("watch").
Namespace(c.ns).
Resource("routes").
VersionedParams(&opts, kapi.ParameterCodec).
Watch()
}
+63
View File
@@ -0,0 +1,63 @@
package client
import (
"fmt"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/apis/extensions"
unversioned_extensions "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/unversioned"
kclient "k8s.io/kubernetes/pkg/client/unversioned"
"github.com/openshift/origin/pkg/api/latest"
)
type delegatingScaleInterface struct {
dcs DeploymentConfigInterface
scales kclient.ScaleInterface
}
type delegatingScaleNamespacer struct {
dcNS DeploymentConfigsNamespacer
scaleNS kclient.ScaleNamespacer
}
func (c *delegatingScaleNamespacer) Scales(namespace string) unversioned_extensions.ScaleInterface {
return &delegatingScaleInterface{
dcs: c.dcNS.DeploymentConfigs(namespace),
scales: c.scaleNS.Scales(namespace),
}
}
func NewDelegatingScaleNamespacer(dcNamespacer DeploymentConfigsNamespacer, sNamespacer kclient.ScaleNamespacer) unversioned_extensions.ScalesGetter {
return &delegatingScaleNamespacer{
dcNS: dcNamespacer,
scaleNS: sNamespacer,
}
}
// Get takes the reference to scale subresource and returns the subresource or error, if one occurs.
func (c *delegatingScaleInterface) Get(kind string, name string) (result *extensions.Scale, err error) {
switch {
case kind == "DeploymentConfig":
return c.dcs.GetScale(name)
// TODO: This is borked because the interface for Get is broken. Kind is insufficient.
case latest.IsKindInAnyOriginGroup(kind):
return nil, errors.NewBadRequest(fmt.Sprintf("Kind %s has no Scale subresource", kind))
default:
return c.scales.Get(kind, name)
}
}
// Update takes a scale subresource object, updates the stored version to match it, and
// returns the subresource or error, if one occurs.
func (c *delegatingScaleInterface) Update(kind string, scale *extensions.Scale) (result *extensions.Scale, err error) {
switch {
case kind == "DeploymentConfig":
return c.dcs.UpdateScale(scale)
// TODO: This is borked because the interface for Update is broken. Kind is insufficient.
case latest.IsKindInAnyOriginGroup(kind):
return nil, errors.NewBadRequest(fmt.Sprintf("Kind %s has no Scale subresource", kind))
default:
return c.scales.Update(kind, scale)
}
}
@@ -0,0 +1,32 @@
package client
import (
authorizationapi "github.com/openshift/origin/pkg/authorization/api"
)
type SelfSubjectRulesReviewsNamespacer interface {
SelfSubjectRulesReviews(namespace string) SelfSubjectRulesReviewInterface
}
type SelfSubjectRulesReviewInterface interface {
Create(*authorizationapi.SelfSubjectRulesReview) (*authorizationapi.SelfSubjectRulesReview, error)
}
type selfSubjectRulesReviews struct {
r *Client
ns string
}
func newSelfSubjectRulesReviews(c *Client, namespace string) *selfSubjectRulesReviews {
return &selfSubjectRulesReviews{
r: c,
ns: namespace,
}
}
func (c *selfSubjectRulesReviews) Create(selfSubjectRulesReview *authorizationapi.SelfSubjectRulesReview) (result *authorizationapi.SelfSubjectRulesReview, err error) {
result = &authorizationapi.SelfSubjectRulesReview{}
err = c.r.Post().Namespace(c.ns).Resource("selfSubjectRulesReviews").Body(selfSubjectRulesReview).Do().Into(result)
return
}
+100
View File
@@ -0,0 +1,100 @@
package client
import (
"errors"
"fmt"
kapierrors "k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/client/restclient"
authorizationapi "github.com/openshift/origin/pkg/authorization/api"
)
type SubjectAccessReviewsImpersonator interface {
ImpersonateSubjectAccessReviews(token string) SubjectAccessReviewInterface
}
// SubjectAccessReviews has methods to work with SubjectAccessReview resources in the cluster scope
type SubjectAccessReviews interface {
SubjectAccessReviews() SubjectAccessReviewInterface
}
// SubjectAccessReviewInterface exposes methods on SubjectAccessReview resources.
type SubjectAccessReviewInterface interface {
Create(policy *authorizationapi.SubjectAccessReview) (*authorizationapi.SubjectAccessReviewResponse, error)
}
// subjectAccessReviews implements SubjectAccessReviews interface
type subjectAccessReviews struct {
r *Client
token *string
}
// newImpersonatingSubjectAccessReviews returns a subjectAccessReviews
func newImpersonatingSubjectAccessReviews(c *Client, token string) *subjectAccessReviews {
return &subjectAccessReviews{
r: c,
token: &token,
}
}
// newSubjectAccessReviews returns a subjectAccessReviews
func newSubjectAccessReviews(c *Client) *subjectAccessReviews {
return &subjectAccessReviews{
r: c,
}
}
func (c *subjectAccessReviews) Create(sar *authorizationapi.SubjectAccessReview) (*authorizationapi.SubjectAccessReviewResponse, error) {
result := &authorizationapi.SubjectAccessReviewResponse{}
// if this a cluster SAR, then no special handling
if len(sar.Action.Namespace) == 0 {
req, err := overrideAuth(c.token, c.r.Post().Resource("subjectAccessReviews"))
if err != nil {
return &authorizationapi.SubjectAccessReviewResponse{}, err
}
err = req.Body(sar).Do().Into(result)
return result, err
}
err := c.r.Post().Resource("subjectAccessReviews").Body(sar).Do().Into(result)
// if the namespace values don't match then we definitely hit an old server. If we got a forbidden, then we might have hit an old server
// and should try the old endpoint
if (sar.Action.Namespace != result.Namespace) || kapierrors.IsForbidden(err) {
deprecatedReq, deprecatedAttemptErr := overrideAuth(c.token, c.r.Post().Namespace(sar.Action.Namespace).Resource("subjectAccessReviews"))
if deprecatedAttemptErr != nil {
return &authorizationapi.SubjectAccessReviewResponse{}, deprecatedAttemptErr
}
deprecatedResponse := &authorizationapi.SubjectAccessReviewResponse{}
deprecatedAttemptErr = deprecatedReq.Body(sar).Do().Into(deprecatedResponse)
// if we definitely hit an old server, then return the error and result you get from the older server.
if sar.Action.Namespace != result.Namespace {
return deprecatedResponse, deprecatedAttemptErr
}
// if we're not certain it was an old server, success overwrites the previous error, but failure doesn't overwrite the previous error
if deprecatedAttemptErr == nil {
err = nil
result = deprecatedResponse
}
}
return result, err
}
// overrideAuth specifies the token to authenticate the request with. token == "" is not allowed
func overrideAuth(token *string, req *restclient.Request) (*restclient.Request, error) {
if token != nil {
if len(*token) == 0 {
return nil, errors.New("impersonating token may not be empty")
}
req.SetHeader("Authorization", fmt.Sprintf("Bearer %s", *token))
}
return req, nil
}
+37
View File
@@ -0,0 +1,37 @@
package client
import (
templateapi "github.com/openshift/origin/pkg/template/api"
)
// TemplateConfigNamespacer has methods to work with Image resources in a namespace
// TODO: Rename to ProcessedTemplates
type TemplateConfigsNamespacer interface {
TemplateConfigs(namespace string) TemplateConfigInterface
}
// TemplateConfigInterface exposes methods on Image resources.
type TemplateConfigInterface interface {
Create(t *templateapi.Template) (*templateapi.Template, error)
}
// templateConfigs implements TemplateConfigsNamespacer interface
type templateConfigs struct {
r *Client
ns string
}
// newTemplateConfigs returns an TemplateConfigInterface
func newTemplateConfigs(c *Client, namespace string) TemplateConfigInterface {
return &templateConfigs{
r: c,
ns: namespace,
}
}
// Create process the Template and returns its current state
func (c *templateConfigs) Create(in *templateapi.Template) (*templateapi.Template, error) {
template := &templateapi.Template{}
err := c.r.Post().Namespace(c.ns).Resource("processedTemplates").Body(in).Do().Into(template)
return template, err
}
+86
View File
@@ -0,0 +1,86 @@
package client
import (
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/watch"
templateapi "github.com/openshift/origin/pkg/template/api"
)
// TemplatesNamespacer has methods to work with Template resources in a namespace
type TemplatesNamespacer interface {
Templates(namespace string) TemplateInterface
}
// TemplateInterface exposes methods on Template resources.
type TemplateInterface interface {
List(opts kapi.ListOptions) (*templateapi.TemplateList, error)
Get(name string) (*templateapi.Template, error)
Create(template *templateapi.Template) (*templateapi.Template, error)
Update(template *templateapi.Template) (*templateapi.Template, error)
Delete(name string) error
Watch(opts kapi.ListOptions) (watch.Interface, error)
}
// templates implements TemplatesNamespacer interface
type templates struct {
r *Client
ns string
}
// newTemplates returns a templates
func newTemplates(c *Client, namespace string) *templates {
return &templates{
r: c,
ns: namespace,
}
}
// List returns a list of templates that match the label and field selectors.
func (c *templates) List(opts kapi.ListOptions) (result *templateapi.TemplateList, err error) {
result = &templateapi.TemplateList{}
err = c.r.Get().
Namespace(c.ns).
Resource("templates").
VersionedParams(&opts, kapi.ParameterCodec).
Do().
Into(result)
return
}
// Get returns information about a particular template and error if one occurs.
func (c *templates) Get(name string) (result *templateapi.Template, err error) {
result = &templateapi.Template{}
err = c.r.Get().Namespace(c.ns).Resource("templates").Name(name).Do().Into(result)
return
}
// Create creates new template. Returns the server's representation of the template and error if one occurs.
func (c *templates) Create(template *templateapi.Template) (result *templateapi.Template, err error) {
result = &templateapi.Template{}
err = c.r.Post().Namespace(c.ns).Resource("templates").Body(template).Do().Into(result)
return
}
// Update updates the template on server. Returns the server's representation of the template and error if one occurs.
func (c *templates) Update(template *templateapi.Template) (result *templateapi.Template, err error) {
result = &templateapi.Template{}
err = c.r.Put().Namespace(c.ns).Resource("templates").Name(template.Name).Body(template).Do().Into(result)
return
}
// Delete deletes a template, returns error if one occurs.
func (c *templates) Delete(name string) (err error) {
err = c.r.Delete().Namespace(c.ns).Resource("templates").Name(name).Do().Error()
return
}
// Watch returns a watch.Interface that watches the requested templates
func (c *templates) Watch(opts kapi.ListOptions) (watch.Interface, error) {
return c.r.Get().
Prefix("watch").
Namespace(c.ns).
Resource("templates").
VersionedParams(&opts, kapi.ParameterCodec).
Watch()
}
+57
View File
@@ -0,0 +1,57 @@
package client
import (
userapi "github.com/openshift/origin/pkg/user/api"
)
// UserIdentityMappingsInterface has methods to work with UserIdentityMapping resources in a namespace
type UserIdentityMappingsInterface interface {
UserIdentityMappings() UserIdentityMappingInterface
}
// UserIdentityMappingInterface exposes methods on UserIdentityMapping resources.
type UserIdentityMappingInterface interface {
Get(string) (*userapi.UserIdentityMapping, error)
Create(*userapi.UserIdentityMapping) (*userapi.UserIdentityMapping, error)
Update(*userapi.UserIdentityMapping) (*userapi.UserIdentityMapping, error)
Delete(string) error
}
// userIdentityMappings implements UserIdentityMappingsNamespacer interface
type userIdentityMappings struct {
r *Client
}
// newUserIdentityMappings returns a userIdentityMappings
func newUserIdentityMappings(c *Client) *userIdentityMappings {
return &userIdentityMappings{
r: c,
}
}
// Get returns information about a particular mapping or an error
func (c *userIdentityMappings) Get(name string) (result *userapi.UserIdentityMapping, err error) {
result = &userapi.UserIdentityMapping{}
err = c.r.Get().Resource("userIdentityMappings").Name(name).Do().Into(result)
return
}
// Create creates a new mapping. Returns the server's representation of the mapping and error if one occurs.
func (c *userIdentityMappings) Create(mapping *userapi.UserIdentityMapping) (result *userapi.UserIdentityMapping, err error) {
result = &userapi.UserIdentityMapping{}
err = c.r.Post().Resource("userIdentityMappings").Body(mapping).Do().Into(result)
return
}
// Update updates the mapping on server. Returns the server's representation of the mapping and error if one occurs.
func (c *userIdentityMappings) Update(mapping *userapi.UserIdentityMapping) (result *userapi.UserIdentityMapping, err error) {
result = &userapi.UserIdentityMapping{}
err = c.r.Put().Resource("userIdentityMappings").Name(mapping.Name).Body(mapping).Do().Into(result)
return
}
// Delete deletes the mapping on server.
func (c *userIdentityMappings) Delete(name string) (err error) {
err = c.r.Delete().Resource("userIdentityMappings").Name(name).Do().Error()
return
}
+81
View File
@@ -0,0 +1,81 @@
package client
import (
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/watch"
userapi "github.com/openshift/origin/pkg/user/api"
)
// UsersInterface has methods to work with User resources
type UsersInterface interface {
Users() UserInterface
}
// UserInterface exposes methods on user resources.
type UserInterface interface {
List(opts kapi.ListOptions) (*userapi.UserList, error)
Get(name string) (*userapi.User, error)
Create(user *userapi.User) (*userapi.User, error)
Update(user *userapi.User) (*userapi.User, error)
Delete(name string) error
Watch(opts kapi.ListOptions) (watch.Interface, error)
}
// users implements UserInterface interface
type users struct {
r *Client
}
// newUsers returns a users
func newUsers(c *Client) *users {
return &users{
r: c,
}
}
// List returns a list of users that match the label and field selectors.
func (c *users) List(opts kapi.ListOptions) (result *userapi.UserList, err error) {
result = &userapi.UserList{}
err = c.r.Get().
Resource("users").
VersionedParams(&opts, kapi.ParameterCodec).
Do().
Into(result)
return
}
// Get returns information about a particular user or an error
func (c *users) Get(name string) (result *userapi.User, err error) {
result = &userapi.User{}
err = c.r.Get().Resource("users").Name(name).Do().Into(result)
return
}
// Create creates a new user. Returns the server's representation of the user and error if one occurs.
func (c *users) Create(user *userapi.User) (result *userapi.User, err error) {
result = &userapi.User{}
err = c.r.Post().Resource("users").Body(user).Do().Into(result)
return
}
// Update updates the user on server. Returns the server's representation of the user and error if one occurs.
func (c *users) Update(user *userapi.User) (result *userapi.User, err error) {
result = &userapi.User{}
err = c.r.Put().Resource("users").Name(user.Name).Body(user).Do().Into(result)
return
}
// Delete deletes the user on server. Returns an error if one occurs.
func (c *users) Delete(name string) (err error) {
return c.r.Delete().Resource("users").Name(name).Do().Error()
}
// Watch returns a watch.Interface that watches the requested users.
func (c *users) Watch(opts kapi.ListOptions) (watch.Interface, error) {
return c.r.Get().
Prefix("watch").
Resource("users").
VersionedParams(&opts, kapi.ParameterCodec).
Watch()
}
+103
View File
@@ -0,0 +1,103 @@
package config
import (
"fmt"
"net"
"net/url"
"strconv"
"strings"
"github.com/openshift/origin/pkg/cmd/util"
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
)
// TODO should be moved upstream
func RelativizeClientConfigPaths(cfg *clientcmdapi.Config, base string) (err error) {
for k, cluster := range cfg.Clusters {
if len(cluster.CertificateAuthority) > 0 {
if cluster.CertificateAuthority, err = util.MakeAbs(cluster.CertificateAuthority, ""); err != nil {
return err
}
if cluster.CertificateAuthority, err = util.MakeRelative(cluster.CertificateAuthority, base); err != nil {
return err
}
cfg.Clusters[k] = cluster
}
}
for k, authInfo := range cfg.AuthInfos {
if len(authInfo.ClientCertificate) > 0 {
if authInfo.ClientCertificate, err = util.MakeAbs(authInfo.ClientCertificate, ""); err != nil {
return err
}
if authInfo.ClientCertificate, err = util.MakeRelative(authInfo.ClientCertificate, base); err != nil {
return err
}
}
if len(authInfo.ClientKey) > 0 {
if authInfo.ClientKey, err = util.MakeAbs(authInfo.ClientKey, ""); err != nil {
return err
}
if authInfo.ClientKey, err = util.MakeRelative(authInfo.ClientKey, base); err != nil {
return err
}
}
cfg.AuthInfos[k] = authInfo
}
return nil
}
var validURLSchemes = []string{"https://", "http://", "tcp://"}
// NormalizeServerURL is opinionated normalization of a string that represents a URL. Returns the URL provided matching the format
// expected when storing a URL in a config. Sets a scheme and port if not present, removes unnecessary trailing
// slashes, etc. Can be used to normalize a URL provided by user input.
func NormalizeServerURL(s string) (string, error) {
// normalize scheme
if !hasScheme(s) {
s = validURLSchemes[0] + s
}
addr, err := url.Parse(s)
if err != nil {
return "", fmt.Errorf("Not a valid URL: %v.", err)
}
// normalize host:port
if strings.Contains(addr.Host, ":") {
_, port, err := net.SplitHostPort(addr.Host)
if err != nil {
return "", fmt.Errorf("Not a valid host:port: %v.", err)
}
_, err = strconv.ParseUint(port, 10, 16)
if err != nil {
return "", fmt.Errorf("Not a valid port: %v. Port numbers must be between 0 and 65535.", port)
}
} else {
port := 0
switch addr.Scheme {
case "http":
port = 80
case "https":
port = 443
default:
return "", fmt.Errorf("No port specified.")
}
addr.Host = net.JoinHostPort(addr.Host, strconv.FormatInt(int64(port), 10))
}
// remove trailing slash if that's the only path we have
if addr.Path == "/" {
addr.Path = ""
}
return addr.String(), nil
}
func hasScheme(s string) bool {
for _, p := range validURLSchemes {
if strings.HasPrefix(s, p) {
return true
}
}
return false
}
+76
View File
@@ -0,0 +1,76 @@
package config
import (
"os"
"path"
"path/filepath"
"runtime"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
kclientcmd "k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/util/homedir"
)
const (
OpenShiftConfigPathEnvVar = "KUBECONFIG"
OpenShiftConfigFlagName = "config"
OpenShiftConfigHomeDir = ".kube"
OpenShiftConfigHomeFileName = "config"
OpenShiftConfigHomeDirFileName = OpenShiftConfigHomeDir + "/" + OpenShiftConfigHomeFileName
)
var RecommendedHomeFile = path.Join(homedir.HomeDir(), OpenShiftConfigHomeDirFileName)
// currentMigrationRules returns a map that holds the history of recommended home directories used in previous versions.
// Any future changes to RecommendedHomeFile and related are expected to add a migration rule here, in order to make
// sure existing config files are migrated to their new locations properly.
func currentMigrationRules() map[string]string {
oldRecommendedHomeFile := path.Join(homedir.HomeDir(), ".kube/.config")
oldRecommendedWindowsHomeFile := path.Join(os.Getenv("HOME"), OpenShiftConfigHomeDirFileName)
migrationRules := map[string]string{}
migrationRules[RecommendedHomeFile] = oldRecommendedHomeFile
if runtime.GOOS == "windows" {
migrationRules[RecommendedHomeFile] = oldRecommendedWindowsHomeFile
}
return migrationRules
}
// NewOpenShiftClientConfigLoadingRules returns file priority loading rules for OpenShift.
// 1. --config value
// 2. if KUBECONFIG env var has a value, use it. Otherwise, ~/.kube/config file
func NewOpenShiftClientConfigLoadingRules() *clientcmd.ClientConfigLoadingRules {
chain := []string{}
envVarFile := os.Getenv(OpenShiftConfigPathEnvVar)
if len(envVarFile) != 0 {
chain = append(chain, filepath.SplitList(envVarFile)...)
} else {
chain = append(chain, RecommendedHomeFile)
}
return &clientcmd.ClientConfigLoadingRules{
Precedence: chain,
MigrationRules: currentMigrationRules(),
}
}
func NewPathOptions(cmd *cobra.Command) *kclientcmd.PathOptions {
return NewPathOptionsWithConfig(kcmdutil.GetFlagString(cmd, OpenShiftConfigFlagName))
}
func NewPathOptionsWithConfig(configPath string) *kclientcmd.PathOptions {
return &kclientcmd.PathOptions{
GlobalFile: RecommendedHomeFile,
EnvVar: OpenShiftConfigPathEnvVar,
ExplicitFileFlag: OpenShiftConfigFlagName,
LoadingRules: &kclientcmd.ClientConfigLoadingRules{
ExplicitPath: configPath,
},
}
}
+194
View File
@@ -0,0 +1,194 @@
package config
import (
"crypto/x509"
"net/url"
"reflect"
"strings"
"k8s.io/kubernetes/pkg/client/restclient"
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
"k8s.io/kubernetes/third_party/forked/golang/netutil"
"github.com/openshift/origin/pkg/auth/authenticator/request/x509request"
osclient "github.com/openshift/origin/pkg/client"
)
// GetClusterNicknameFromConfig returns host:port of the clientConfig.Host, with .'s replaced by -'s
func GetClusterNicknameFromConfig(clientCfg *restclient.Config) (string, error) {
return GetClusterNicknameFromURL(clientCfg.Host)
}
// GetClusterNicknameFromURL returns host:port of the apiServerLocation, with .'s replaced by -'s
func GetClusterNicknameFromURL(apiServerLocation string) (string, error) {
u, err := url.Parse(apiServerLocation)
if err != nil {
return "", err
}
hostPort := netutil.CanonicalAddr(u)
// we need a character other than "." to avoid conflicts with. replace with '-'
return strings.Replace(hostPort, ".", "-", -1), nil
}
// GetUserNicknameFromConfig returns "username(as known by the server)/GetClusterNicknameFromConfig". This allows tab completion for switching users to
// work easily and obviously.
func GetUserNicknameFromConfig(clientCfg *restclient.Config) (string, error) {
client, err := osclient.New(clientCfg)
if err != nil {
return "", err
}
userInfo, err := client.Users().Get("~")
if err != nil {
return "", err
}
clusterNick, err := GetClusterNicknameFromConfig(clientCfg)
if err != nil {
return "", err
}
return userInfo.Name + "/" + clusterNick, nil
}
func GetUserNicknameFromCert(clusterNick string, chain ...*x509.Certificate) (string, error) {
userInfo, _, err := x509request.SubjectToUserConversion(chain)
if err != nil {
return "", err
}
return userInfo.GetName() + "/" + clusterNick, nil
}
// GetContextNicknameFromConfig returns "namespace/GetClusterNicknameFromConfig/username(as known by the server)". This allows tab completion for switching projects/context
// to work easily. First tab is the most selective on project. Second stanza in the next most selective on cluster name. The chances of a user trying having
// one projects on a single server that they want to operate against with two identities is low, so username is last.
func GetContextNicknameFromConfig(namespace string, clientCfg *restclient.Config) (string, error) {
client, err := osclient.New(clientCfg)
if err != nil {
return "", err
}
userInfo, err := client.Users().Get("~")
if err != nil {
return "", err
}
clusterNick, err := GetClusterNicknameFromConfig(clientCfg)
if err != nil {
return "", err
}
return namespace + "/" + clusterNick + "/" + userInfo.Name, nil
}
func GetContextNickname(namespace, clusterNick, userNick string) string {
tokens := strings.SplitN(userNick, "/", 2)
return namespace + "/" + clusterNick + "/" + tokens[0]
}
// CreateConfig takes a clientCfg and builds a config (kubeconfig style) from it.
func CreateConfig(namespace string, clientCfg *restclient.Config) (*clientcmdapi.Config, error) {
clusterNick, err := GetClusterNicknameFromConfig(clientCfg)
if err != nil {
return nil, err
}
userNick, err := GetUserNicknameFromConfig(clientCfg)
if err != nil {
return nil, err
}
contextNick, err := GetContextNicknameFromConfig(namespace, clientCfg)
if err != nil {
return nil, err
}
config := clientcmdapi.NewConfig()
credentials := clientcmdapi.NewAuthInfo()
credentials.Token = clientCfg.BearerToken
credentials.ClientCertificate = clientCfg.TLSClientConfig.CertFile
if len(credentials.ClientCertificate) == 0 {
credentials.ClientCertificateData = clientCfg.TLSClientConfig.CertData
}
credentials.ClientKey = clientCfg.TLSClientConfig.KeyFile
if len(credentials.ClientKey) == 0 {
credentials.ClientKeyData = clientCfg.TLSClientConfig.KeyData
}
config.AuthInfos[userNick] = credentials
cluster := clientcmdapi.NewCluster()
cluster.Server = clientCfg.Host
cluster.CertificateAuthority = clientCfg.CAFile
if len(cluster.CertificateAuthority) == 0 {
cluster.CertificateAuthorityData = clientCfg.CAData
}
cluster.InsecureSkipTLSVerify = clientCfg.Insecure
if clientCfg.GroupVersion != nil {
cluster.APIVersion = clientCfg.GroupVersion.String()
}
config.Clusters[clusterNick] = cluster
context := clientcmdapi.NewContext()
context.Cluster = clusterNick
context.AuthInfo = userNick
context.Namespace = namespace
config.Contexts[contextNick] = context
config.CurrentContext = contextNick
return config, nil
}
// MergeConfig adds the additional Config stanzas to the startingConfig. It blindly stomps clusters and users, but
// it searches for a matching context before writing a new one.
func MergeConfig(startingConfig, addition clientcmdapi.Config) (*clientcmdapi.Config, error) {
ret := startingConfig
for requestedKey, value := range addition.Clusters {
ret.Clusters[requestedKey] = value
}
for requestedKey, value := range addition.AuthInfos {
ret.AuthInfos[requestedKey] = value
}
requestedContextNamesToActualContextNames := map[string]string{}
for requestedKey, newContext := range addition.Contexts {
actualContext := clientcmdapi.NewContext()
actualContext.AuthInfo = newContext.AuthInfo
actualContext.Cluster = newContext.Cluster
actualContext.Namespace = newContext.Namespace
actualContext.Extensions = newContext.Extensions
if existingName := FindExistingContextName(startingConfig, *actualContext); len(existingName) > 0 {
// if this already exists, just move to the next, our job is done
requestedContextNamesToActualContextNames[requestedKey] = existingName
continue
}
requestedContextNamesToActualContextNames[requestedKey] = requestedKey
ret.Contexts[requestedKey] = actualContext
}
if len(addition.CurrentContext) > 0 {
if newCurrentContext, exists := requestedContextNamesToActualContextNames[addition.CurrentContext]; exists {
ret.CurrentContext = newCurrentContext
} else {
ret.CurrentContext = addition.CurrentContext
}
}
return &ret, nil
}
// FindExistingContextName finds the nickname for the passed context
func FindExistingContextName(haystack clientcmdapi.Config, needle clientcmdapi.Context) string {
for key, context := range haystack.Contexts {
context.LocationOfOrigin = ""
if reflect.DeepEqual(context, needle) {
return key
}
}
return ""
}
@@ -0,0 +1,319 @@
package describe
import (
"fmt"
"sort"
"strings"
"github.com/golang/glog"
"github.com/gonum/graph"
"github.com/gonum/graph/encoding/dot"
"github.com/gonum/graph/path"
kapi "k8s.io/kubernetes/pkg/api"
utilerrors "k8s.io/kubernetes/pkg/util/errors"
"k8s.io/kubernetes/pkg/util/sets"
osgraph "github.com/openshift/origin/pkg/api/graph"
buildedges "github.com/openshift/origin/pkg/build/graph"
buildanalysis "github.com/openshift/origin/pkg/build/graph/analysis"
buildgraph "github.com/openshift/origin/pkg/build/graph/nodes"
"github.com/openshift/origin/pkg/client"
imageapi "github.com/openshift/origin/pkg/image/api"
imagegraph "github.com/openshift/origin/pkg/image/graph/nodes"
dotutil "github.com/openshift/origin/pkg/util/dot"
"github.com/openshift/origin/pkg/util/parallel"
)
// NotFoundErr is returned when the imageStreamTag (ist) of interest cannot
// be found in the graph. This doesn't mean though that the IST does not
// exist. A user may have an image stream without a build configuration
// pointing at it. In that case, the IST of interest simply doesn't have
// other dependant ists
type NotFoundErr string
func (e NotFoundErr) Error() string {
return fmt.Sprintf("couldn't find image stream tag: %q", string(e))
}
// ChainDescriber generates extended information about a chain of
// dependencies of an image stream
type ChainDescriber struct {
c client.BuildConfigsNamespacer
namespaces sets.String
outputFormat string
namer osgraph.Namer
}
// NewChainDescriber returns a new ChainDescriber
func NewChainDescriber(c client.BuildConfigsNamespacer, namespaces sets.String, out string) *ChainDescriber {
return &ChainDescriber{c: c, namespaces: namespaces, outputFormat: out, namer: namespacedFormatter{hideNamespace: true}}
}
// MakeGraph will create the graph of all build configurations and the image streams
// they point to via image change triggers in the provided namespace(s)
func (d *ChainDescriber) MakeGraph() (osgraph.Graph, error) {
g := osgraph.New()
loaders := []GraphLoader{}
for namespace := range d.namespaces {
glog.V(4).Infof("Loading build configurations from %q", namespace)
loaders = append(loaders, &bcLoader{namespace: namespace, lister: d.c})
}
loadingFuncs := []func() error{}
for _, loader := range loaders {
loadingFuncs = append(loadingFuncs, loader.Load)
}
if errs := parallel.Run(loadingFuncs...); len(errs) > 0 {
return g, utilerrors.NewAggregate(errs)
}
for _, loader := range loaders {
loader.AddToGraph(g)
}
buildedges.AddAllInputOutputEdges(g)
return g, nil
}
// Describe returns the output of the graph starting from the provided
// image stream tag (name:tag) in namespace. Namespace is needed here
// because image stream tags with the same name can be found across
// different namespaces.
func (d *ChainDescriber) Describe(ist *imageapi.ImageStreamTag, includeInputImages, reverse bool) (string, error) {
g, err := d.MakeGraph()
if err != nil {
return "", err
}
// Retrieve the imageStreamTag node of interest
istNode := g.Find(imagegraph.ImageStreamTagNodeName(ist))
if istNode == nil {
return "", NotFoundErr(fmt.Sprintf("%q", ist.Name))
}
markers := buildanalysis.FindCircularBuilds(g, d.namer)
if len(markers) > 0 {
for _, marker := range markers {
if strings.Contains(marker.Message, ist.Name) {
return marker.Message, nil
}
}
}
buildInputEdgeKinds := []string{buildedges.BuildTriggerImageEdgeKind}
if includeInputImages {
buildInputEdgeKinds = append(buildInputEdgeKinds, buildedges.BuildInputImageEdgeKind)
}
// Partition down to the subgraph containing the imagestreamtag of interest
var partitioned osgraph.Graph
if reverse {
partitioned = partitionReverse(g, istNode, buildInputEdgeKinds)
} else {
partitioned = partition(g, istNode, buildInputEdgeKinds)
}
switch strings.ToLower(d.outputFormat) {
case "dot":
data, err := dot.Marshal(partitioned, dotutil.Quote(ist.Name), "", " ", false)
if err != nil {
return "", err
}
return string(data), nil
case "":
return d.humanReadableOutput(partitioned, d.namer, istNode, reverse), nil
}
return "", fmt.Errorf("unknown specified format %q", d.outputFormat)
}
// partition the graph down to a subgraph starting from the given root
func partition(g osgraph.Graph, root graph.Node, buildInputEdgeKinds []string) osgraph.Graph {
// Filter out all but BuildConfig and ImageStreamTag nodes
nodeFn := osgraph.NodesOfKind(buildgraph.BuildConfigNodeKind, imagegraph.ImageStreamTagNodeKind)
// Filter out all but BuildInputImage and BuildOutput edges
edgeKinds := []string{}
edgeKinds = append(edgeKinds, buildInputEdgeKinds...)
edgeKinds = append(edgeKinds, buildedges.BuildOutputEdgeKind)
edgeFn := osgraph.EdgesOfKind(edgeKinds...)
sub := g.Subgraph(nodeFn, edgeFn)
// Filter out inbound edges to the IST of interest
edgeFn = osgraph.RemoveInboundEdges([]graph.Node{root})
sub = sub.Subgraph(nodeFn, edgeFn)
// Check all paths leading from the root node, collect any
// node found in them, and create the desired subgraph
desired := []graph.Node{root}
paths := path.DijkstraAllPaths(sub)
for _, node := range sub.Nodes() {
if node == root {
continue
}
path, _, _ := paths.Between(root, node)
if len(path) != 0 {
desired = append(desired, node)
}
}
return sub.SubgraphWithNodes(desired, osgraph.ExistingDirectEdge)
}
// partitionReverse the graph down to a subgraph starting from the given root
func partitionReverse(g osgraph.Graph, root graph.Node, buildInputEdgeKinds []string) osgraph.Graph {
// Filter out all but BuildConfig and ImageStreamTag nodes
nodeFn := osgraph.NodesOfKind(buildgraph.BuildConfigNodeKind, imagegraph.ImageStreamTagNodeKind)
// Filter out all but BuildInputImage and BuildOutput edges
edgeKinds := []string{}
edgeKinds = append(edgeKinds, buildInputEdgeKinds...)
edgeKinds = append(edgeKinds, buildedges.BuildOutputEdgeKind)
edgeFn := osgraph.EdgesOfKind(edgeKinds...)
sub := g.Subgraph(nodeFn, edgeFn)
// Filter out inbound edges to the IST of interest
edgeFn = osgraph.RemoveOutboundEdges([]graph.Node{root})
sub = sub.Subgraph(nodeFn, edgeFn)
// Check all paths leading from the root node, collect any
// node found in them, and create the desired subgraph
desired := []graph.Node{root}
paths := path.DijkstraAllPaths(sub)
for _, node := range sub.Nodes() {
if node == root {
continue
}
path, _, _ := paths.Between(node, root)
if len(path) != 0 {
desired = append(desired, node)
}
}
return sub.SubgraphWithNodes(desired, osgraph.ExistingDirectEdge)
}
// humanReadableOutput traverses the provided graph using DFS and outputs it
// in a human-readable format. It starts from the provided root, assuming it
// is an imageStreamTag node and continues to the rest of the graph handling
// only imageStreamTag and buildConfig nodes.
func (d *ChainDescriber) humanReadableOutput(g osgraph.Graph, f osgraph.Namer, root graph.Node, reverse bool) string {
if reverse {
g = g.EdgeSubgraph(osgraph.ReverseExistingDirectEdge)
}
var singleNamespace bool
if len(d.namespaces) == 1 && !d.namespaces.Has(kapi.NamespaceAll) {
singleNamespace = true
}
depth := map[graph.Node]int{
root: 0,
}
out := ""
dfs := &DepthFirst{
Visit: func(u, v graph.Node) {
depth[v] = depth[u] + 1
},
}
until := func(node graph.Node) bool {
var info string
switch t := node.(type) {
case *imagegraph.ImageStreamTagNode:
info = outputHelper(f.ResourceName(t), t.Namespace, singleNamespace)
case *buildgraph.BuildConfigNode:
info = outputHelper(f.ResourceName(t), t.BuildConfig.Namespace, singleNamespace)
default:
panic("this graph contains node kinds other than imageStreamTags and buildConfigs")
}
if depth[node] != 0 {
out += "\n"
}
out += fmt.Sprintf("%s", strings.Repeat("\t", depth[node]))
out += fmt.Sprintf("%s", info)
return false
}
dfs.Walk(g, root, until)
return out
}
// outputHelper returns resource/name in a single namespace, <namespace resource/name>
// in multiple namespaces
func outputHelper(info, namespace string, singleNamespace bool) string {
if singleNamespace {
return info
}
return fmt.Sprintf("<%s %s>", namespace, info)
}
// DepthFirst implements stateful depth-first graph traversal.
// Modifies behavior of visitor.DepthFirst to allow nodes to be visited multiple
// times as long as they're not in the current stack
type DepthFirst struct {
EdgeFilter func(graph.Edge) bool
Visit func(u, v graph.Node)
stack NodeStack
}
// Walk performs a depth-first traversal of the graph g starting from the given node
func (d *DepthFirst) Walk(g graph.Graph, from graph.Node, until func(graph.Node) bool) graph.Node {
return d.visit(g, from, until)
}
func (d *DepthFirst) visit(g graph.Graph, t graph.Node, until func(graph.Node) bool) graph.Node {
if until != nil && until(t) {
return t
}
d.stack.Push(t)
children := osgraph.ByID(g.From(t))
sort.Sort(children)
for _, n := range children {
if d.EdgeFilter != nil && !d.EdgeFilter(g.Edge(t, n)) {
continue
}
if d.visited(n.ID()) {
continue
}
if d.Visit != nil {
d.Visit(t, n)
}
result := d.visit(g, n, until)
if result != nil {
return result
}
}
d.stack.Pop()
return nil
}
func (d *DepthFirst) visited(id int) bool {
for _, n := range d.stack {
if n.ID() == id {
return true
}
}
return false
}
// NodeStack implements a LIFO stack of graph.Node.
// NodeStack is internal only in go 1.5.
type NodeStack []graph.Node
// Len returns the number of graph.Nodes on the stack.
func (s *NodeStack) Len() int { return len(*s) }
// Pop returns the last graph.Node on the stack and removes it
// from the stack.
func (s *NodeStack) Pop() graph.Node {
v := *s
v, n := v[:len(v)-1], v[len(v)-1]
*s = v
return n
}
// Push adds the node n to the stack at the last position.
func (s *NodeStack) Push(n graph.Node) { *s = append(*s, n) }
+417
View File
@@ -0,0 +1,417 @@
package describe
import (
"fmt"
"io"
"sort"
"strconv"
"strings"
"text/tabwriter"
kapi "k8s.io/kubernetes/pkg/api"
kerrors "k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/autoscaling"
kclient "k8s.io/kubernetes/pkg/client/unversioned"
rcutils "k8s.io/kubernetes/pkg/controller/replication"
kctl "k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/labels"
"github.com/openshift/origin/pkg/api/graph"
kubegraph "github.com/openshift/origin/pkg/api/kubegraph/nodes"
"github.com/openshift/origin/pkg/client"
deployapi "github.com/openshift/origin/pkg/deploy/api"
deployedges "github.com/openshift/origin/pkg/deploy/graph"
deploygraph "github.com/openshift/origin/pkg/deploy/graph/nodes"
deployutil "github.com/openshift/origin/pkg/deploy/util"
imageapi "github.com/openshift/origin/pkg/image/api"
)
const (
// maxDisplayDeployments is the number of deployments to show when describing
// deployment configuration.
maxDisplayDeployments = 3
// maxDisplayDeploymentsEvents is the number of events to display when
// describing the deployment configuration.
// TODO: Make the estimation of this number more sophisticated and make this
// number configurable via DescriberSettings
maxDisplayDeploymentsEvents = 8
)
// DeploymentConfigDescriber generates information about a DeploymentConfig
type DeploymentConfigDescriber struct {
osClient client.Interface
kubeClient kclient.Interface
config *deployapi.DeploymentConfig
}
// NewDeploymentConfigDescriber returns a new DeploymentConfigDescriber
func NewDeploymentConfigDescriber(client client.Interface, kclient kclient.Interface, config *deployapi.DeploymentConfig) *DeploymentConfigDescriber {
return &DeploymentConfigDescriber{
osClient: client,
kubeClient: kclient,
config: config,
}
}
// Describe returns the description of a DeploymentConfig
func (d *DeploymentConfigDescriber) Describe(namespace, name string, settings kctl.DescriberSettings) (string, error) {
var deploymentConfig *deployapi.DeploymentConfig
if d.config != nil {
// If a deployment config is already provided use that.
// This is used by `oc rollback --dry-run`.
deploymentConfig = d.config
} else {
var err error
deploymentConfig, err = d.osClient.DeploymentConfigs(namespace).Get(name)
if err != nil {
return "", err
}
}
return tabbedString(func(out *tabwriter.Writer) error {
formatMeta(out, deploymentConfig.ObjectMeta)
var (
deploymentsHistory []kapi.ReplicationController
activeDeploymentName string
)
if d.config == nil {
if rcs, err := d.kubeClient.ReplicationControllers(namespace).List(kapi.ListOptions{LabelSelector: deployutil.ConfigSelector(deploymentConfig.Name)}); err == nil {
deploymentsHistory = rcs.Items
}
}
if deploymentConfig.Status.LatestVersion == 0 {
formatString(out, "Latest Version", "Not deployed")
} else {
formatString(out, "Latest Version", strconv.FormatInt(deploymentConfig.Status.LatestVersion, 10))
}
printDeploymentConfigSpec(d.kubeClient, *deploymentConfig, out)
fmt.Fprintln(out)
latestDeploymentName := deployutil.LatestDeploymentNameForConfig(deploymentConfig)
if activeDeployment := deployutil.ActiveDeployment(deploymentConfig, deploymentsHistory); activeDeployment != nil {
activeDeploymentName = activeDeployment.Name
}
var deployment *kapi.ReplicationController
isNotDeployed := len(deploymentsHistory) == 0
for _, item := range deploymentsHistory {
if item.Name == latestDeploymentName {
deployment = &item
}
}
if isNotDeployed {
formatString(out, "Latest Deployment", "<none>")
} else {
header := fmt.Sprintf("Deployment #%d (latest)", deployutil.DeploymentVersionFor(deployment))
// Show details if the current deployment is the active one or it is the
// initial deployment.
printDeploymentRc(deployment, d.kubeClient, out, header, (deployment.Name == activeDeploymentName) || len(deploymentsHistory) == 1)
}
// We don't show the deployment history when running `oc rollback --dry-run`.
if d.config == nil && !isNotDeployed {
sorted := deploymentsHistory
sort.Sort(sort.Reverse(rcutils.OverlappingControllers(sorted)))
counter := 1
for _, item := range sorted {
if item.Name != latestDeploymentName && deploymentConfig.Name == deployutil.DeploymentConfigNameFor(&item) {
header := fmt.Sprintf("Deployment #%d", deployutil.DeploymentVersionFor(&item))
printDeploymentRc(&item, d.kubeClient, out, header, item.Name == activeDeploymentName)
counter++
}
if counter == maxDisplayDeployments {
break
}
}
}
if settings.ShowEvents {
// Events
if events, err := d.kubeClient.Events(deploymentConfig.Namespace).Search(deploymentConfig); err == nil && events != nil {
latestDeploymentEvents := &kapi.EventList{Items: []kapi.Event{}}
for i := len(events.Items); i != 0 && i > len(events.Items)-maxDisplayDeploymentsEvents; i-- {
latestDeploymentEvents.Items = append(latestDeploymentEvents.Items, events.Items[i-1])
}
fmt.Fprintln(out)
kctl.DescribeEvents(latestDeploymentEvents, out)
}
}
return nil
})
}
func multilineStringArray(sep, indent string, args ...string) string {
for i, s := range args {
if strings.HasSuffix(s, "\n") {
s = strings.TrimSuffix(s, "\n")
}
if strings.Contains(s, "\n") {
s = "\n" + indent + strings.Join(strings.Split(s, "\n"), "\n"+indent)
}
args[i] = s
}
strings.TrimRight(args[len(args)-1], "\n ")
return strings.Join(args, " ")
}
func printStrategy(strategy deployapi.DeploymentStrategy, indent string, w *tabwriter.Writer) {
if strategy.CustomParams != nil {
if len(strategy.CustomParams.Image) == 0 {
fmt.Fprintf(w, "%sImage:\t%s\n", indent, "<default>")
} else {
fmt.Fprintf(w, "%sImage:\t%s\n", indent, strategy.CustomParams.Image)
}
if len(strategy.CustomParams.Environment) > 0 {
fmt.Fprintf(w, "%sEnvironment:\t%s\n", indent, formatLabels(convertEnv(strategy.CustomParams.Environment)))
}
if len(strategy.CustomParams.Command) > 0 {
fmt.Fprintf(w, "%sCommand:\t%v\n", indent, multilineStringArray(" ", "\t ", strategy.CustomParams.Command...))
}
}
if strategy.RecreateParams != nil {
pre := strategy.RecreateParams.Pre
mid := strategy.RecreateParams.Mid
post := strategy.RecreateParams.Post
if pre != nil {
printHook("Pre-deployment", pre, indent, w)
}
if mid != nil {
printHook("Mid-deployment", mid, indent, w)
}
if post != nil {
printHook("Post-deployment", post, indent, w)
}
}
if strategy.RollingParams != nil {
pre := strategy.RollingParams.Pre
post := strategy.RollingParams.Post
if pre != nil {
printHook("Pre-deployment", pre, indent, w)
}
if post != nil {
printHook("Post-deployment", post, indent, w)
}
}
}
func printHook(prefix string, hook *deployapi.LifecycleHook, indent string, w io.Writer) {
if hook.ExecNewPod != nil {
fmt.Fprintf(w, "%s%s hook (pod type, failure policy: %s):\n", indent, prefix, hook.FailurePolicy)
fmt.Fprintf(w, "%s Container:\t%s\n", indent, hook.ExecNewPod.ContainerName)
fmt.Fprintf(w, "%s Command:\t%v\n", indent, multilineStringArray(" ", "\t ", hook.ExecNewPod.Command...))
if len(hook.ExecNewPod.Env) > 0 {
fmt.Fprintf(w, "%s Env:\t%s\n", indent, formatLabels(convertEnv(hook.ExecNewPod.Env)))
}
}
if len(hook.TagImages) > 0 {
fmt.Fprintf(w, "%s%s hook (tag images, failure policy: %s):\n", indent, prefix, hook.FailurePolicy)
for _, image := range hook.TagImages {
fmt.Fprintf(w, "%s Tag:\tcontainer %s to %s %s %s\n", indent, image.ContainerName, image.To.Kind, image.To.Name, image.To.Namespace)
}
}
}
func printTriggers(triggers []deployapi.DeploymentTriggerPolicy, w *tabwriter.Writer) {
if len(triggers) == 0 {
formatString(w, "Triggers", "<none>")
return
}
labels := []string{}
for _, t := range triggers {
switch t.Type {
case deployapi.DeploymentTriggerOnConfigChange:
labels = append(labels, "Config")
case deployapi.DeploymentTriggerOnImageChange:
if len(t.ImageChangeParams.From.Name) > 0 {
name, tag, _ := imageapi.SplitImageStreamTag(t.ImageChangeParams.From.Name)
labels = append(labels, fmt.Sprintf("Image(%s@%s, auto=%v)", name, tag, t.ImageChangeParams.Automatic))
}
}
}
desc := strings.Join(labels, ", ")
formatString(w, "Triggers", desc)
}
func printDeploymentConfigSpec(kc kclient.Interface, dc deployapi.DeploymentConfig, w *tabwriter.Writer) error {
spec := dc.Spec
// Selector
formatString(w, "Selector", formatLabels(spec.Selector))
// Replicas
test := ""
if spec.Test {
test = " (test, will be scaled down between deployments)"
}
formatString(w, "Replicas", fmt.Sprintf("%d%s", spec.Replicas, test))
if spec.Paused {
formatString(w, "Paused", "yes")
}
// Autoscaling info
printAutoscalingInfo(deployapi.Resource("DeploymentConfig"), dc.Namespace, dc.Name, kc, w)
// Triggers
printTriggers(spec.Triggers, w)
// Strategy
formatString(w, "Strategy", spec.Strategy.Type)
printStrategy(spec.Strategy, " ", w)
if dc.Spec.MinReadySeconds > 0 {
formatString(w, "MinReadySeconds", fmt.Sprintf("%d", spec.MinReadySeconds))
}
// Pod template
fmt.Fprintf(w, "Template:\n")
kctl.DescribePodTemplate(spec.Template, w)
return nil
}
// TODO: Move this upstream
func printAutoscalingInfo(res unversioned.GroupResource, namespace, name string, kclient kclient.Interface, w *tabwriter.Writer) {
hpaList, err := kclient.Autoscaling().HorizontalPodAutoscalers(namespace).List(kapi.ListOptions{LabelSelector: labels.Everything()})
if err != nil {
return
}
scaledBy := []autoscaling.HorizontalPodAutoscaler{}
for _, hpa := range hpaList.Items {
if hpa.Spec.ScaleTargetRef.Name == name && hpa.Spec.ScaleTargetRef.Kind == res.String() {
scaledBy = append(scaledBy, hpa)
}
}
for _, hpa := range scaledBy {
cpuUtil := ""
if hpa.Spec.TargetCPUUtilizationPercentage != nil {
cpuUtil = fmt.Sprintf(", triggered at %d%% CPU usage", *hpa.Spec.TargetCPUUtilizationPercentage)
}
fmt.Fprintf(w, "Autoscaling:\tbetween %d and %d replicas%s\n", *hpa.Spec.MinReplicas, hpa.Spec.MaxReplicas, cpuUtil)
// TODO: Print a warning in case of multiple hpas.
// Related oc status PR: https://github.com/openshift/origin/pull/7799
break
}
}
func printDeploymentRc(deployment *kapi.ReplicationController, kubeClient kclient.Interface, w io.Writer, header string, verbose bool) error {
if len(header) > 0 {
fmt.Fprintf(w, "%v:\n", header)
}
if verbose {
fmt.Fprintf(w, "\tName:\t%s\n", deployment.Name)
}
timeAt := strings.ToLower(formatRelativeTime(deployment.CreationTimestamp.Time))
fmt.Fprintf(w, "\tCreated:\t%s ago\n", timeAt)
fmt.Fprintf(w, "\tStatus:\t%s\n", deployutil.DeploymentStatusFor(deployment))
fmt.Fprintf(w, "\tReplicas:\t%d current / %d desired\n", deployment.Status.Replicas, deployment.Spec.Replicas)
if verbose {
fmt.Fprintf(w, "\tSelector:\t%s\n", formatLabels(deployment.Spec.Selector))
fmt.Fprintf(w, "\tLabels:\t%s\n", formatLabels(deployment.Labels))
running, waiting, succeeded, failed, err := getPodStatusForDeployment(deployment, kubeClient)
if err != nil {
return err
}
fmt.Fprintf(w, "\tPods Status:\t%d Running / %d Waiting / %d Succeeded / %d Failed\n", running, waiting, succeeded, failed)
}
return nil
}
func getPodStatusForDeployment(deployment *kapi.ReplicationController, kubeClient kclient.Interface) (running, waiting, succeeded, failed int, err error) {
rcPods, err := kubeClient.Pods(deployment.Namespace).List(kapi.ListOptions{LabelSelector: labels.Set(deployment.Spec.Selector).AsSelector()})
if err != nil {
return
}
for _, pod := range rcPods.Items {
switch pod.Status.Phase {
case kapi.PodRunning:
running++
case kapi.PodPending:
waiting++
case kapi.PodSucceeded:
succeeded++
case kapi.PodFailed:
failed++
}
}
return
}
type LatestDeploymentsDescriber struct {
count int
osClient client.Interface
kubeClient kclient.Interface
}
// NewLatestDeploymentsDescriber lists the latest deployments limited to "count". In case count == -1, list back to the last successful.
func NewLatestDeploymentsDescriber(client client.Interface, kclient kclient.Interface, count int) *LatestDeploymentsDescriber {
return &LatestDeploymentsDescriber{
count: count,
osClient: client,
kubeClient: kclient,
}
}
// Describe returns the description of the latest deployments for a config
func (d *LatestDeploymentsDescriber) Describe(namespace, name string) (string, error) {
var f formatter
config, err := d.osClient.DeploymentConfigs(namespace).Get(name)
if err != nil {
return "", err
}
var deployments []kapi.ReplicationController
if d.count == -1 || d.count > 1 {
list, err := d.kubeClient.ReplicationControllers(namespace).List(kapi.ListOptions{LabelSelector: deployutil.ConfigSelector(name)})
if err != nil && !kerrors.IsNotFound(err) {
return "", err
}
deployments = list.Items
} else {
deploymentName := deployutil.LatestDeploymentNameForConfig(config)
deployment, err := d.kubeClient.ReplicationControllers(config.Namespace).Get(deploymentName)
if err != nil && !kerrors.IsNotFound(err) {
return "", err
}
if deployment != nil {
deployments = []kapi.ReplicationController{*deployment}
}
}
g := graph.New()
dcNode := deploygraph.EnsureDeploymentConfigNode(g, config)
for i := range deployments {
kubegraph.EnsureReplicationControllerNode(g, &deployments[i])
}
deployedges.AddTriggerEdges(g, dcNode)
deployedges.AddDeploymentEdges(g, dcNode)
activeDeployment, inactiveDeployments := deployedges.RelevantDeployments(g, dcNode)
return tabbedString(func(out *tabwriter.Writer) error {
descriptions := describeDeployments(f, dcNode, activeDeployment, inactiveDeployments, nil, d.count)
for i, description := range descriptions {
descriptions[i] = fmt.Sprintf("%v %v", name, description)
}
printLines(out, "", 0, descriptions...)
return nil
})
}

Some files were not shown because too many files have changed in this diff Show More