forked from LaconicNetwork/kompose
switch from godep to glide
This commit is contained in:
-693
@@ -1,693 +0,0 @@
|
||||
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
|
||||
}
|
||||
-84
@@ -1,84 +0,0 @@
|
||||
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)
|
||||
}
|
||||
-240
@@ -1,240 +0,0 @@
|
||||
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
@@ -1,46 +0,0 @@
|
||||
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
@@ -1,51 +0,0 @@
|
||||
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
@@ -1,39 +0,0 @@
|
||||
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
@@ -1,100 +0,0 @@
|
||||
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()
|
||||
}
|
||||
-134
@@ -1,134 +0,0 @@
|
||||
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
@@ -1,134 +0,0 @@
|
||||
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
@@ -1,69 +0,0 @@
|
||||
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
@@ -1,180 +0,0 @@
|
||||
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
@@ -1,135 +0,0 @@
|
||||
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
|
||||
}
|
||||
-124
@@ -1,124 +0,0 @@
|
||||
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
@@ -1,56 +0,0 @@
|
||||
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
@@ -1,248 +0,0 @@
|
||||
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
@@ -1,187 +0,0 @@
|
||||
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
@@ -1,325 +0,0 @@
|
||||
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
|
||||
}
|
||||
-176
@@ -1,176 +0,0 @@
|
||||
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)
|
||||
}
|
||||
+1
-1
@@ -41,7 +41,7 @@ type UserIdentityMapper interface {
|
||||
|
||||
type Client interface {
|
||||
GetId() string
|
||||
ValidateSecret(secret string) bool
|
||||
GetSecret() string
|
||||
GetRedirectUri() string
|
||||
GetUserData() interface{}
|
||||
}
|
||||
|
||||
Generated
Vendored
+35
-26
@@ -40,28 +40,34 @@ func New(opts x509.VerifyOptions, user UserConversion) *Authenticator {
|
||||
|
||||
// AuthenticateRequest authenticates the request using presented client certificates
|
||||
func (a *Authenticator) AuthenticateRequest(req *http.Request) (user.Info, bool, error) {
|
||||
if req.TLS == nil {
|
||||
if req.TLS == nil || len(req.TLS.PeerCertificates) == 0 {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
// Use intermediates, if provided
|
||||
optsCopy := a.opts
|
||||
if optsCopy.Intermediates == nil && len(req.TLS.PeerCertificates) > 1 {
|
||||
optsCopy.Intermediates = x509.NewCertPool()
|
||||
for _, intermediate := range req.TLS.PeerCertificates[1:] {
|
||||
optsCopy.Intermediates.AddCert(intermediate)
|
||||
}
|
||||
}
|
||||
|
||||
chains, err := req.TLS.PeerCertificates[0].Verify(optsCopy)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
var errlist []error
|
||||
for _, cert := range req.TLS.PeerCertificates {
|
||||
chains, err := cert.Verify(a.opts)
|
||||
for _, chain := range chains {
|
||||
user, ok, err := a.user.User(chain)
|
||||
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
|
||||
}
|
||||
if ok {
|
||||
return user, ok, err
|
||||
}
|
||||
}
|
||||
return nil, false, kerrors.NewAggregate(errlist)
|
||||
@@ -81,25 +87,28 @@ func NewVerifier(opts x509.VerifyOptions, auth authenticator.Request, allowedCom
|
||||
return &Verifier{opts, auth, allowedCommonNames}
|
||||
}
|
||||
|
||||
// AuthenticateRequest verifies the presented client certificates, then delegates to the wrapped auth
|
||||
// AuthenticateRequest verifies the presented client certificate, then delegates to the wrapped auth
|
||||
func (a *Verifier) AuthenticateRequest(req *http.Request) (user.Info, bool, error) {
|
||||
if req.TLS == nil {
|
||||
if req.TLS == nil || len(req.TLS.PeerCertificates) == 0 {
|
||||
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
|
||||
// Use intermediates, if provided
|
||||
optsCopy := a.opts
|
||||
if optsCopy.Intermediates == nil && len(req.TLS.PeerCertificates) > 1 {
|
||||
optsCopy.Intermediates = x509.NewCertPool()
|
||||
for _, intermediate := range req.TLS.PeerCertificates[1:] {
|
||||
optsCopy.Intermediates.AddCert(intermediate)
|
||||
}
|
||||
if err := a.verifySubject(cert.Subject); err != nil {
|
||||
errlist = append(errlist, err)
|
||||
continue
|
||||
}
|
||||
return a.auth.AuthenticateRequest(req)
|
||||
}
|
||||
return nil, false, kerrors.NewAggregate(errlist)
|
||||
|
||||
if _, err := req.TLS.PeerCertificates[0].Verify(optsCopy); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
if err := a.verifySubject(req.TLS.PeerCertificates[0].Subject); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return a.auth.AuthenticateRequest(req)
|
||||
}
|
||||
|
||||
func (a *Verifier) verifySubject(subject pkix.Name) error {
|
||||
|
||||
+1
@@ -39,6 +39,7 @@ func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
&RoleList{},
|
||||
|
||||
&SelfSubjectRulesReview{},
|
||||
&SubjectRulesReview{},
|
||||
&ResourceAccessReview{},
|
||||
&SubjectAccessReview{},
|
||||
&LocalResourceAccessReview{},
|
||||
|
||||
+1
@@ -9,6 +9,7 @@ const (
|
||||
|
||||
NodeMetricsResource = "nodes/metrics"
|
||||
NodeStatsResource = "nodes/stats"
|
||||
NodeSpecResource = "nodes/spec"
|
||||
NodeLogResource = "nodes/log"
|
||||
|
||||
RestrictedEndpointsResource = "endpoints/restricted"
|
||||
|
||||
+22
@@ -50,6 +50,7 @@ var DiscoveryRule = PolicyRule{
|
||||
"/apis", "/apis/*",
|
||||
"/oapi", "/oapi/*",
|
||||
"/osapi", "/osapi/", // these cannot be removed until we can drop support for pre 3.1 clients
|
||||
"/.well-known", "/.well-known/*",
|
||||
),
|
||||
}
|
||||
|
||||
@@ -159,6 +160,27 @@ type SelfSubjectRulesReviewSpec struct {
|
||||
Scopes []string
|
||||
}
|
||||
|
||||
// SubjectRulesReview is a resource you can create to determine which actions another user can perform in a namespace
|
||||
type SubjectRulesReview struct {
|
||||
unversioned.TypeMeta
|
||||
|
||||
// Spec adds information about how to conduct the check
|
||||
Spec SubjectRulesReviewSpec
|
||||
|
||||
// Status is completed by the server to tell which permissions you have
|
||||
Status SubjectRulesReviewStatus
|
||||
}
|
||||
|
||||
// SubjectRulesReviewSpec adds information about how to conduct the check
|
||||
type SubjectRulesReviewSpec struct {
|
||||
// User is optional. At least one of User and Groups must be specified.
|
||||
User string
|
||||
// Groups is optional. Groups is the list of groups to which the User belongs. At least one of User and Groups must be specified.
|
||||
Groups []string
|
||||
// Scopes to use for the evaluation. Empty means "use the unscoped (full) permissions of the user/groups".
|
||||
Scopes []string
|
||||
}
|
||||
|
||||
// SubjectRulesReviewStatus is contains the result of a rules check
|
||||
type SubjectRulesReviewStatus struct {
|
||||
// Rules is the list of rules (no particular sort) that are allowed for the subject
|
||||
|
||||
Generated
Vendored
+40
@@ -48,6 +48,8 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error {
|
||||
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_SubjectRulesReview, InType: reflect.TypeOf(&SubjectRulesReview{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_SubjectRulesReviewSpec, InType: reflect.TypeOf(&SubjectRulesReviewSpec{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_SubjectRulesReviewStatus, InType: reflect.TypeOf(&SubjectRulesReviewStatus{})},
|
||||
)
|
||||
}
|
||||
@@ -669,6 +671,44 @@ func DeepCopy_api_SubjectAccessReviewResponse(in interface{}, out interface{}, c
|
||||
}
|
||||
}
|
||||
|
||||
func DeepCopy_api_SubjectRulesReview(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*SubjectRulesReview)
|
||||
out := out.(*SubjectRulesReview)
|
||||
out.TypeMeta = in.TypeMeta
|
||||
if err := DeepCopy_api_SubjectRulesReviewSpec(&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_SubjectRulesReviewSpec(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*SubjectRulesReviewSpec)
|
||||
out := out.(*SubjectRulesReviewSpec)
|
||||
out.User = in.User
|
||||
if in.Groups != nil {
|
||||
in, out := &in.Groups, &out.Groups
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
} 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_SubjectRulesReviewStatus(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*SubjectRulesReviewStatus)
|
||||
|
||||
-60
@@ -1,60 +0,0 @@
|
||||
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
@@ -1,47 +0,0 @@
|
||||
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
|
||||
}
|
||||
+54
-12
@@ -19,6 +19,12 @@ const (
|
||||
BuildCloneAnnotation = "openshift.io/build.clone-of"
|
||||
// BuildPodNameAnnotation is an annotation whose value is the name of the pod running this build
|
||||
BuildPodNameAnnotation = "openshift.io/build.pod-name"
|
||||
// BuildJenkinsStatusJSONAnnotation is an annotation holding the Jenkins status information
|
||||
BuildJenkinsStatusJSONAnnotation = "openshift.io/jenkins-status-json"
|
||||
// BuildJenkinsLogURLAnnotation is an annotation holding a link to the Jenkins build console log
|
||||
BuildJenkinsLogURLAnnotation = "openshift.io/jenkins-log-url"
|
||||
// BuildJenkinsBuildURIAnnotation is an annotation holding a link to the Jenkins build
|
||||
BuildJenkinsBuildURIAnnotation = "openshift.io/jenkins-build-uri"
|
||||
// BuildLabel is the key of a Pod label whose value is the Name of a Build which is run.
|
||||
// NOTE: The value for this label may not contain the entire Build name because it will be
|
||||
// truncated to maximum label length.
|
||||
@@ -106,8 +112,22 @@ type CommonSpec struct {
|
||||
// be active on a node before the system actively tries to terminate the
|
||||
// build; value must be positive integer.
|
||||
CompletionDeadlineSeconds *int64
|
||||
|
||||
// NodeSelector is a selector which must be true for the build pod to fit on a node
|
||||
// If nil, it can be overridden by default build nodeselector values for the cluster.
|
||||
// If set to an empty map or a map with any values, default build nodeselector values
|
||||
// are ignored.
|
||||
NodeSelector map[string]string
|
||||
}
|
||||
|
||||
const (
|
||||
BuildTriggerCauseManualMsg = "Manually triggered"
|
||||
BuildTriggerCauseConfigMsg = "Build configuration change"
|
||||
BuildTriggerCauseImageMsg = "Image change"
|
||||
BuildTriggerCauseGithubMsg = "GitHub WebHook"
|
||||
BuildTriggerCauseGenericMsg = "Generic WebHook"
|
||||
)
|
||||
|
||||
// BuildTriggerCause holds information about a triggered build. It is used for
|
||||
// displaying build trigger data for each build and build configuration in oc
|
||||
// describe. It is also used to describe which triggers led to the most recent
|
||||
@@ -240,32 +260,32 @@ const (
|
||||
|
||||
// StatusReasonCannotCreateBuildPodSpec is an error condition when the build
|
||||
// strategy cannot create a build pod spec.
|
||||
StatusReasonCannotCreateBuildPodSpec = "CannotCreateBuildPodSpec"
|
||||
StatusReasonCannotCreateBuildPodSpec StatusReason = "CannotCreateBuildPodSpec"
|
||||
|
||||
// StatusReasonCannotCreateBuildPod is an error condition when a build pod
|
||||
// cannot be created.
|
||||
StatusReasonCannotCreateBuildPod = "CannotCreateBuildPod"
|
||||
StatusReasonCannotCreateBuildPod StatusReason = "CannotCreateBuildPod"
|
||||
|
||||
// StatusReasonInvalidOutputReference is an error condition when the build
|
||||
// output is an invalid reference.
|
||||
StatusReasonInvalidOutputReference = "InvalidOutputReference"
|
||||
StatusReasonInvalidOutputReference StatusReason = "InvalidOutputReference"
|
||||
|
||||
// StatusReasonCancelBuildFailed is an error condition when cancelling a build
|
||||
// fails.
|
||||
StatusReasonCancelBuildFailed = "CancelBuildFailed"
|
||||
StatusReasonCancelBuildFailed StatusReason = "CancelBuildFailed"
|
||||
|
||||
// StatusReasonBuildPodDeleted is an error condition when the build pod is
|
||||
// deleted before build completion.
|
||||
StatusReasonBuildPodDeleted = "BuildPodDeleted"
|
||||
StatusReasonBuildPodDeleted StatusReason = "BuildPodDeleted"
|
||||
|
||||
// StatusReasonExceededRetryTimeout is an error condition when the build has
|
||||
// not completed and retrying the build times out.
|
||||
StatusReasonExceededRetryTimeout = "ExceededRetryTimeout"
|
||||
StatusReasonExceededRetryTimeout StatusReason = "ExceededRetryTimeout"
|
||||
|
||||
// StatusReasonMissingPushSecret indicates that the build is missing required
|
||||
// secret for pushing the output image.
|
||||
// The build will stay in the pending state until the secret is created, or the build times out.
|
||||
StatusReasonMissingPushSecret = "MissingPushSecret"
|
||||
StatusReasonMissingPushSecret StatusReason = "MissingPushSecret"
|
||||
)
|
||||
|
||||
// BuildSource is the input used for the build.
|
||||
@@ -387,6 +407,18 @@ type GitSourceRevision struct {
|
||||
Message string
|
||||
}
|
||||
|
||||
// ProxyConfig defines what proxies to use for an operation
|
||||
type ProxyConfig struct {
|
||||
// HTTPProxy is a proxy used to reach the git repository over http
|
||||
HTTPProxy *string
|
||||
|
||||
// HTTPSProxy is a proxy used to reach the git repository over https
|
||||
HTTPSProxy *string
|
||||
|
||||
// NoProxy is the list of domains for which the proxy should not be used
|
||||
NoProxy *string
|
||||
}
|
||||
|
||||
// GitBuildSource defines the parameters of a Git SCM
|
||||
type GitBuildSource struct {
|
||||
// URI points to the source that will be built. The structure of the source
|
||||
@@ -396,11 +428,8 @@ type GitBuildSource struct {
|
||||
// Ref is the branch/tag/ref to build.
|
||||
Ref string
|
||||
|
||||
// HTTPProxy is a proxy used to reach the git repository over http
|
||||
HTTPProxy *string
|
||||
|
||||
// HTTPSProxy is a proxy used to reach the git repository over https
|
||||
HTTPSProxy *string
|
||||
// ProxyConfig defines the proxies to use for the git clone operation
|
||||
ProxyConfig
|
||||
}
|
||||
|
||||
// SourceControlUser defines the identity of a user of source control
|
||||
@@ -646,6 +675,19 @@ type BuildOutput struct {
|
||||
// up the authentication for executing the Docker push to authentication
|
||||
// enabled Docker Registry (or Docker Hub).
|
||||
PushSecret *kapi.LocalObjectReference
|
||||
|
||||
// ImageLabels define a list of labels that are applied to the resulting image. If there
|
||||
// are multiple labels with the same name then the last one in the list is used.
|
||||
ImageLabels []ImageLabel
|
||||
}
|
||||
|
||||
// ImageLabel represents a label applied to the resulting image.
|
||||
type ImageLabel struct {
|
||||
// Name defines the name of the label. It must have non-zero length.
|
||||
Name string
|
||||
|
||||
// Value defines the literal value of the label.
|
||||
Value string
|
||||
}
|
||||
|
||||
// BuildConfig is a template which can be used to create new builds.
|
||||
|
||||
+61
-13
@@ -51,9 +51,11 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error {
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_GitSourceRevision, InType: reflect.TypeOf(&GitSourceRevision{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_ImageChangeCause, InType: reflect.TypeOf(&ImageChangeCause{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_ImageChangeTrigger, InType: reflect.TypeOf(&ImageChangeTrigger{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_ImageLabel, InType: reflect.TypeOf(&ImageLabel{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_ImageSource, InType: reflect.TypeOf(&ImageSource{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_ImageSourcePath, InType: reflect.TypeOf(&ImageSourcePath{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_JenkinsPipelineBuildStrategy, InType: reflect.TypeOf(&JenkinsPipelineBuildStrategy{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_ProxyConfig, InType: reflect.TypeOf(&ProxyConfig{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_SecretBuildSource, InType: reflect.TypeOf(&SecretBuildSource{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_SecretSpec, InType: reflect.TypeOf(&SecretSpec{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_SourceBuildStrategy, InType: reflect.TypeOf(&SourceBuildStrategy{})},
|
||||
@@ -275,6 +277,15 @@ func DeepCopy_api_BuildOutput(in interface{}, out interface{}, c *conversion.Clo
|
||||
} else {
|
||||
out.PushSecret = nil
|
||||
}
|
||||
if in.ImageLabels != nil {
|
||||
in, out := &in.ImageLabels, &out.ImageLabels
|
||||
*out = make([]ImageLabel, len(*in))
|
||||
for i := range *in {
|
||||
(*out)[i] = (*in)[i]
|
||||
}
|
||||
} else {
|
||||
out.ImageLabels = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -635,6 +646,15 @@ func DeepCopy_api_CommonSpec(in interface{}, out interface{}, c *conversion.Clon
|
||||
} else {
|
||||
out.CompletionDeadlineSeconds = nil
|
||||
}
|
||||
if in.NodeSelector != nil {
|
||||
in, out := &in.NodeSelector, &out.NodeSelector
|
||||
*out = make(map[string]string)
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
} else {
|
||||
out.NodeSelector = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -766,19 +786,8 @@ func DeepCopy_api_GitBuildSource(in interface{}, out interface{}, c *conversion.
|
||||
out := out.(*GitBuildSource)
|
||||
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
|
||||
if err := DeepCopy_api_ProxyConfig(&in.ProxyConfig, &out.ProxyConfig, c); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -881,6 +890,16 @@ func DeepCopy_api_ImageChangeTrigger(in interface{}, out interface{}, c *convers
|
||||
}
|
||||
}
|
||||
|
||||
func DeepCopy_api_ImageLabel(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*ImageLabel)
|
||||
out := out.(*ImageLabel)
|
||||
out.Name = in.Name
|
||||
out.Value = in.Value
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func DeepCopy_api_ImageSource(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*ImageSource)
|
||||
@@ -926,6 +945,35 @@ func DeepCopy_api_JenkinsPipelineBuildStrategy(in interface{}, out interface{},
|
||||
}
|
||||
}
|
||||
|
||||
func DeepCopy_api_ProxyConfig(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*ProxyConfig)
|
||||
out := out.(*ProxyConfig)
|
||||
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
|
||||
}
|
||||
if in.NoProxy != nil {
|
||||
in, out := &in.NoProxy, &out.NoProxy
|
||||
*out = new(string)
|
||||
**out = **in
|
||||
} else {
|
||||
out.NoProxy = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func DeepCopy_api_SecretBuildSource(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*SecretBuildSource)
|
||||
|
||||
-109
@@ -1,109 +0,0 @@
|
||||
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
@@ -1,2 +0,0 @@
|
||||
// Package cmd provides command helpers for builds
|
||||
package cmd
|
||||
-150
@@ -1,150 +0,0 @@
|
||||
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
@@ -1,351 +0,0 @@
|
||||
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
@@ -1,133 +0,0 @@
|
||||
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
@@ -1,111 +0,0 @@
|
||||
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
@@ -1,47 +0,0 @@
|
||||
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
@@ -1,90 +0,0 @@
|
||||
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
@@ -1,3 +0,0 @@
|
||||
// Package util contains common functions that are used
|
||||
// by the rest of the OpenShift build system.
|
||||
package util
|
||||
-169
@@ -1,169 +0,0 @@
|
||||
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
|
||||
}
|
||||
+5
@@ -48,6 +48,7 @@ type Interface interface {
|
||||
SubjectAccessReviews
|
||||
LocalSubjectAccessReviewsNamespacer
|
||||
SelfSubjectRulesReviewsNamespacer
|
||||
SubjectRulesReviewsNamespacer
|
||||
TemplatesNamespacer
|
||||
TemplateConfigsNamespacer
|
||||
OAuthClientsInterface
|
||||
@@ -245,6 +246,10 @@ func (c *Client) SelfSubjectRulesReviews(namespace string) SelfSubjectRulesRevie
|
||||
return newSelfSubjectRulesReviews(c, namespace)
|
||||
}
|
||||
|
||||
func (c *Client) SubjectRulesReviews(namespace string) SubjectRulesReviewInterface {
|
||||
return newSubjectRulesReviews(c, namespace)
|
||||
}
|
||||
|
||||
func (c *Client) OAuthClients() OAuthClientInterface {
|
||||
return newOAuthClients(c)
|
||||
}
|
||||
|
||||
+13
@@ -30,6 +30,7 @@ type DeploymentConfigInterface interface {
|
||||
GetScale(name string) (*extensions.Scale, error)
|
||||
UpdateScale(scale *extensions.Scale) (*extensions.Scale, error)
|
||||
UpdateStatus(config *deployapi.DeploymentConfig) (*deployapi.DeploymentConfig, error)
|
||||
Instantiate(request *deployapi.DeploymentRequest) (*deployapi.DeploymentConfig, error)
|
||||
}
|
||||
|
||||
// deploymentConfigs implements DeploymentConfigsNamespacer interface
|
||||
@@ -155,6 +156,18 @@ func (c *deploymentConfigs) UpdateStatus(deploymentConfig *deployapi.DeploymentC
|
||||
return
|
||||
}
|
||||
|
||||
// Instantiate instantiates a new build from build config returning new object or an error
|
||||
func (c *deploymentConfigs) Instantiate(request *deployapi.DeploymentRequest) (*deployapi.DeploymentConfig, error) {
|
||||
result := &deployapi.DeploymentConfig{}
|
||||
resp := c.r.Post().Namespace(c.ns).Resource("deploymentConfigs").Name(request.Name).SubResource("instantiate").Body(request).Do()
|
||||
var statusCode int
|
||||
if resp.StatusCode(&statusCode); statusCode == 204 {
|
||||
return nil, nil
|
||||
}
|
||||
err := resp.Into(result)
|
||||
return result, err
|
||||
}
|
||||
|
||||
type updateConfigFunc func(d *deployapi.DeploymentConfig)
|
||||
|
||||
// UpdateConfigWithRetries will try to update a deployment config and ignore any update conflicts.
|
||||
|
||||
-7
@@ -68,13 +68,6 @@ func (c *imageStreams) Get(name string) (result *imageapi.ImageStream, err error
|
||||
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{}
|
||||
|
||||
+7
@@ -17,6 +17,7 @@ type OAuthClientInterface interface {
|
||||
Get(name string) (*oauthapi.OAuthClient, error)
|
||||
Delete(name string) error
|
||||
Watch(opts kapi.ListOptions) (watch.Interface, error)
|
||||
Update(client *oauthapi.OAuthClient) (*oauthapi.OAuthClient, error)
|
||||
}
|
||||
|
||||
type oauthClients struct {
|
||||
@@ -55,3 +56,9 @@ func (c *oauthClients) Delete(name string) (err error) {
|
||||
func (c *oauthClients) Watch(opts kapi.ListOptions) (watch.Interface, error) {
|
||||
return c.r.Get().Prefix("watch").Resource("oAuthClients").VersionedParams(&opts, kapi.ParameterCodec).Watch()
|
||||
}
|
||||
|
||||
func (c *oauthClients) Update(client *oauthapi.OAuthClient) (result *oauthapi.OAuthClient, err error) {
|
||||
result = &oauthapi.OAuthClient{}
|
||||
err = c.r.Put().Resource("oAuthClients").Name(client.Name).Body(client).Do().Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
authorizationapi "github.com/openshift/origin/pkg/authorization/api"
|
||||
)
|
||||
|
||||
type SubjectRulesReviewsNamespacer interface {
|
||||
SubjectRulesReviews(namespace string) SubjectRulesReviewInterface
|
||||
}
|
||||
|
||||
type SubjectRulesReviewInterface interface {
|
||||
Create(*authorizationapi.SubjectRulesReview) (*authorizationapi.SubjectRulesReview, error)
|
||||
}
|
||||
|
||||
type subjectRulesReviews struct {
|
||||
r *Client
|
||||
ns string
|
||||
}
|
||||
|
||||
func newSubjectRulesReviews(c *Client, namespace string) *subjectRulesReviews {
|
||||
return &subjectRulesReviews{
|
||||
r: c,
|
||||
ns: namespace,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *subjectRulesReviews) Create(subjectRulesReview *authorizationapi.SubjectRulesReview) (result *authorizationapi.SubjectRulesReview, err error) {
|
||||
result = &authorizationapi.SubjectRulesReview{}
|
||||
err = c.r.Post().Namespace(c.ns).Resource("subjectRulesReviews").Body(subjectRulesReview).Do().Into(result)
|
||||
|
||||
return
|
||||
}
|
||||
+25
-11
@@ -6,6 +6,7 @@ import (
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
kerrors "k8s.io/kubernetes/pkg/api/errors"
|
||||
"k8s.io/kubernetes/pkg/client/restclient"
|
||||
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
|
||||
"k8s.io/kubernetes/third_party/forked/golang/netutil"
|
||||
@@ -34,11 +35,7 @@ func GetClusterNicknameFromURL(apiServerLocation string) (string, error) {
|
||||
// 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("~")
|
||||
userPartOfNick, err := getUserPartOfNickname(clientCfg)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -48,7 +45,7 @@ func GetUserNicknameFromConfig(clientCfg *restclient.Config) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return userInfo.Name + "/" + clusterNick, nil
|
||||
return userPartOfNick + "/" + clusterNick, nil
|
||||
}
|
||||
|
||||
func GetUserNicknameFromCert(clusterNick string, chain ...*x509.Certificate) (string, error) {
|
||||
@@ -60,15 +57,32 @@ func GetUserNicknameFromCert(clusterNick string, chain ...*x509.Certificate) (st
|
||||
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) {
|
||||
func getUserPartOfNickname(clientCfg *restclient.Config) (string, error) {
|
||||
client, err := osclient.New(clientCfg)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
userInfo, err := client.Users().Get("~")
|
||||
if kerrors.IsNotFound(err) {
|
||||
// if we're talking to kube (or likely talking to kube), take a best guess consistent with login
|
||||
switch {
|
||||
case len(clientCfg.BearerToken) > 0:
|
||||
userInfo.Name = clientCfg.BearerToken
|
||||
case len(clientCfg.Username) > 0:
|
||||
userInfo.Name = clientCfg.Username
|
||||
}
|
||||
} else if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return userInfo.Name, 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) {
|
||||
userPartOfNick, err := getUserPartOfNickname(clientCfg)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -78,7 +92,7 @@ func GetContextNicknameFromConfig(namespace string, clientCfg *restclient.Config
|
||||
return "", err
|
||||
}
|
||||
|
||||
return namespace + "/" + clusterNick + "/" + userInfo.Name, nil
|
||||
return namespace + "/" + clusterNick + "/" + userPartOfNick, nil
|
||||
}
|
||||
|
||||
func GetContextNickname(namespace, clusterNick, userNick string) string {
|
||||
|
||||
-319
@@ -1,319 +0,0 @@
|
||||
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
@@ -1,417 +0,0 @@
|
||||
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
|
||||
})
|
||||
}
|
||||
-1606
File diff suppressed because it is too large
Load Diff
-427
@@ -1,427 +0,0 @@
|
||||
package describe
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
units "github.com/docker/go-units"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/util/sets"
|
||||
|
||||
buildapi "github.com/openshift/origin/pkg/build/api"
|
||||
"github.com/openshift/origin/pkg/client"
|
||||
imageapi "github.com/openshift/origin/pkg/image/api"
|
||||
)
|
||||
|
||||
const emptyString = "<none>"
|
||||
|
||||
func tabbedString(f func(*tabwriter.Writer) error) (string, error) {
|
||||
out := new(tabwriter.Writer)
|
||||
buf := &bytes.Buffer{}
|
||||
out.Init(buf, 0, 8, 1, '\t', 0)
|
||||
|
||||
err := f(out)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
out.Flush()
|
||||
str := string(buf.String())
|
||||
return str, nil
|
||||
}
|
||||
|
||||
func toString(v interface{}) string {
|
||||
value := fmt.Sprintf("%v", v)
|
||||
if len(value) == 0 {
|
||||
value = emptyString
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func bold(v interface{}) string {
|
||||
return "\033[1m" + toString(v) + "\033[0m"
|
||||
}
|
||||
|
||||
func convertEnv(env []api.EnvVar) map[string]string {
|
||||
result := make(map[string]string, len(env))
|
||||
for _, e := range env {
|
||||
result[e.Name] = toString(e.Value)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func formatEnv(env api.EnvVar) string {
|
||||
if env.ValueFrom != nil && env.ValueFrom.FieldRef != nil {
|
||||
return fmt.Sprintf("%s=<%s>", env.Name, env.ValueFrom.FieldRef.FieldPath)
|
||||
}
|
||||
return fmt.Sprintf("%s=%s", env.Name, env.Value)
|
||||
}
|
||||
|
||||
func formatString(out *tabwriter.Writer, label string, v interface{}) {
|
||||
fmt.Fprintf(out, fmt.Sprintf("%s:\t%s\n", label, toString(v)))
|
||||
}
|
||||
|
||||
func formatTime(out *tabwriter.Writer, label string, t time.Time) {
|
||||
fmt.Fprintf(out, fmt.Sprintf("%s:\t%s ago\n", label, formatRelativeTime(t)))
|
||||
}
|
||||
|
||||
func formatLabels(labelMap map[string]string) string {
|
||||
return labels.Set(labelMap).String()
|
||||
}
|
||||
|
||||
func extractAnnotations(annotations map[string]string, keys ...string) ([]string, map[string]string) {
|
||||
extracted := make([]string, len(keys))
|
||||
remaining := make(map[string]string)
|
||||
for k, v := range annotations {
|
||||
remaining[k] = v
|
||||
}
|
||||
for i, key := range keys {
|
||||
extracted[i] = remaining[key]
|
||||
delete(remaining, key)
|
||||
}
|
||||
return extracted, remaining
|
||||
}
|
||||
|
||||
func formatMapStringString(out *tabwriter.Writer, label string, items map[string]string) {
|
||||
keys := sets.NewString()
|
||||
for k := range items {
|
||||
keys.Insert(k)
|
||||
}
|
||||
if keys.Len() == 0 {
|
||||
formatString(out, label, "")
|
||||
return
|
||||
}
|
||||
for i, key := range keys.List() {
|
||||
if i == 0 {
|
||||
formatString(out, label, fmt.Sprintf("%s=%s", key, items[key]))
|
||||
} else {
|
||||
fmt.Fprintf(out, "%s\t%s=%s\n", "", key, items[key])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func formatAnnotations(out *tabwriter.Writer, m api.ObjectMeta, prefix string) {
|
||||
values, annotations := extractAnnotations(m.Annotations, "description")
|
||||
if len(values[0]) > 0 {
|
||||
formatString(out, prefix+"Description", values[0])
|
||||
}
|
||||
formatMapStringString(out, prefix+"Annotations", annotations)
|
||||
}
|
||||
|
||||
var timeNowFn = func() time.Time {
|
||||
return time.Now()
|
||||
}
|
||||
|
||||
// Receives a time.Duration and returns Docker go-utils'
|
||||
// human-readable output
|
||||
func formatToHumanDuration(dur time.Duration) string {
|
||||
return units.HumanDuration(dur)
|
||||
}
|
||||
|
||||
func formatRelativeTime(t time.Time) string {
|
||||
return units.HumanDuration(timeNowFn().Sub(t))
|
||||
}
|
||||
|
||||
// FormatRelativeTime converts a time field into a human readable age string (hours, minutes, days).
|
||||
func FormatRelativeTime(t time.Time) string {
|
||||
return formatRelativeTime(t)
|
||||
}
|
||||
|
||||
func formatMeta(out *tabwriter.Writer, m api.ObjectMeta) {
|
||||
formatString(out, "Name", m.Name)
|
||||
formatString(out, "Namespace", m.Namespace)
|
||||
if !m.CreationTimestamp.IsZero() {
|
||||
formatTime(out, "Created", m.CreationTimestamp.Time)
|
||||
}
|
||||
formatMapStringString(out, "Labels", m.Labels)
|
||||
formatAnnotations(out, m, "")
|
||||
}
|
||||
|
||||
// DescribeWebhook holds the URL information about a webhook and for generic
|
||||
// webhooks it tells us if we allow env variables.
|
||||
type DescribeWebhook struct {
|
||||
URL string
|
||||
AllowEnv *bool
|
||||
}
|
||||
|
||||
// webhookDescribe returns a map of webhook trigger types and its corresponding
|
||||
// information.
|
||||
func webHooksDescribe(triggers []buildapi.BuildTriggerPolicy, name, namespace string, cli client.BuildConfigsNamespacer) map[string][]DescribeWebhook {
|
||||
result := map[string][]DescribeWebhook{}
|
||||
|
||||
for _, trigger := range triggers {
|
||||
var webHookTrigger string
|
||||
var allowEnv *bool
|
||||
|
||||
switch trigger.Type {
|
||||
case buildapi.GitHubWebHookBuildTriggerType:
|
||||
webHookTrigger = trigger.GitHubWebHook.Secret
|
||||
|
||||
case buildapi.GenericWebHookBuildTriggerType:
|
||||
webHookTrigger = trigger.GenericWebHook.Secret
|
||||
allowEnv = &trigger.GenericWebHook.AllowEnv
|
||||
|
||||
default:
|
||||
continue
|
||||
}
|
||||
webHookDesc := result[string(trigger.Type)]
|
||||
|
||||
if len(webHookTrigger) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
var urlStr string
|
||||
url, err := cli.BuildConfigs(namespace).WebHookURL(name, &trigger)
|
||||
if err != nil {
|
||||
urlStr = fmt.Sprintf("<error: %s>", err.Error())
|
||||
} else {
|
||||
urlStr = url.String()
|
||||
}
|
||||
|
||||
webHookDesc = append(webHookDesc,
|
||||
DescribeWebhook{
|
||||
URL: urlStr,
|
||||
AllowEnv: allowEnv,
|
||||
})
|
||||
result[string(trigger.Type)] = webHookDesc
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
var reLongImageID = regexp.MustCompile(`[a-f0-9]{60,}$`)
|
||||
|
||||
// shortenImagePullSpec returns a version of the pull spec intended for
|
||||
// display, which may result in the image not being usable via cut-and-paste
|
||||
// for users.
|
||||
func shortenImagePullSpec(spec string) string {
|
||||
if reLongImageID.MatchString(spec) {
|
||||
return spec[:len(spec)-50]
|
||||
}
|
||||
return spec
|
||||
}
|
||||
|
||||
func formatImageStreamTags(out *tabwriter.Writer, stream *imageapi.ImageStream) {
|
||||
if len(stream.Status.Tags) == 0 && len(stream.Spec.Tags) == 0 {
|
||||
fmt.Fprintf(out, "Tags:\t<none>\n")
|
||||
return
|
||||
}
|
||||
|
||||
now := timeNowFn()
|
||||
|
||||
images := make(map[string]string)
|
||||
for tag, tags := range stream.Status.Tags {
|
||||
for _, item := range tags.Items {
|
||||
switch {
|
||||
case len(item.Image) > 0:
|
||||
if _, ok := images[item.Image]; !ok {
|
||||
images[item.Image] = tag
|
||||
}
|
||||
case len(item.DockerImageReference) > 0:
|
||||
if _, ok := images[item.DockerImageReference]; !ok {
|
||||
images[item.Image] = item.DockerImageReference
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sortedTags := []string{}
|
||||
for k := range stream.Status.Tags {
|
||||
sortedTags = append(sortedTags, k)
|
||||
}
|
||||
var localReferences sets.String
|
||||
var referentialTags map[string]sets.String
|
||||
for k := range stream.Spec.Tags {
|
||||
if target, _, ok, multiple := imageapi.FollowTagReference(stream, k); ok && multiple {
|
||||
if referentialTags == nil {
|
||||
referentialTags = make(map[string]sets.String)
|
||||
}
|
||||
if localReferences == nil {
|
||||
localReferences = sets.NewString()
|
||||
}
|
||||
localReferences.Insert(k)
|
||||
v := referentialTags[target]
|
||||
if v == nil {
|
||||
v = sets.NewString()
|
||||
referentialTags[target] = v
|
||||
}
|
||||
v.Insert(k)
|
||||
}
|
||||
if _, ok := stream.Status.Tags[k]; !ok {
|
||||
sortedTags = append(sortedTags, k)
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(out, "Unique Images:\t%d\nTags:\t%d\n\n", len(images), len(sortedTags))
|
||||
|
||||
first := true
|
||||
imageapi.PrioritizeTags(sortedTags)
|
||||
for _, tag := range sortedTags {
|
||||
if localReferences.Has(tag) {
|
||||
continue
|
||||
}
|
||||
if first {
|
||||
first = false
|
||||
} else {
|
||||
fmt.Fprintf(out, "\n")
|
||||
}
|
||||
taglist, _ := stream.Status.Tags[tag]
|
||||
tagRef, hasSpecTag := stream.Spec.Tags[tag]
|
||||
scheduled := false
|
||||
insecure := false
|
||||
importing := false
|
||||
|
||||
var name string
|
||||
if hasSpecTag && tagRef.From != nil {
|
||||
if len(tagRef.From.Namespace) > 0 && tagRef.From.Namespace != stream.Namespace {
|
||||
name = fmt.Sprintf("%s/%s", tagRef.From.Namespace, tagRef.From.Name)
|
||||
} else {
|
||||
name = tagRef.From.Name
|
||||
}
|
||||
scheduled, insecure = tagRef.ImportPolicy.Scheduled, tagRef.ImportPolicy.Insecure
|
||||
gen := imageapi.LatestObservedTagGeneration(stream, tag)
|
||||
importing = !tagRef.Reference && tagRef.Generation != nil && *tagRef.Generation != gen
|
||||
}
|
||||
|
||||
// updates whenever tag :5.2 is changed
|
||||
|
||||
// :latest (30 minutes ago) -> 102.205.358.453/foo/bar@sha256:abcde734
|
||||
// error: last import failed 20 minutes ago
|
||||
// updates automatically from index.docker.io/mysql/bar
|
||||
// will use insecure HTTPS connections or HTTP
|
||||
//
|
||||
// MySQL 5.5
|
||||
// ---------
|
||||
// Describes a system for updating based on practical changes to a database system
|
||||
// with some other data involved
|
||||
//
|
||||
// 20 minutes ago <import failed>
|
||||
// Failed to locate the server in time
|
||||
// 30 minutes ago 102.205.358.453/foo/bar@sha256:abcdef
|
||||
// 1 hour ago 102.205.358.453/foo/bar@sha256:bfedfc
|
||||
|
||||
//var shortErrors []string
|
||||
/*
|
||||
var internalReference *imageapi.DockerImageReference
|
||||
if value := stream.Status.DockerImageRepository; len(value) > 0 {
|
||||
ref, err := imageapi.ParseDockerImageReference(value)
|
||||
if err != nil {
|
||||
internalReference = &ref
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if referentialTags[tag].Len() > 0 {
|
||||
references := referentialTags[tag].List()
|
||||
imageapi.PrioritizeTags(references)
|
||||
fmt.Fprintf(out, "%s (%s)\n", tag, strings.Join(references, ", "))
|
||||
} else {
|
||||
fmt.Fprintf(out, "%s\n", tag)
|
||||
}
|
||||
|
||||
switch {
|
||||
case !hasSpecTag || tagRef.From == nil:
|
||||
fmt.Fprintf(out, " pushed image\n")
|
||||
case tagRef.From.Kind == "ImageStreamTag":
|
||||
switch {
|
||||
case tagRef.Reference:
|
||||
fmt.Fprintf(out, " reference to %s\n", name)
|
||||
case scheduled:
|
||||
fmt.Fprintf(out, " updates automatically from %s\n", name)
|
||||
default:
|
||||
fmt.Fprintf(out, " tagged from %s\n", name)
|
||||
}
|
||||
case tagRef.From.Kind == "DockerImage":
|
||||
switch {
|
||||
case tagRef.Reference:
|
||||
fmt.Fprintf(out, " reference to registry %s\n", name)
|
||||
case scheduled:
|
||||
fmt.Fprintf(out, " updates automatically from registry %s\n", name)
|
||||
default:
|
||||
fmt.Fprintf(out, " tagged from %s\n", name)
|
||||
}
|
||||
case tagRef.From.Kind == "ImageStreamImage":
|
||||
switch {
|
||||
case tagRef.Reference:
|
||||
fmt.Fprintf(out, " reference to image %s\n", name)
|
||||
default:
|
||||
fmt.Fprintf(out, " tagged from %s\n", name)
|
||||
}
|
||||
default:
|
||||
switch {
|
||||
case tagRef.Reference:
|
||||
fmt.Fprintf(out, " reference to %s %s\n", tagRef.From.Kind, name)
|
||||
default:
|
||||
fmt.Fprintf(out, " updates from %s %s\n", tagRef.From.Kind, name)
|
||||
}
|
||||
}
|
||||
if insecure {
|
||||
fmt.Fprintf(out, " will use insecure HTTPS or HTTP connections\n")
|
||||
}
|
||||
|
||||
fmt.Fprintln(out)
|
||||
|
||||
extraOutput := false
|
||||
if d := tagRef.Annotations["description"]; len(d) > 0 {
|
||||
fmt.Fprintf(out, " %s\n", d)
|
||||
extraOutput = true
|
||||
}
|
||||
if t := tagRef.Annotations["tags"]; len(t) > 0 {
|
||||
fmt.Fprintf(out, " Tags: %s\n", strings.Join(strings.Split(t, ","), ", "))
|
||||
extraOutput = true
|
||||
}
|
||||
if t := tagRef.Annotations["supports"]; len(t) > 0 {
|
||||
fmt.Fprintf(out, " Supports: %s\n", strings.Join(strings.Split(t, ","), ", "))
|
||||
extraOutput = true
|
||||
}
|
||||
if t := tagRef.Annotations["sampleRepo"]; len(t) > 0 {
|
||||
fmt.Fprintf(out, " Example Repo: %s\n", t)
|
||||
extraOutput = true
|
||||
}
|
||||
if extraOutput {
|
||||
fmt.Fprintln(out)
|
||||
}
|
||||
|
||||
if importing {
|
||||
fmt.Fprintf(out, " ~ importing latest image ...\n")
|
||||
}
|
||||
|
||||
for i := range taglist.Conditions {
|
||||
condition := &taglist.Conditions[i]
|
||||
switch condition.Type {
|
||||
case imageapi.ImportSuccess:
|
||||
if condition.Status == api.ConditionFalse {
|
||||
d := now.Sub(condition.LastTransitionTime.Time)
|
||||
fmt.Fprintf(out, " ! error: Import failed (%s): %s\n %s ago\n", condition.Reason, condition.Message, units.HumanDuration(d))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(taglist.Items) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
for i, event := range taglist.Items {
|
||||
d := now.Sub(event.Created.Time)
|
||||
|
||||
if i == 0 {
|
||||
fmt.Fprintf(out, " * %s\n", event.DockerImageReference)
|
||||
} else {
|
||||
fmt.Fprintf(out, " %s\n", event.DockerImageReference)
|
||||
}
|
||||
|
||||
ref, err := imageapi.ParseDockerImageReference(event.DockerImageReference)
|
||||
id := event.Image
|
||||
if len(id) > 0 && err == nil && ref.ID != id {
|
||||
fmt.Fprintf(out, " %s ago\t%s\n", units.HumanDuration(d), id)
|
||||
} else {
|
||||
fmt.Fprintf(out, " %s ago\n", units.HumanDuration(d))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-1057
File diff suppressed because it is too large
Load Diff
-1458
File diff suppressed because it is too large
Load Diff
-176
@@ -1,176 +0,0 @@
|
||||
package flagtypes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// urlPrefixes is the list of string prefix values that may indicate a URL
|
||||
// is present.
|
||||
var urlPrefixes = []string{"http://", "https://", "tcp://"}
|
||||
|
||||
// Addr is a flag type that attempts to load a host, IP, host:port, or
|
||||
// URL value from a string argument. It tracks whether the value was set
|
||||
// and allows the caller to provide defaults for the scheme and port.
|
||||
type Addr struct {
|
||||
// Specified by the caller
|
||||
DefaultScheme string
|
||||
DefaultPort int
|
||||
AllowPrefix bool
|
||||
|
||||
// Provided will be true if Set is invoked
|
||||
Provided bool
|
||||
// Value is the exact value provided on the flag
|
||||
Value string
|
||||
|
||||
// URL represents the user input. The Host field is guaranteed
|
||||
// to be set if Provided is true
|
||||
URL *url.URL
|
||||
// Host is the hostname or IP portion of the user input
|
||||
Host string
|
||||
// IPv6Host is true if the hostname appears to be an IPv6 input
|
||||
IPv6Host bool
|
||||
// Port is the port portion of the user input. Will be 0 if no port was found
|
||||
// and no default port could be established.
|
||||
Port int
|
||||
}
|
||||
|
||||
// Default creates a new Address with the value set
|
||||
func (a Addr) Default() Addr {
|
||||
if err := a.Set(a.Value); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
a.Provided = false
|
||||
return a
|
||||
}
|
||||
|
||||
// String returns the string representation of the Addr
|
||||
func (a *Addr) String() string {
|
||||
if a.URL == nil {
|
||||
return a.Value
|
||||
}
|
||||
return a.URL.String()
|
||||
}
|
||||
|
||||
// Set attempts to set a string value to an address
|
||||
func (a *Addr) Set(value string) error {
|
||||
scheme := a.DefaultScheme
|
||||
if len(scheme) == 0 {
|
||||
scheme = "tcp"
|
||||
}
|
||||
addr := &url.URL{
|
||||
Scheme: scheme,
|
||||
}
|
||||
|
||||
switch {
|
||||
case a.isURL(value):
|
||||
parsed, err := url.Parse(value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("not a valid URL: %v", err)
|
||||
}
|
||||
if !a.AllowPrefix {
|
||||
parsed.Path = ""
|
||||
}
|
||||
parsed.RawQuery = ""
|
||||
parsed.Fragment = ""
|
||||
|
||||
if strings.Contains(parsed.Host, ":") {
|
||||
host, port, err := net.SplitHostPort(parsed.Host)
|
||||
if err != nil {
|
||||
return fmt.Errorf("not a valid host:port: %v", err)
|
||||
}
|
||||
portNum, err := strconv.ParseUint(port, 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("not a valid port: %v", err)
|
||||
}
|
||||
a.Host = host
|
||||
a.Port = int(portNum)
|
||||
|
||||
} else {
|
||||
port := 0
|
||||
switch parsed.Scheme {
|
||||
case "http":
|
||||
port = 80
|
||||
case "https":
|
||||
port = 443
|
||||
default:
|
||||
return fmt.Errorf("no port specified")
|
||||
}
|
||||
a.Host = parsed.Host
|
||||
a.Port = port
|
||||
}
|
||||
addr = parsed
|
||||
|
||||
case isIPv6Host(value):
|
||||
a.Host = value
|
||||
a.Port = a.DefaultPort
|
||||
|
||||
case strings.Contains(value, ":"):
|
||||
host, port, err := net.SplitHostPort(value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("not a valid host:port: %v", err)
|
||||
}
|
||||
portNum, err := strconv.ParseUint(port, 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("not a valid port: %v", err)
|
||||
}
|
||||
a.Host = host
|
||||
a.Port = int(portNum)
|
||||
|
||||
default:
|
||||
port := a.DefaultPort
|
||||
if port == 0 {
|
||||
switch a.DefaultScheme {
|
||||
case "http":
|
||||
port = 80
|
||||
case "https":
|
||||
port = 443
|
||||
default:
|
||||
return fmt.Errorf("no port specified")
|
||||
}
|
||||
}
|
||||
a.Host = value
|
||||
a.Port = port
|
||||
}
|
||||
addr.Host = net.JoinHostPort(a.Host, strconv.FormatInt(int64(a.Port), 10))
|
||||
|
||||
if value != a.Value {
|
||||
a.Provided = true
|
||||
}
|
||||
a.URL = addr
|
||||
a.IPv6Host = isIPv6Host(a.Host)
|
||||
a.Value = value
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Type returns a string representation of what kind of value this is
|
||||
func (a *Addr) Type() string {
|
||||
return "string"
|
||||
}
|
||||
|
||||
// isURL returns true if the provided value appears to be a valid URL.
|
||||
func (a *Addr) isURL(value string) bool {
|
||||
prefixes := urlPrefixes
|
||||
if a.DefaultScheme != "" {
|
||||
prefixes = append(prefixes, fmt.Sprintf("%s://", a.DefaultScheme))
|
||||
}
|
||||
for _, p := range prefixes {
|
||||
if strings.HasPrefix(value, p) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isIPv6Host returns true if the value appears to be an IPv6 host string (that does
|
||||
// not include a port).
|
||||
func isIPv6Host(value string) bool {
|
||||
if strings.HasPrefix(value, "[") {
|
||||
return false
|
||||
}
|
||||
return strings.Contains(value, "%") || strings.Count(value, ":") > 1
|
||||
}
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
// Package flagtypes provides types that implement the pflags.Value interface for
|
||||
// converting command line flags to objects.
|
||||
package flagtypes
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
package flagtypes
|
||||
|
||||
import (
|
||||
"flag"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
// GLog binds the log flags from the default Google "flag" package into a pflag.FlagSet.
|
||||
func GLog(flags *pflag.FlagSet) {
|
||||
from := flag.CommandLine
|
||||
if flag := from.Lookup("v"); flag != nil {
|
||||
level := flag.Value.(*glog.Level)
|
||||
levelPtr := (*int32)(level)
|
||||
flags.Int32Var(levelPtr, "loglevel", 0, "Set the level of log output (0-10)")
|
||||
}
|
||||
if flag := from.Lookup("vmodule"); flag != nil {
|
||||
value := flag.Value
|
||||
flags.Var(pflagValue{value}, "logspec", "Set per module logging with file|pattern=LEVEL,...")
|
||||
}
|
||||
}
|
||||
|
||||
type pflagValue struct {
|
||||
flag.Value
|
||||
}
|
||||
|
||||
func (pflagValue) Type() string {
|
||||
return "string"
|
||||
}
|
||||
-59
@@ -1,59 +0,0 @@
|
||||
package flagtypes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// lifted from kubernetes/pkg/util/net.go. same flags vs pflags problem as we had with StringList
|
||||
|
||||
// IP adapts net.IP for use as a flag.
|
||||
type IP net.IP
|
||||
|
||||
func (ip IP) String() string {
|
||||
return net.IP(ip).String()
|
||||
}
|
||||
|
||||
func (ip *IP) Set(value string) error {
|
||||
*ip = IP(net.ParseIP(strings.TrimSpace(value)))
|
||||
if *ip == nil {
|
||||
return fmt.Errorf("invalid IP address: '%s'", value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Type returns a string representation of what kind of argument this is
|
||||
func (ip *IP) Type() string {
|
||||
return "cmd.flagtypes.IP"
|
||||
}
|
||||
|
||||
// IPNet adapts net.IPNet for use as a flag.
|
||||
type IPNet net.IPNet
|
||||
|
||||
func DefaultIPNet(value string) IPNet {
|
||||
ret := IPNet{}
|
||||
if err := ret.Set(value); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (ipnet IPNet) String() string {
|
||||
n := net.IPNet(ipnet)
|
||||
return n.String()
|
||||
}
|
||||
|
||||
func (ipnet *IPNet) Set(value string) error {
|
||||
_, n, err := net.ParseCIDR(strings.TrimSpace(value))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*ipnet = IPNet(*n)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Type returns a string representation of what kind of argument this is
|
||||
func (ipnet *IPNet) Type() string {
|
||||
return "cmd.flagtypes.IPNet"
|
||||
}
|
||||
-136
@@ -1,136 +0,0 @@
|
||||
package clientcmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
kapi "k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/client/typed/discovery"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
// CachedDiscoveryClient implements the functions that discovery server-supported API groups,
|
||||
// versions and resources.
|
||||
type CachedDiscoveryClient struct {
|
||||
discovery.DiscoveryInterface
|
||||
|
||||
// cacheDirectory is the directory where discovery docs are held. It must be unique per host:port combination to work well.
|
||||
cacheDirectory string
|
||||
|
||||
// ttl is how long the cache should be considered valid
|
||||
ttl time.Duration
|
||||
}
|
||||
|
||||
// ServerResourcesForGroupVersion returns the supported resources for a group and version.
|
||||
func (d *CachedDiscoveryClient) ServerResourcesForGroupVersion(groupVersion string) (*unversioned.APIResourceList, error) {
|
||||
filename := filepath.Join(d.cacheDirectory, groupVersion, "serverresources.json")
|
||||
cachedBytes, err := d.getCachedFile(filename)
|
||||
// don't fail on errors, we either don't have a file or won't be able to run the cached check. Either way we can fallback.
|
||||
if err == nil {
|
||||
cachedResources := &unversioned.APIResourceList{}
|
||||
if err := runtime.DecodeInto(kapi.Codecs.UniversalDecoder(), cachedBytes, cachedResources); err == nil {
|
||||
glog.V(6).Infof("returning cached discovery info from %v", filename)
|
||||
return cachedResources, nil
|
||||
}
|
||||
}
|
||||
|
||||
liveResources, err := d.DiscoveryInterface.ServerResourcesForGroupVersion(groupVersion)
|
||||
if err != nil {
|
||||
return liveResources, err
|
||||
}
|
||||
|
||||
if err := d.writeCachedFile(filename, liveResources); err != nil {
|
||||
glog.V(3).Infof("failed to write cache to %v due to %v", filename, err)
|
||||
}
|
||||
|
||||
return liveResources, nil
|
||||
}
|
||||
|
||||
// ServerResources returns the supported resources for all groups and versions.
|
||||
func (d *CachedDiscoveryClient) 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
|
||||
}
|
||||
|
||||
func (d *CachedDiscoveryClient) ServerGroups() (*unversioned.APIGroupList, error) {
|
||||
filename := filepath.Join(d.cacheDirectory, "servergroups.json")
|
||||
cachedBytes, err := d.getCachedFile(filename)
|
||||
// don't fail on errors, we either don't have a file or won't be able to run the cached check. Either way we can fallback.
|
||||
if err == nil {
|
||||
cachedGroups := &unversioned.APIGroupList{}
|
||||
if err := runtime.DecodeInto(kapi.Codecs.UniversalDecoder(), cachedBytes, cachedGroups); err == nil {
|
||||
glog.V(6).Infof("returning cached discovery info from %v", filename)
|
||||
return cachedGroups, nil
|
||||
}
|
||||
}
|
||||
|
||||
liveGroups, err := d.DiscoveryInterface.ServerGroups()
|
||||
if err != nil {
|
||||
return liveGroups, err
|
||||
}
|
||||
|
||||
if err := d.writeCachedFile(filename, liveGroups); err != nil {
|
||||
glog.V(3).Infof("failed to write cache to %v due to %v", filename, err)
|
||||
}
|
||||
|
||||
return liveGroups, nil
|
||||
}
|
||||
|
||||
func (d *CachedDiscoveryClient) getCachedFile(filename string) ([]byte, error) {
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fileInfo, err := file.Stat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if time.Now().After(fileInfo.ModTime().Add(d.ttl)) {
|
||||
return nil, errors.New("cache expired")
|
||||
}
|
||||
|
||||
// the cache is present and its valid. Try to read and use it.
|
||||
cachedBytes, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cachedBytes, nil
|
||||
}
|
||||
|
||||
func (d *CachedDiscoveryClient) writeCachedFile(filename string, obj runtime.Object) error {
|
||||
if err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bytes, err := runtime.Encode(kapi.Codecs.LegacyCodec(), obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(filename, bytes, 0755)
|
||||
}
|
||||
|
||||
// NewCachedDiscoveryClient creates a new DiscoveryClient. cacheDirectory is the directory where discovery docs are held. It must be unique per host:port combination to work well.
|
||||
func NewCachedDiscoveryClient(delegate discovery.DiscoveryInterface, cacheDirectory string, ttl time.Duration) *CachedDiscoveryClient {
|
||||
return &CachedDiscoveryClient{DiscoveryInterface: delegate, cacheDirectory: cacheDirectory, ttl: ttl}
|
||||
}
|
||||
-247
@@ -1,247 +0,0 @@
|
||||
package clientcmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/pflag"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/client/restclient"
|
||||
kclient "k8s.io/kubernetes/pkg/client/unversioned"
|
||||
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
|
||||
|
||||
osclient "github.com/openshift/origin/pkg/client"
|
||||
"github.com/openshift/origin/pkg/cmd/flagtypes"
|
||||
"github.com/openshift/origin/pkg/cmd/util"
|
||||
)
|
||||
|
||||
const ConfigSyntax = " --master=<addr>"
|
||||
|
||||
// Config contains all the necessary bits for client configuration
|
||||
type Config struct {
|
||||
// MasterAddr is the address the master can be reached on (host, host:port, or URL).
|
||||
MasterAddr flagtypes.Addr
|
||||
// KubernetesAddr is the address of the Kubernetes server (host, host:port, or URL).
|
||||
// If omitted defaults to the master.
|
||||
KubernetesAddr flagtypes.Addr
|
||||
// CommonConfig is the shared base config for both the OpenShift config and Kubernetes config
|
||||
CommonConfig restclient.Config
|
||||
// Namespace is the namespace to act in
|
||||
Namespace string
|
||||
|
||||
// If set, allow kubeconfig file loading
|
||||
FromFile bool
|
||||
// If true, no environment is loaded (for testing, primarily)
|
||||
SkipEnv bool
|
||||
clientConfig clientcmd.ClientConfig
|
||||
}
|
||||
|
||||
// NewConfig returns a new configuration
|
||||
func NewConfig() *Config {
|
||||
return &Config{
|
||||
MasterAddr: flagtypes.Addr{Value: "localhost:8080", DefaultScheme: "http", DefaultPort: 8080, AllowPrefix: true}.Default(),
|
||||
KubernetesAddr: flagtypes.Addr{Value: "localhost:8080", DefaultScheme: "http", DefaultPort: 8080}.Default(),
|
||||
CommonConfig: restclient.Config{},
|
||||
}
|
||||
}
|
||||
|
||||
// AnonymousClientConfig returns a copy of the given config with all user credentials (cert/key, bearer token, and username/password) removed
|
||||
func AnonymousClientConfig(config *restclient.Config) restclient.Config {
|
||||
// copy only known safe fields
|
||||
// TODO: expose a copy method on the config that is "auth free"
|
||||
return restclient.Config{
|
||||
Host: config.Host,
|
||||
APIPath: config.APIPath,
|
||||
Prefix: config.Prefix,
|
||||
ContentConfig: config.ContentConfig,
|
||||
TLSClientConfig: restclient.TLSClientConfig{
|
||||
CAFile: config.TLSClientConfig.CAFile,
|
||||
CAData: config.TLSClientConfig.CAData,
|
||||
},
|
||||
RateLimiter: config.RateLimiter,
|
||||
Insecure: config.Insecure,
|
||||
UserAgent: config.UserAgent,
|
||||
Transport: config.Transport,
|
||||
WrapTransport: config.WrapTransport,
|
||||
QPS: config.QPS,
|
||||
Burst: config.Burst,
|
||||
}
|
||||
}
|
||||
|
||||
// BindClientConfigSecurityFlags adds flags for the supplied client config
|
||||
func BindClientConfigSecurityFlags(config *restclient.Config, flags *pflag.FlagSet) {
|
||||
flags.BoolVar(&config.Insecure, "insecure-skip-tls-verify", config.Insecure, "If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure.")
|
||||
flags.StringVar(&config.CertFile, "client-certificate", config.CertFile, "Path to a client certificate file for TLS.")
|
||||
flags.StringVar(&config.KeyFile, "client-key", config.KeyFile, "Path to a client key file for TLS.")
|
||||
flags.StringVar(&config.CAFile, "certificate-authority", config.CAFile, "Path to a cert. file for the certificate authority")
|
||||
flags.StringVar(&config.BearerToken, "token", config.BearerToken, "If present, the bearer token for this request.")
|
||||
}
|
||||
|
||||
// Bind binds configuration values to the passed flagset
|
||||
func (cfg *Config) Bind(flags *pflag.FlagSet) {
|
||||
flags.Var(&cfg.MasterAddr, "master", "The address the master can be reached on (host, host:port, or URL).")
|
||||
flags.Var(&cfg.KubernetesAddr, "kubernetes", "The address of the Kubernetes server (host, host:port, or URL). If omitted defaults to the master.")
|
||||
|
||||
if cfg.FromFile {
|
||||
cfg.clientConfig = DefaultClientConfig(flags)
|
||||
} else {
|
||||
BindClientConfigSecurityFlags(&cfg.CommonConfig, flags)
|
||||
}
|
||||
}
|
||||
|
||||
// BindToFile is used when this config will not be bound to flags, but should load the config file
|
||||
// from disk if available.
|
||||
func (cfg *Config) BindToFile() *Config {
|
||||
cfg.clientConfig = DefaultClientConfig(pflag.NewFlagSet("empty", pflag.ContinueOnError))
|
||||
return cfg
|
||||
}
|
||||
|
||||
func EnvVars(host string, caData []byte, insecure bool, bearerTokenFile string) []api.EnvVar {
|
||||
envvars := []api.EnvVar{
|
||||
{Name: "KUBERNETES_MASTER", Value: host},
|
||||
{Name: "OPENSHIFT_MASTER", Value: host},
|
||||
}
|
||||
|
||||
if len(bearerTokenFile) > 0 {
|
||||
envvars = append(envvars, api.EnvVar{Name: "BEARER_TOKEN_FILE", Value: bearerTokenFile})
|
||||
}
|
||||
|
||||
if len(caData) > 0 {
|
||||
envvars = append(envvars, api.EnvVar{Name: "OPENSHIFT_CA_DATA", Value: string(caData)})
|
||||
} else if insecure {
|
||||
envvars = append(envvars, api.EnvVar{Name: "OPENSHIFT_INSECURE", Value: "true"})
|
||||
}
|
||||
|
||||
return envvars
|
||||
}
|
||||
|
||||
func (cfg *Config) bindEnv() error {
|
||||
// bypass loading from env
|
||||
if cfg.SkipEnv {
|
||||
return nil
|
||||
}
|
||||
var err error
|
||||
|
||||
// callers may not use the config file if they have specified a master directly, for backwards
|
||||
// compatibility with components that used to use env, switch to service account token, and have
|
||||
// config defined in env.
|
||||
_, masterSet := util.GetEnv("OPENSHIFT_MASTER")
|
||||
specifiedMaster := masterSet || cfg.MasterAddr.Provided
|
||||
|
||||
if cfg.clientConfig != nil && !specifiedMaster {
|
||||
clientConfig, err := cfg.clientConfig.ClientConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfg.CommonConfig = *clientConfig
|
||||
cfg.Namespace, _, err = cfg.clientConfig.Namespace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !cfg.MasterAddr.Provided {
|
||||
cfg.MasterAddr.Set(cfg.CommonConfig.Host)
|
||||
}
|
||||
if !cfg.KubernetesAddr.Provided {
|
||||
cfg.KubernetesAddr.Set(cfg.CommonConfig.Host)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Legacy path - preserve env vars set on pods that previously were honored.
|
||||
if value, ok := util.GetEnv("KUBERNETES_MASTER"); ok && !cfg.KubernetesAddr.Provided {
|
||||
cfg.KubernetesAddr.Set(value)
|
||||
}
|
||||
if value, ok := util.GetEnv("OPENSHIFT_MASTER"); ok && !cfg.MasterAddr.Provided {
|
||||
cfg.MasterAddr.Set(value)
|
||||
}
|
||||
if value, ok := util.GetEnv("BEARER_TOKEN"); ok && len(cfg.CommonConfig.BearerToken) == 0 {
|
||||
cfg.CommonConfig.BearerToken = value
|
||||
}
|
||||
if value, ok := util.GetEnv("BEARER_TOKEN_FILE"); ok && len(cfg.CommonConfig.BearerToken) == 0 {
|
||||
if tokenData, tokenErr := ioutil.ReadFile(value); tokenErr == nil {
|
||||
cfg.CommonConfig.BearerToken = strings.TrimSpace(string(tokenData))
|
||||
if len(cfg.CommonConfig.BearerToken) == 0 {
|
||||
err = fmt.Errorf("BEARER_TOKEN_FILE %q was empty", value)
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("Error reading BEARER_TOKEN_FILE %q: %v", value, tokenErr)
|
||||
}
|
||||
}
|
||||
|
||||
if value, ok := util.GetEnv("OPENSHIFT_CA_FILE"); ok && len(cfg.CommonConfig.CAFile) == 0 {
|
||||
cfg.CommonConfig.CAFile = value
|
||||
} else if value, ok := util.GetEnv("OPENSHIFT_CA_DATA"); ok && len(cfg.CommonConfig.CAData) == 0 {
|
||||
cfg.CommonConfig.CAData = []byte(value)
|
||||
}
|
||||
|
||||
if value, ok := util.GetEnv("OPENSHIFT_CERT_FILE"); ok && len(cfg.CommonConfig.CertFile) == 0 {
|
||||
cfg.CommonConfig.CertFile = value
|
||||
} else if value, ok := util.GetEnv("OPENSHIFT_CERT_DATA"); ok && len(cfg.CommonConfig.CertData) == 0 {
|
||||
cfg.CommonConfig.CertData = []byte(value)
|
||||
}
|
||||
|
||||
if value, ok := util.GetEnv("OPENSHIFT_KEY_FILE"); ok && len(cfg.CommonConfig.KeyFile) == 0 {
|
||||
cfg.CommonConfig.KeyFile = value
|
||||
} else if value, ok := util.GetEnv("OPENSHIFT_KEY_DATA"); ok && len(cfg.CommonConfig.KeyData) == 0 {
|
||||
cfg.CommonConfig.KeyData = []byte(value)
|
||||
}
|
||||
|
||||
if value, ok := util.GetEnv("OPENSHIFT_INSECURE"); ok && len(value) != 0 {
|
||||
cfg.CommonConfig.Insecure = value == "true"
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// KubeConfig returns the Kubernetes configuration
|
||||
func (cfg *Config) KubeConfig() *restclient.Config {
|
||||
err := cfg.bindEnv()
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
}
|
||||
|
||||
kaddr := cfg.KubernetesAddr
|
||||
if !kaddr.Provided {
|
||||
kaddr = cfg.MasterAddr
|
||||
}
|
||||
|
||||
kConfig := cfg.CommonConfig
|
||||
kConfig.Host = kaddr.URL.String()
|
||||
|
||||
return &kConfig
|
||||
}
|
||||
|
||||
// OpenShiftConfig returns the OpenShift configuration
|
||||
func (cfg *Config) OpenShiftConfig() *restclient.Config {
|
||||
err := cfg.bindEnv()
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
}
|
||||
|
||||
osConfig := cfg.CommonConfig
|
||||
if len(osConfig.Host) == 0 || cfg.MasterAddr.Provided {
|
||||
osConfig.Host = cfg.MasterAddr.String()
|
||||
}
|
||||
|
||||
return &osConfig
|
||||
}
|
||||
|
||||
// Clients returns an OpenShift and a Kubernetes client from a given configuration
|
||||
func (cfg *Config) Clients() (osclient.Interface, kclient.Interface, error) {
|
||||
cfg.bindEnv()
|
||||
|
||||
kubeClient, err := kclient.New(cfg.KubeConfig())
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Unable to configure Kubernetes client: %v", err)
|
||||
}
|
||||
|
||||
osClient, err := osclient.New(cfg.OpenShiftConfig())
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Unable to configure Origin client: %v", err)
|
||||
}
|
||||
|
||||
return osClient, kubeClient, nil
|
||||
}
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
package clientcmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"github.com/openshift/origin/pkg/cmd/cli/config"
|
||||
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
|
||||
)
|
||||
|
||||
func DefaultClientConfig(flags *pflag.FlagSet) clientcmd.ClientConfig {
|
||||
loadingRules := config.NewOpenShiftClientConfigLoadingRules()
|
||||
flags.StringVar(&loadingRules.ExplicitPath, config.OpenShiftConfigFlagName, "", "Path to the config file to use for CLI requests.")
|
||||
cobra.MarkFlagFilename(flags, config.OpenShiftConfigFlagName)
|
||||
|
||||
overrides := &clientcmd.ConfigOverrides{}
|
||||
overrideFlags := clientcmd.RecommendedConfigOverrideFlags("")
|
||||
overrideFlags.ContextOverrideFlags.Namespace.ShortName = "n"
|
||||
overrideFlags.AuthOverrideFlags.Username.LongName = ""
|
||||
overrideFlags.AuthOverrideFlags.Password.LongName = ""
|
||||
clientcmd.BindOverrideFlags(overrides, flags, overrideFlags)
|
||||
cobra.MarkFlagFilename(flags, overrideFlags.AuthOverrideFlags.ClientCertificate.LongName)
|
||||
cobra.MarkFlagFilename(flags, overrideFlags.AuthOverrideFlags.ClientKey.LongName)
|
||||
cobra.MarkFlagFilename(flags, overrideFlags.ClusterOverrideFlags.CertificateAuthority.LongName)
|
||||
|
||||
clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, overrides)
|
||||
|
||||
return clientConfig
|
||||
}
|
||||
-108
@@ -1,108 +0,0 @@
|
||||
package clientcmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
kerrors "k8s.io/kubernetes/pkg/api/errors"
|
||||
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
|
||||
)
|
||||
|
||||
const (
|
||||
unknownReason = iota
|
||||
noServerFoundReason
|
||||
certificateAuthorityUnknownReason
|
||||
configurationInvalidReason
|
||||
tlsOversizedRecordReason
|
||||
|
||||
certificateAuthorityUnknownMsg = "The server uses a certificate signed by unknown authority. You may need to use the --certificate-authority flag to provide the path to a certificate file for the certificate authority, or --insecure-skip-tls-verify to bypass the certificate check and use insecure connections."
|
||||
notConfiguredMsg = `The client is not configured. You need to run the login command in order to create a default config for your server and credentials:
|
||||
oc login
|
||||
You can also run this command again providing the path to a config file directly, either through the --config flag of the KUBECONFIG environment variable.
|
||||
`
|
||||
tlsOversizedRecordMsg = `Unable to connect to %[2]s using TLS: %[1]s.
|
||||
Ensure the specified server supports HTTPS.`
|
||||
)
|
||||
|
||||
// GetPrettyMessageFor prettifys the message of the provided error
|
||||
func GetPrettyMessageFor(err error) string {
|
||||
return GetPrettyMessageForServer(err, "")
|
||||
}
|
||||
|
||||
// GetPrettyMessageForServer prettifys the message of the provided error
|
||||
func GetPrettyMessageForServer(err error, serverName string) string {
|
||||
if err == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
reason := detectReason(err)
|
||||
|
||||
switch reason {
|
||||
case noServerFoundReason:
|
||||
return notConfiguredMsg
|
||||
|
||||
case certificateAuthorityUnknownReason:
|
||||
return certificateAuthorityUnknownMsg
|
||||
|
||||
case tlsOversizedRecordReason:
|
||||
if len(serverName) == 0 {
|
||||
serverName = "server"
|
||||
}
|
||||
return fmt.Sprintf(tlsOversizedRecordMsg, err, serverName)
|
||||
}
|
||||
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
// GetPrettyErrorFor prettifys the message of the provided error
|
||||
func GetPrettyErrorFor(err error) error {
|
||||
return GetPrettyErrorForServer(err, "")
|
||||
}
|
||||
|
||||
// GetPrettyErrorForServer prettifys the message of the provided error
|
||||
func GetPrettyErrorForServer(err error, serverName string) error {
|
||||
return errors.New(GetPrettyMessageForServer(err, serverName))
|
||||
}
|
||||
|
||||
// IsNoServerFound checks whether the provided error is a 'no server found' error or not
|
||||
func IsNoServerFound(err error) bool {
|
||||
return detectReason(err) == noServerFoundReason
|
||||
}
|
||||
|
||||
// IsConfigurationInvalid checks whether the provided error is a 'invalid configuration' error or not
|
||||
func IsConfigurationInvalid(err error) bool {
|
||||
return detectReason(err) == configurationInvalidReason
|
||||
}
|
||||
|
||||
// IsCertificateAuthorityUnknown checks whether the provided error is a 'certificate authority unknown' error or not
|
||||
func IsCertificateAuthorityUnknown(err error) bool {
|
||||
return detectReason(err) == certificateAuthorityUnknownReason
|
||||
}
|
||||
|
||||
// IsForbidden checks whether the provided error is a 'forbidden' error or not
|
||||
func IsForbidden(err error) bool {
|
||||
return kerrors.IsForbidden(err)
|
||||
}
|
||||
|
||||
// IsTLSOversizedRecord checks whether the provided error is a url.Error
|
||||
// with "tls: oversized record received", which usually means TLS not supported.
|
||||
func IsTLSOversizedRecord(err error) bool {
|
||||
return detectReason(err) == tlsOversizedRecordReason
|
||||
}
|
||||
|
||||
func detectReason(err error) int {
|
||||
if err != nil {
|
||||
switch {
|
||||
case strings.Contains(err.Error(), "certificate signed by unknown authority"):
|
||||
return certificateAuthorityUnknownReason
|
||||
case strings.Contains(err.Error(), "no server defined"):
|
||||
return noServerFoundReason
|
||||
case clientcmd.IsConfigurationInvalid(err):
|
||||
return configurationInvalidReason
|
||||
case strings.Contains(err.Error(), "tls: oversized record received"):
|
||||
return tlsOversizedRecordReason
|
||||
}
|
||||
}
|
||||
return unknownReason
|
||||
}
|
||||
-1071
File diff suppressed because it is too large
Load Diff
-116
@@ -1,116 +0,0 @@
|
||||
package clientcmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/errors"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/client/restclient"
|
||||
kclient "k8s.io/kubernetes/pkg/client/unversioned"
|
||||
)
|
||||
|
||||
// negotiateVersion queries the server's supported api versions to find a version that both client and server support.
|
||||
// - If no version is provided, try registered client versions in order of preference.
|
||||
// - If version is provided, but not default config (explicitly requested via
|
||||
// commandline flag), and is unsupported by the server, print a warning to
|
||||
// stderr and try client's registered versions in order of preference.
|
||||
// - If version is config default, and the server does not support it, return an error.
|
||||
func negotiateVersion(client *kclient.Client, config *restclient.Config, requestedGV *unversioned.GroupVersion, clientGVs []unversioned.GroupVersion) (*unversioned.GroupVersion, error) {
|
||||
// Ensure we have a client
|
||||
var err error
|
||||
if client == nil {
|
||||
client, err = kclient.New(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Determine our preferred version
|
||||
preferredGV := copyGroupVersion(requestedGV)
|
||||
if preferredGV == nil {
|
||||
preferredGV = copyGroupVersion(config.GroupVersion)
|
||||
}
|
||||
|
||||
// Get server versions
|
||||
serverGVs, err := serverAPIVersions(client, "/oapi")
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
glog.V(4).Infof("Server path /oapi was not found, returning the requested group version %v", preferredGV)
|
||||
return preferredGV, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Find a version we can all agree on
|
||||
matchedGV, err := matchAPIVersion(preferredGV, clientGVs, serverGVs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Enforce a match if the preferredGV is the config default
|
||||
if config.GroupVersion != nil && (*preferredGV == *config.GroupVersion) && (*matchedGV != *config.GroupVersion) {
|
||||
return nil, fmt.Errorf("server does not support API version %q", config.GroupVersion.String())
|
||||
}
|
||||
|
||||
return matchedGV, err
|
||||
}
|
||||
|
||||
// serverAPIVersions fetches the server versions available from the groupless API at the given prefix
|
||||
func serverAPIVersions(c *kclient.Client, grouplessPrefix string) ([]unversioned.GroupVersion, error) {
|
||||
// Get versions doc
|
||||
var v unversioned.APIVersions
|
||||
if err := c.Get().AbsPath(grouplessPrefix).Do().Into(&v); err != nil {
|
||||
return []unversioned.GroupVersion{}, err
|
||||
}
|
||||
|
||||
// Convert to GroupVersion structs
|
||||
serverAPIVersions := []unversioned.GroupVersion{}
|
||||
for _, version := range v.Versions {
|
||||
gv, err := unversioned.ParseGroupVersion(version)
|
||||
if err != nil {
|
||||
return []unversioned.GroupVersion{}, err
|
||||
}
|
||||
serverAPIVersions = append(serverAPIVersions, gv)
|
||||
}
|
||||
return serverAPIVersions, nil
|
||||
}
|
||||
|
||||
func matchAPIVersion(preferredGV *unversioned.GroupVersion, clientGVs []unversioned.GroupVersion, serverGVs []unversioned.GroupVersion) (*unversioned.GroupVersion, error) {
|
||||
// If version explicitly requested verify that both client and server support it.
|
||||
// If server does not support warn, but try to negotiate a lower version.
|
||||
if preferredGV != nil {
|
||||
if !containsGroupVersion(clientGVs, *preferredGV) {
|
||||
return nil, fmt.Errorf("client does not support API version %q; client supported API versions: %v", preferredGV, clientGVs)
|
||||
}
|
||||
if containsGroupVersion(serverGVs, *preferredGV) {
|
||||
return preferredGV, nil
|
||||
}
|
||||
}
|
||||
|
||||
for _, clientGV := range clientGVs {
|
||||
if containsGroupVersion(serverGVs, clientGV) {
|
||||
t := clientGV
|
||||
return &t, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("failed to negotiate an api version; server supports: %v, client supports: %v", serverGVs, clientGVs)
|
||||
}
|
||||
|
||||
func copyGroupVersion(version *unversioned.GroupVersion) *unversioned.GroupVersion {
|
||||
if version == nil {
|
||||
return nil
|
||||
}
|
||||
versionCopy := *version
|
||||
return &versionCopy
|
||||
}
|
||||
|
||||
func containsGroupVersion(versions []unversioned.GroupVersion, version unversioned.GroupVersion) bool {
|
||||
for _, v := range versions {
|
||||
if v == version {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
-141
@@ -1,141 +0,0 @@
|
||||
package clientcmd
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api/meta"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/client/typed/discovery"
|
||||
)
|
||||
|
||||
// ShortcutExpander is a RESTMapper that can be used for OpenShift resources. It expands the resource first, then invokes the wrapped
|
||||
type ShortcutExpander struct {
|
||||
RESTMapper meta.RESTMapper
|
||||
|
||||
All []string
|
||||
}
|
||||
|
||||
var _ meta.RESTMapper = &ShortcutExpander{}
|
||||
|
||||
func NewShortcutExpander(discoveryClient discovery.DiscoveryInterface, delegate meta.RESTMapper) ShortcutExpander {
|
||||
defaultMapper := ShortcutExpander{RESTMapper: delegate}
|
||||
|
||||
// this assumes that legacy kube versions and legacy origin versions are the same, probably fair
|
||||
apiResources, err := discoveryClient.ServerResources()
|
||||
if err != nil {
|
||||
return defaultMapper
|
||||
}
|
||||
|
||||
availableResources := []unversioned.GroupVersionResource{}
|
||||
for groupVersionString, resourceList := range apiResources {
|
||||
currVersion, err := unversioned.ParseGroupVersion(groupVersionString)
|
||||
if err != nil {
|
||||
return defaultMapper
|
||||
}
|
||||
|
||||
for _, resource := range resourceList.APIResources {
|
||||
availableResources = append(availableResources, currVersion.WithResource(resource.Name))
|
||||
}
|
||||
}
|
||||
|
||||
availableAll := []string{}
|
||||
for _, requestedResource := range userResources {
|
||||
for _, availableResource := range availableResources {
|
||||
if requestedResource == availableResource.Resource {
|
||||
availableAll = append(availableAll, requestedResource)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ShortcutExpander{All: availableAll, RESTMapper: delegate}
|
||||
}
|
||||
|
||||
func (e ShortcutExpander) KindFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionKind, error) {
|
||||
return e.RESTMapper.KindFor(expandResourceShortcut(resource))
|
||||
}
|
||||
|
||||
func (e ShortcutExpander) KindsFor(resource unversioned.GroupVersionResource) ([]unversioned.GroupVersionKind, error) {
|
||||
return e.RESTMapper.KindsFor(expandResourceShortcut(resource))
|
||||
}
|
||||
|
||||
func (e ShortcutExpander) ResourcesFor(resource unversioned.GroupVersionResource) ([]unversioned.GroupVersionResource, error) {
|
||||
return e.RESTMapper.ResourcesFor(expandResourceShortcut(resource))
|
||||
}
|
||||
|
||||
func (e ShortcutExpander) ResourceFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionResource, error) {
|
||||
return e.RESTMapper.ResourceFor(expandResourceShortcut(resource))
|
||||
}
|
||||
|
||||
func (e ShortcutExpander) ResourceSingularizer(resource string) (string, error) {
|
||||
return e.RESTMapper.ResourceSingularizer(expandResourceShortcut(unversioned.GroupVersionResource{Resource: resource}).Resource)
|
||||
}
|
||||
|
||||
func (e ShortcutExpander) RESTMapping(gk unversioned.GroupKind, versions ...string) (*meta.RESTMapping, error) {
|
||||
return e.RESTMapper.RESTMapping(gk, versions...)
|
||||
}
|
||||
|
||||
func (e ShortcutExpander) RESTMappings(gk unversioned.GroupKind) ([]*meta.RESTMapping, error) {
|
||||
return e.RESTMapper.RESTMappings(gk)
|
||||
}
|
||||
|
||||
// userResources are the resource names that apply to the primary, user facing resources used by
|
||||
// client tools. They are in deletion-first order - dependent resources should be last.
|
||||
var userResources = []string{
|
||||
"buildconfigs", "builds",
|
||||
"imagestreams",
|
||||
"deploymentconfigs", "replicationcontrollers",
|
||||
"routes", "services",
|
||||
"pods",
|
||||
}
|
||||
|
||||
// AliasesForResource returns whether a resource has an alias or not
|
||||
func (e ShortcutExpander) AliasesForResource(resource string) ([]string, bool) {
|
||||
aliases := map[string][]string{
|
||||
"all": userResources,
|
||||
}
|
||||
if len(e.All) != 0 {
|
||||
aliases["all"] = e.All
|
||||
}
|
||||
|
||||
if res, ok := aliases[resource]; ok {
|
||||
return res, true
|
||||
}
|
||||
return e.RESTMapper.AliasesForResource(expandResourceShortcut(unversioned.GroupVersionResource{Resource: resource}).Resource)
|
||||
}
|
||||
|
||||
// shortForms is the list of short names to their expanded names
|
||||
var shortForms = map[string]string{
|
||||
"dc": "deploymentconfigs",
|
||||
"bc": "buildconfigs",
|
||||
"is": "imagestreams",
|
||||
"istag": "imagestreamtags",
|
||||
"isimage": "imagestreamimages",
|
||||
"sa": "serviceaccounts",
|
||||
"pv": "persistentvolumes",
|
||||
"pvc": "persistentvolumeclaims",
|
||||
"clusterquota": "clusterresourcequota",
|
||||
}
|
||||
|
||||
// expandResourceShortcut will return the expanded version of resource
|
||||
// (something that a pkg/api/meta.RESTMapper can understand), if it is
|
||||
// indeed a shortcut. Otherwise, will return resource unmodified.
|
||||
func expandResourceShortcut(resource unversioned.GroupVersionResource) unversioned.GroupVersionResource {
|
||||
if expanded, ok := shortForms[resource.Resource]; ok {
|
||||
resource.Resource = expanded
|
||||
return resource
|
||||
}
|
||||
return resource
|
||||
}
|
||||
|
||||
// resourceShortFormFor looks up for a short form of resource names.
|
||||
func resourceShortFormFor(resource string) (string, bool) {
|
||||
var alias string
|
||||
exists := false
|
||||
for k, val := range shortForms {
|
||||
if val == resource {
|
||||
alias = k
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
return alias, exists
|
||||
}
|
||||
+11
-15
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
@@ -20,6 +21,8 @@ import (
|
||||
// ErrExit is a marker interface for cli commands indicating that the response has been processed
|
||||
var ErrExit = fmt.Errorf("exit directly")
|
||||
|
||||
var commaSepVarsPattern = regexp.MustCompile(".*=.*,.*=.*")
|
||||
|
||||
// ReplaceCommandName recursively processes the examples in a given command to change a hardcoded
|
||||
// command name (like 'kubectl' to the appropriate target name). It returns c.
|
||||
func ReplaceCommandName(from, to string, c *cobra.Command) *cobra.Command {
|
||||
@@ -30,21 +33,6 @@ func ReplaceCommandName(from, to string, c *cobra.Command) *cobra.Command {
|
||||
return c
|
||||
}
|
||||
|
||||
// RequireNoArguments exits with a usage error if extra arguments are provided.
|
||||
func RequireNoArguments(c *cobra.Command, args []string) {
|
||||
if len(args) > 0 {
|
||||
kcmdutil.CheckErr(kcmdutil.UsageError(c, fmt.Sprintf(`unknown command "%s"`, strings.Join(args, " "))))
|
||||
}
|
||||
}
|
||||
|
||||
func DefaultSubCommandRun(out io.Writer) func(c *cobra.Command, args []string) {
|
||||
return func(c *cobra.Command, args []string) {
|
||||
c.SetOutput(out)
|
||||
RequireNoArguments(c, args)
|
||||
c.Help()
|
||||
}
|
||||
}
|
||||
|
||||
// GetDisplayFilename returns the absolute path of the filename as long as there was no error, otherwise it returns the filename as-is
|
||||
func GetDisplayFilename(filename string) string {
|
||||
if absName, err := filepath.Abs(filename); err == nil {
|
||||
@@ -163,3 +151,11 @@ func VersionedPrintObject(fn func(*cobra.Command, meta.RESTMapper, runtime.Objec
|
||||
return fn(c, mapper, obj, out)
|
||||
}
|
||||
}
|
||||
|
||||
func WarnAboutCommaSeparation(errout io.Writer, values []string, flag string) {
|
||||
for _, value := range values {
|
||||
if commaSepVarsPattern.MatchString(value) {
|
||||
fmt.Fprintf(errout, "warning: %s no longer accepts comma-separated lists of values. %q will be treated as a single key-value pair.\n", flag, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-122
@@ -1,122 +0,0 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/pkg/term"
|
||||
"github.com/golang/glog"
|
||||
|
||||
kterm "k8s.io/kubernetes/pkg/util/term"
|
||||
)
|
||||
|
||||
// PromptForString takes an io.Reader and prompts for user input if it's a terminal, returning the result.
|
||||
func PromptForString(r io.Reader, w io.Writer, format string, a ...interface{}) string {
|
||||
if w == nil {
|
||||
w = os.Stdout
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, format, a...)
|
||||
return readInput(r)
|
||||
}
|
||||
|
||||
// PromptForPasswordString prompts for user input by disabling echo in terminal, useful for password prompt.
|
||||
func PromptForPasswordString(r io.Reader, w io.Writer, format string, a ...interface{}) string {
|
||||
if w == nil {
|
||||
w = os.Stdout
|
||||
}
|
||||
|
||||
if file, ok := r.(*os.File); ok {
|
||||
inFd := file.Fd()
|
||||
|
||||
if term.IsTerminal(inFd) {
|
||||
oldState, err := term.SaveState(inFd)
|
||||
if err != nil {
|
||||
glog.V(3).Infof("Unable to save terminal state")
|
||||
return PromptForString(r, w, format, a...)
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, format, a...)
|
||||
|
||||
term.DisableEcho(inFd, oldState)
|
||||
|
||||
input := readInput(r)
|
||||
|
||||
defer term.RestoreTerminal(inFd, oldState)
|
||||
|
||||
fmt.Fprintf(w, "\n")
|
||||
|
||||
return input
|
||||
}
|
||||
glog.V(3).Infof("Stdin is not a terminal")
|
||||
return PromptForString(r, w, format, a...)
|
||||
}
|
||||
return PromptForString(r, w, format, a...)
|
||||
}
|
||||
|
||||
// PromptForBool prompts for user input of a boolean value. The accepted values are:
|
||||
// yes, y, true, t, 1 (not case sensitive)
|
||||
// no, n, false, f, 0 (not case sensitive)
|
||||
// A valid answer is mandatory so it will keep asking until an answer is provided.
|
||||
func PromptForBool(r io.Reader, w io.Writer, format string, a ...interface{}) bool {
|
||||
if w == nil {
|
||||
w = os.Stdout
|
||||
}
|
||||
|
||||
str := PromptForString(r, w, format, a...)
|
||||
switch strings.ToLower(str) {
|
||||
case "1", "t", "true", "y", "yes":
|
||||
return true
|
||||
case "0", "f", "false", "n", "no":
|
||||
return false
|
||||
}
|
||||
fmt.Println("Please enter 'yes' or 'no'.")
|
||||
return PromptForBool(r, w, format, a...)
|
||||
}
|
||||
|
||||
// PromptForStringWithDefault prompts for user input but take a default in case nothing is provided.
|
||||
func PromptForStringWithDefault(r io.Reader, w io.Writer, def string, format string, a ...interface{}) string {
|
||||
if w == nil {
|
||||
w = os.Stdout
|
||||
}
|
||||
|
||||
s := PromptForString(r, w, format, a...)
|
||||
if len(s) == 0 {
|
||||
return def
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func readInput(r io.Reader) string {
|
||||
if kterm.IsTerminal(r) {
|
||||
return readInputFromTerminal(r)
|
||||
}
|
||||
return readInputFromReader(r)
|
||||
}
|
||||
|
||||
func readInputFromTerminal(r io.Reader) string {
|
||||
reader := bufio.NewReader(r)
|
||||
result, _ := reader.ReadString('\n')
|
||||
return strings.TrimRight(result, "\r\n")
|
||||
}
|
||||
|
||||
func readInputFromReader(r io.Reader) string {
|
||||
var result string
|
||||
fmt.Fscan(r, &result)
|
||||
return result
|
||||
}
|
||||
|
||||
// IsTerminalReader returns whether the passed io.Reader is a terminal or not
|
||||
func IsTerminalReader(r io.Reader) bool {
|
||||
file, ok := r.(*os.File)
|
||||
return ok && term.IsTerminal(file.Fd())
|
||||
}
|
||||
|
||||
// IsTerminalWriter returns whether the passed io.Writer is a terminal or not
|
||||
func IsTerminalWriter(w io.Writer) bool {
|
||||
file, ok := w.(*os.File)
|
||||
return ok && term.IsTerminal(file.Fd())
|
||||
}
|
||||
+7
@@ -49,6 +49,13 @@ func ScaleFromConfig(dc *DeploymentConfig) *extensions.Scale {
|
||||
}
|
||||
}
|
||||
|
||||
// RequestForConfig builds a new deployment request for a deployment config.
|
||||
func RequestForConfig(dc *DeploymentConfig) *DeploymentRequest {
|
||||
return &DeploymentRequest{
|
||||
Name: dc.Name,
|
||||
}
|
||||
}
|
||||
|
||||
// TemplateImage is a structure for helping a caller iterate over a PodSpec
|
||||
type TemplateImage struct {
|
||||
Image string
|
||||
|
||||
+2
@@ -32,6 +32,7 @@ func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
&DeploymentConfig{},
|
||||
&DeploymentConfigList{},
|
||||
&DeploymentConfigRollback{},
|
||||
&DeploymentRequest{},
|
||||
&DeploymentLog{},
|
||||
&DeploymentLogOptions{},
|
||||
)
|
||||
@@ -41,5 +42,6 @@ func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
func (obj *DeploymentConfig) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
func (obj *DeploymentConfigList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
func (obj *DeploymentConfigRollback) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
func (obj *DeploymentRequest) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
func (obj *DeploymentLog) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
func (obj *DeploymentLogOptions) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
|
||||
+251
-208
@@ -6,181 +6,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/util/intstr"
|
||||
)
|
||||
|
||||
// DeploymentStatus describes the possible states a deployment can be in.
|
||||
type DeploymentStatus string
|
||||
|
||||
const (
|
||||
// DeploymentStatusNew means the deployment has been accepted but not yet acted upon.
|
||||
DeploymentStatusNew DeploymentStatus = "New"
|
||||
// DeploymentStatusPending means the deployment been handed over to a deployment strategy,
|
||||
// but the strategy has not yet declared the deployment to be running.
|
||||
DeploymentStatusPending DeploymentStatus = "Pending"
|
||||
// DeploymentStatusRunning means the deployment strategy has reported the deployment as
|
||||
// being in-progress.
|
||||
DeploymentStatusRunning DeploymentStatus = "Running"
|
||||
// DeploymentStatusComplete means the deployment finished without an error.
|
||||
DeploymentStatusComplete DeploymentStatus = "Complete"
|
||||
// DeploymentStatusFailed means the deployment finished with an error.
|
||||
DeploymentStatusFailed DeploymentStatus = "Failed"
|
||||
)
|
||||
|
||||
// DeploymentStrategy describes how to perform a deployment.
|
||||
type DeploymentStrategy struct {
|
||||
// Type is the name of a deployment strategy.
|
||||
Type DeploymentStrategyType
|
||||
|
||||
// RecreateParams are the input to the Recreate deployment strategy.
|
||||
RecreateParams *RecreateDeploymentStrategyParams
|
||||
// RollingParams are the input to the Rolling deployment strategy.
|
||||
RollingParams *RollingDeploymentStrategyParams
|
||||
|
||||
// CustomParams are the input to the Custom deployment strategy, and may also
|
||||
// be specified for the Recreate and Rolling strategies to customize the execution
|
||||
// process that runs the deployment.
|
||||
CustomParams *CustomDeploymentStrategyParams
|
||||
|
||||
// Resources contains resource requirements to execute the deployment
|
||||
Resources kapi.ResourceRequirements
|
||||
// Labels is a set of key, value pairs added to custom deployer and lifecycle pre/post hook pods.
|
||||
Labels map[string]string
|
||||
// Annotations is a set of key, value pairs added to custom deployer and lifecycle pre/post hook pods.
|
||||
Annotations map[string]string
|
||||
}
|
||||
|
||||
// DeploymentStrategyType refers to a specific DeploymentStrategy implementation.
|
||||
type DeploymentStrategyType string
|
||||
|
||||
const (
|
||||
// DeploymentStrategyTypeRecreate is a simple strategy suitable as a default.
|
||||
DeploymentStrategyTypeRecreate DeploymentStrategyType = "Recreate"
|
||||
// DeploymentStrategyTypeCustom is a user defined strategy. It is optional to set.
|
||||
DeploymentStrategyTypeCustom DeploymentStrategyType = "Custom"
|
||||
// DeploymentStrategyTypeRolling uses the Kubernetes RollingUpdater.
|
||||
DeploymentStrategyTypeRolling DeploymentStrategyType = "Rolling"
|
||||
)
|
||||
|
||||
// CustomDeploymentStrategyParams are the input to the Custom deployment strategy.
|
||||
type CustomDeploymentStrategyParams struct {
|
||||
// Image specifies a Docker image which can carry out a deployment.
|
||||
Image string
|
||||
// Environment holds the environment which will be given to the container for Image.
|
||||
Environment []kapi.EnvVar
|
||||
// Command is optional and overrides CMD in the container Image.
|
||||
Command []string
|
||||
}
|
||||
|
||||
// RecreateDeploymentStrategyParams are the input to the Recreate deployment
|
||||
// strategy.
|
||||
type RecreateDeploymentStrategyParams struct {
|
||||
// TimeoutSeconds is the time to wait for updates before giving up. If the
|
||||
// value is nil, a default will be used.
|
||||
TimeoutSeconds *int64
|
||||
// Pre is a lifecycle hook which is executed before the strategy manipulates
|
||||
// the deployment. All LifecycleHookFailurePolicy values are supported.
|
||||
Pre *LifecycleHook
|
||||
// Mid is a lifecycle hook which is executed while the deployment is scaled down to zero before the first new
|
||||
// pod is created. All LifecycleHookFailurePolicy values are supported.
|
||||
Mid *LifecycleHook
|
||||
// Post is a lifecycle hook which is executed after the strategy has
|
||||
// finished all deployment logic.
|
||||
Post *LifecycleHook
|
||||
}
|
||||
|
||||
// LifecycleHook defines a specific deployment lifecycle action. Only one type of action may be specified at any time.
|
||||
type LifecycleHook struct {
|
||||
// FailurePolicy specifies what action to take if the hook fails.
|
||||
FailurePolicy LifecycleHookFailurePolicy
|
||||
|
||||
// ExecNewPod specifies the options for a lifecycle hook backed by a pod.
|
||||
ExecNewPod *ExecNewPodHook
|
||||
|
||||
// TagImages instructs the deployer to tag the current image referenced under a container onto an image stream tag if the deployment succeeds.
|
||||
TagImages []TagImageHook
|
||||
}
|
||||
|
||||
// LifecycleHookFailurePolicy describes possibles actions to take if a hook fails.
|
||||
type LifecycleHookFailurePolicy string
|
||||
|
||||
const (
|
||||
// LifecycleHookFailurePolicyRetry means retry the hook until it succeeds.
|
||||
LifecycleHookFailurePolicyRetry LifecycleHookFailurePolicy = "Retry"
|
||||
// LifecycleHookFailurePolicyAbort means abort the deployment (if possible).
|
||||
LifecycleHookFailurePolicyAbort LifecycleHookFailurePolicy = "Abort"
|
||||
// LifecycleHookFailurePolicyIgnore means ignore failure and continue the deployment.
|
||||
LifecycleHookFailurePolicyIgnore LifecycleHookFailurePolicy = "Ignore"
|
||||
)
|
||||
|
||||
// ExecNewPodHook is a hook implementation which runs a command in a new pod
|
||||
// based on the specified container which is assumed to be part of the
|
||||
// deployment template.
|
||||
type ExecNewPodHook struct {
|
||||
// Command is the action command and its arguments.
|
||||
Command []string
|
||||
// Env is a set of environment variables to supply to the hook pod's container.
|
||||
Env []kapi.EnvVar
|
||||
// ContainerName is the name of a container in the deployment pod template
|
||||
// whose Docker image will be used for the hook pod's container.
|
||||
ContainerName string
|
||||
// Volumes is a list of named volumes from the pod template which should be
|
||||
// copied to the hook pod.
|
||||
Volumes []string
|
||||
}
|
||||
|
||||
// TagImageHook is a request to tag the image in a particular container onto an ImageStreamTag.
|
||||
type TagImageHook struct {
|
||||
// ContainerName is the name of a container in the deployment config whose image value will be used as the source of the tag
|
||||
ContainerName string
|
||||
// To is the target ImageStreamTag to set the image of
|
||||
To kapi.ObjectReference
|
||||
}
|
||||
|
||||
// RollingDeploymentStrategyParams are the input to the Rolling deployment
|
||||
// strategy.
|
||||
type RollingDeploymentStrategyParams struct {
|
||||
// UpdatePeriodSeconds is the time to wait between individual pod updates.
|
||||
// If the value is nil, a default will be used.
|
||||
UpdatePeriodSeconds *int64
|
||||
// IntervalSeconds is the time to wait between polling deployment status
|
||||
// after update. If the value is nil, a default will be used.
|
||||
IntervalSeconds *int64
|
||||
// TimeoutSeconds is the time to wait for updates before giving up. If the
|
||||
// value is nil, a default will be used.
|
||||
TimeoutSeconds *int64
|
||||
// The maximum number of pods that can be unavailable during the update.
|
||||
// Value can be an absolute number (ex: 5) or a percentage of total pods at the start of update (ex: 10%).
|
||||
// Absolute number is calculated from percentage by rounding up.
|
||||
// This can not be 0 if MaxSurge is 0.
|
||||
// By default, a fixed value of 1 is used.
|
||||
// Example: when this is set to 30%, the old RC can be scaled down by 30%
|
||||
// immediately when the rolling update starts. Once new pods are ready, old RC
|
||||
// can be scaled down further, followed by scaling up the new RC, ensuring
|
||||
// that at least 70% of original number of pods are available at all times
|
||||
// during the update.
|
||||
MaxUnavailable intstr.IntOrString
|
||||
// The maximum number of pods that can be scheduled above the original number of
|
||||
// pods.
|
||||
// Value can be an absolute number (ex: 5) or a percentage of total pods at
|
||||
// the start of the update (ex: 10%). This can not be 0 if MaxUnavailable is 0.
|
||||
// Absolute number is calculated from percentage by rounding up.
|
||||
// By default, a value of 1 is used.
|
||||
// Example: when this is set to 30%, the new RC can be scaled up by 30%
|
||||
// immediately when the rolling update starts. Once old pods have been killed,
|
||||
// new RC can be scaled up further, ensuring that total number of pods running
|
||||
// at any time during the update is atmost 130% of original pods.
|
||||
MaxSurge intstr.IntOrString
|
||||
// UpdatePercent is the percentage of replicas to scale up or down each
|
||||
// interval. If nil, one replica will be scaled up and down each interval.
|
||||
// If negative, the scale order will be down/up instead of up/down.
|
||||
// DEPRECATED: Use MaxUnavailable/MaxSurge instead.
|
||||
UpdatePercent *int32
|
||||
// Pre is a lifecycle hook which is executed before the deployment process
|
||||
// begins. All LifecycleHookFailurePolicy values are supported.
|
||||
Pre *LifecycleHook
|
||||
// Post is a lifecycle hook which is executed after the strategy has
|
||||
// finished all deployment logic.
|
||||
Post *LifecycleHook
|
||||
}
|
||||
|
||||
// These constants represent defaults used in the deployment process.
|
||||
const (
|
||||
// DefaultRollingTimeoutSeconds is the default TimeoutSeconds for RollingDeploymentStrategyParams.
|
||||
DefaultRollingTimeoutSeconds int64 = 10 * 60
|
||||
@@ -188,6 +14,10 @@ const (
|
||||
DefaultRollingIntervalSeconds int64 = 1
|
||||
// DefaultRollingUpdatePeriodSeconds is the default PeriodSeconds for RollingDeploymentStrategyParams.
|
||||
DefaultRollingUpdatePeriodSeconds int64 = 1
|
||||
// MaxDeploymentDurationSeconds represents the maximum duration that a deployment is allowed to run.
|
||||
// This is set as the default value for ActiveDeadlineSeconds for the deployer pod.
|
||||
// Currently set to 6 hours.
|
||||
MaxDeploymentDurationSeconds int64 = 21600
|
||||
)
|
||||
|
||||
// These constants represent keys used for correlating objects related to deployments.
|
||||
@@ -253,6 +83,13 @@ const (
|
||||
PostHookPodSuffix = "hook-post"
|
||||
)
|
||||
|
||||
// These constants represent values used in deployment annotations.
|
||||
const (
|
||||
// DeploymentCancelledAnnotationValue represents the value for the DeploymentCancelledAnnotation
|
||||
// annotation that signifies that the deployment should be cancelled
|
||||
DeploymentCancelledAnnotationValue = "true"
|
||||
)
|
||||
|
||||
// These constants represent the various reasons for cancelling a deployment
|
||||
// or for a deployment being placed in a failed state
|
||||
const (
|
||||
@@ -262,18 +99,23 @@ const (
|
||||
DeploymentFailedDeployerPodNoLongerExists = "deployer pod no longer exists"
|
||||
)
|
||||
|
||||
// MaxDeploymentDurationSeconds represents the maximum duration that a deployment is allowed to run
|
||||
// This is set as the default value for ActiveDeadlineSeconds for the deployer pod
|
||||
// Currently set to 6 hours
|
||||
const MaxDeploymentDurationSeconds int64 = 21600
|
||||
// DeploymentStatus describes the possible states a deployment can be in.
|
||||
type DeploymentStatus string
|
||||
|
||||
// DeploymentCancelledAnnotationValue represents the value for the DeploymentCancelledAnnotation
|
||||
// annotation that signifies that the deployment should be cancelled
|
||||
const DeploymentCancelledAnnotationValue = "true"
|
||||
|
||||
// DeploymentInstantiatedAnnotationValue represents the value for the DeploymentInstantiatedAnnotation
|
||||
// annotation that signifies that the deployment should be instantiated.
|
||||
const DeploymentInstantiatedAnnotationValue = "true"
|
||||
const (
|
||||
// DeploymentStatusNew means the deployment has been accepted but not yet acted upon.
|
||||
DeploymentStatusNew DeploymentStatus = "New"
|
||||
// DeploymentStatusPending means the deployment been handed over to a deployment strategy,
|
||||
// but the strategy has not yet declared the deployment to be running.
|
||||
DeploymentStatusPending DeploymentStatus = "Pending"
|
||||
// DeploymentStatusRunning means the deployment strategy has reported the deployment as
|
||||
// being in-progress.
|
||||
DeploymentStatusRunning DeploymentStatus = "Running"
|
||||
// DeploymentStatusComplete means the deployment finished without an error.
|
||||
DeploymentStatusComplete DeploymentStatus = "Complete"
|
||||
// DeploymentStatusFailed means the deployment finished with an error.
|
||||
DeploymentStatusFailed DeploymentStatus = "Failed"
|
||||
)
|
||||
|
||||
// +genclient=true
|
||||
|
||||
@@ -331,25 +173,162 @@ type DeploymentConfigSpec struct {
|
||||
Template *kapi.PodTemplateSpec
|
||||
}
|
||||
|
||||
// DeploymentConfigStatus represents the current deployment state.
|
||||
type DeploymentConfigStatus struct {
|
||||
// LatestVersion is used to determine whether the current deployment associated with a deployment
|
||||
// config is out of sync.
|
||||
LatestVersion int64
|
||||
// ObservedGeneration is the most recent generation observed by the deployment config controller.
|
||||
ObservedGeneration int64
|
||||
// Replicas is the total number of pods targeted by this deployment config.
|
||||
Replicas int32
|
||||
// UpdatedReplicas is the total number of non-terminated pods targeted by this deployment config
|
||||
// that have the desired template spec.
|
||||
UpdatedReplicas int32
|
||||
// AvailableReplicas is the total number of available pods targeted by this deployment config.
|
||||
AvailableReplicas int32
|
||||
// UnavailableReplicas is the total number of unavailable pods targeted by this deployment config.
|
||||
UnavailableReplicas int32
|
||||
// Details are the reasons for the update to this deployment config.
|
||||
// This could be based on a change made by the user or caused by an automatic trigger
|
||||
Details *DeploymentDetails
|
||||
// DeploymentStrategy describes how to perform a deployment.
|
||||
type DeploymentStrategy struct {
|
||||
// Type is the name of a deployment strategy.
|
||||
Type DeploymentStrategyType
|
||||
|
||||
// CustomParams are the input to the Custom deployment strategy, and may also
|
||||
// be specified for the Recreate and Rolling strategies to customize the execution
|
||||
// process that runs the deployment.
|
||||
CustomParams *CustomDeploymentStrategyParams
|
||||
// RecreateParams are the input to the Recreate deployment strategy.
|
||||
RecreateParams *RecreateDeploymentStrategyParams
|
||||
// RollingParams are the input to the Rolling deployment strategy.
|
||||
RollingParams *RollingDeploymentStrategyParams
|
||||
|
||||
// Resources contains resource requirements to execute the deployment and any hooks.
|
||||
Resources kapi.ResourceRequirements
|
||||
// Labels is a set of key, value pairs added to custom deployer and lifecycle pre/post hook pods.
|
||||
Labels map[string]string
|
||||
// Annotations is a set of key, value pairs added to custom deployer and lifecycle pre/post hook pods.
|
||||
Annotations map[string]string
|
||||
}
|
||||
|
||||
// DeploymentStrategyType refers to a specific DeploymentStrategy implementation.
|
||||
type DeploymentStrategyType string
|
||||
|
||||
const (
|
||||
// DeploymentStrategyTypeRecreate is a simple strategy suitable as a default.
|
||||
DeploymentStrategyTypeRecreate DeploymentStrategyType = "Recreate"
|
||||
// DeploymentStrategyTypeCustom is a user defined strategy.
|
||||
DeploymentStrategyTypeCustom DeploymentStrategyType = "Custom"
|
||||
// DeploymentStrategyTypeRolling uses the Kubernetes RollingUpdater.
|
||||
DeploymentStrategyTypeRolling DeploymentStrategyType = "Rolling"
|
||||
)
|
||||
|
||||
// CustomDeploymentStrategyParams are the input to the Custom deployment strategy.
|
||||
type CustomDeploymentStrategyParams struct {
|
||||
// Image specifies a Docker image which can carry out a deployment.
|
||||
Image string
|
||||
// Environment holds the environment which will be given to the container for Image.
|
||||
Environment []kapi.EnvVar
|
||||
// Command is optional and overrides CMD in the container Image.
|
||||
Command []string
|
||||
}
|
||||
|
||||
// RecreateDeploymentStrategyParams are the input to the Recreate deployment
|
||||
// strategy.
|
||||
type RecreateDeploymentStrategyParams struct {
|
||||
// TimeoutSeconds is the time to wait for updates before giving up. If the
|
||||
// value is nil, a default will be used.
|
||||
TimeoutSeconds *int64
|
||||
// Pre is a lifecycle hook which is executed before the strategy manipulates
|
||||
// the deployment. All LifecycleHookFailurePolicy values are supported.
|
||||
Pre *LifecycleHook
|
||||
// Mid is a lifecycle hook which is executed while the deployment is scaled down to zero before the first new
|
||||
// pod is created. All LifecycleHookFailurePolicy values are supported.
|
||||
Mid *LifecycleHook
|
||||
// Post is a lifecycle hook which is executed after the strategy has
|
||||
// finished all deployment logic. All LifecycleHookFailurePolicy values are supported.
|
||||
Post *LifecycleHook
|
||||
}
|
||||
|
||||
// RollingDeploymentStrategyParams are the input to the Rolling deployment
|
||||
// strategy.
|
||||
type RollingDeploymentStrategyParams struct {
|
||||
// UpdatePeriodSeconds is the time to wait between individual pod updates.
|
||||
// If the value is nil, a default will be used.
|
||||
UpdatePeriodSeconds *int64
|
||||
// IntervalSeconds is the time to wait between polling deployment status
|
||||
// after update. If the value is nil, a default will be used.
|
||||
IntervalSeconds *int64
|
||||
// TimeoutSeconds is the time to wait for updates before giving up. If the
|
||||
// value is nil, a default will be used.
|
||||
TimeoutSeconds *int64
|
||||
// MaxUnavailable is the maximum number of pods that can be unavailable
|
||||
// during the update. Value can be an absolute number (ex: 5) or a
|
||||
// percentage of total pods at the start of update (ex: 10%). Absolute
|
||||
// number is calculated from percentage by rounding up.
|
||||
//
|
||||
// This cannot be 0 if MaxSurge is 0. By default, 25% is used.
|
||||
//
|
||||
// Example: when this is set to 30%, the old RC can be scaled down by 30%
|
||||
// immediately when the rolling update starts. Once new pods are ready, old
|
||||
// RC can be scaled down further, followed by scaling up the new RC,
|
||||
// ensuring that at least 70% of original number of pods are available at
|
||||
// all times during the update.
|
||||
MaxUnavailable intstr.IntOrString
|
||||
// MaxSurge is the maximum number of pods that can be scheduled above the
|
||||
// original number of pods. Value can be an absolute number (ex: 5) or a
|
||||
// percentage of total pods at the start of the update (ex: 10%). Absolute
|
||||
// number is calculated from percentage by rounding up.
|
||||
//
|
||||
// This cannot be 0 if MaxUnavailable is 0. By default, 25% is used.
|
||||
//
|
||||
// Example: when this is set to 30%, the new RC can be scaled up by 30%
|
||||
// immediately when the rolling update starts. Once old pods have been
|
||||
// killed, new RC can be scaled up further, ensuring that total number of
|
||||
// pods running at any time during the update is atmost 130% of original
|
||||
// pods.
|
||||
MaxSurge intstr.IntOrString
|
||||
// Pre is a lifecycle hook which is executed before the deployment process
|
||||
// begins. All LifecycleHookFailurePolicy values are supported.
|
||||
Pre *LifecycleHook
|
||||
// Post is a lifecycle hook which is executed after the strategy has
|
||||
// finished all deployment logic. All LifecycleHookFailurePolicy values
|
||||
// are supported.
|
||||
Post *LifecycleHook
|
||||
}
|
||||
|
||||
// LifecycleHook defines a specific deployment lifecycle action. Only one type of action may be specified at any time.
|
||||
type LifecycleHook struct {
|
||||
// FailurePolicy specifies what action to take if the hook fails.
|
||||
FailurePolicy LifecycleHookFailurePolicy
|
||||
|
||||
// ExecNewPod specifies the options for a lifecycle hook backed by a pod.
|
||||
ExecNewPod *ExecNewPodHook
|
||||
|
||||
// TagImages instructs the deployer to tag the current image referenced under a container onto an image stream tag.
|
||||
TagImages []TagImageHook
|
||||
}
|
||||
|
||||
// LifecycleHookFailurePolicy describes possibles actions to take if a hook fails.
|
||||
type LifecycleHookFailurePolicy string
|
||||
|
||||
const (
|
||||
// LifecycleHookFailurePolicyRetry means retry the hook until it succeeds.
|
||||
LifecycleHookFailurePolicyRetry LifecycleHookFailurePolicy = "Retry"
|
||||
// LifecycleHookFailurePolicyAbort means abort the deployment.
|
||||
LifecycleHookFailurePolicyAbort LifecycleHookFailurePolicy = "Abort"
|
||||
// LifecycleHookFailurePolicyIgnore means ignore failure and continue the deployment.
|
||||
LifecycleHookFailurePolicyIgnore LifecycleHookFailurePolicy = "Ignore"
|
||||
)
|
||||
|
||||
// ExecNewPodHook is a hook implementation which runs a command in a new pod
|
||||
// based on the specified container which is assumed to be part of the
|
||||
// deployment template.
|
||||
type ExecNewPodHook struct {
|
||||
// Command is the action command and its arguments.
|
||||
Command []string
|
||||
// Env is a set of environment variables to supply to the hook pod's container.
|
||||
Env []kapi.EnvVar
|
||||
// ContainerName is the name of a container in the deployment pod template
|
||||
// whose Docker image will be used for the hook pod's container.
|
||||
ContainerName string
|
||||
// Volumes is a list of named volumes from the pod template which should be
|
||||
// copied to the hook pod. Volumes names not found in pod spec are ignored.
|
||||
// An empty list means no volumes will be copied.
|
||||
Volumes []string
|
||||
}
|
||||
|
||||
// TagImageHook is a request to tag the image in a particular container onto an ImageStreamTag.
|
||||
type TagImageHook struct {
|
||||
// ContainerName is the name of a container in the deployment config whose image value will be used as the source of the tag. If there is only a single
|
||||
// container this value will be defaulted to the name of that container.
|
||||
ContainerName string
|
||||
// To is the target ImageStreamTag to set the container's image onto.
|
||||
To kapi.ObjectReference
|
||||
}
|
||||
|
||||
// DeploymentTriggerPolicy describes a policy for a single trigger that results in a new deployment.
|
||||
@@ -377,9 +356,7 @@ const (
|
||||
// DeploymentTriggerImageChangeParams represents the parameters to the ImageChange trigger.
|
||||
type DeploymentTriggerImageChangeParams struct {
|
||||
// Automatic means that the detection of a new tag value should result in an image update
|
||||
// inside the pod template. Deployment configs that haven't been deployed yet will always
|
||||
// have their images updated. Deployment configs that have been deployed at least once, will
|
||||
// have their images updated only if this is set to true.
|
||||
// inside the pod template.
|
||||
Automatic bool
|
||||
// ContainerNames is used to restrict tag updates to the specified set of container names in a pod.
|
||||
ContainerNames []string
|
||||
@@ -391,6 +368,29 @@ type DeploymentTriggerImageChangeParams struct {
|
||||
LastTriggeredImage string
|
||||
}
|
||||
|
||||
// DeploymentConfigStatus represents the current deployment state.
|
||||
type DeploymentConfigStatus struct {
|
||||
// LatestVersion is used to determine whether the current deployment associated with a deployment
|
||||
// config is out of sync.
|
||||
LatestVersion int64
|
||||
// ObservedGeneration is the most recent generation observed by the deployment config controller.
|
||||
ObservedGeneration int64
|
||||
// Replicas is the total number of pods targeted by this deployment config.
|
||||
Replicas int32
|
||||
// UpdatedReplicas is the total number of non-terminated pods targeted by this deployment config
|
||||
// that have the desired template spec.
|
||||
UpdatedReplicas int32
|
||||
// AvailableReplicas is the total number of available pods targeted by this deployment config.
|
||||
AvailableReplicas int32
|
||||
// UnavailableReplicas is the total number of unavailable pods targeted by this deployment config.
|
||||
UnavailableReplicas int32
|
||||
// Details are the reasons for the update to this deployment config.
|
||||
// This could be based on a change made by the user or caused by an automatic trigger
|
||||
Details *DeploymentDetails
|
||||
// Conditions represents the latest available observations of a deployment config's current state.
|
||||
Conditions []DeploymentCondition
|
||||
}
|
||||
|
||||
// DeploymentDetails captures information about the causes of a deployment.
|
||||
type DeploymentDetails struct {
|
||||
// Message is the user specified change message, if this deployment was triggered manually by the user
|
||||
@@ -414,6 +414,37 @@ type DeploymentCauseImageTrigger struct {
|
||||
From kapi.ObjectReference
|
||||
}
|
||||
|
||||
type DeploymentConditionType string
|
||||
|
||||
// These are valid conditions of a deployment config.
|
||||
const (
|
||||
// DeploymentAvailable means the deployment config is available, ie. at least the minimum available
|
||||
// replicas required are up and running for at least minReadySeconds.
|
||||
DeploymentAvailable DeploymentConditionType = "Available"
|
||||
// DeploymentProgressing means the deployment config is progressing. Progress for a deployment
|
||||
// config is considered when a new replica set is created or adopted, and when new pods scale up or
|
||||
// old pods scale down. Progress is not estimated for paused deployment configs, when the deployment
|
||||
// config needs to rollback, or when progressDeadlineSeconds is not specified.
|
||||
DeploymentProgressing DeploymentConditionType = "Progressing"
|
||||
// DeploymentReplicaFailure is added in a deployment config when one of its pods
|
||||
// fails to be created or deleted.
|
||||
DeploymentReplicaFailure DeploymentConditionType = "ReplicaFailure"
|
||||
)
|
||||
|
||||
// DeploymentCondition describes the state of a deployment config at a certain point.
|
||||
type DeploymentCondition struct {
|
||||
// Type of deployment condition.
|
||||
Type DeploymentConditionType
|
||||
// Status of the condition, one of True, False, Unknown.
|
||||
Status kapi.ConditionStatus
|
||||
// The last time the condition transitioned from one status to another.
|
||||
LastTransitionTime unversioned.Time
|
||||
// The reason for the condition's last transition.
|
||||
Reason string
|
||||
// A human readable message indicating details about the transition.
|
||||
Message string
|
||||
}
|
||||
|
||||
// DeploymentConfigList is a collection of deployment configs.
|
||||
type DeploymentConfigList struct {
|
||||
unversioned.TypeMeta
|
||||
@@ -450,6 +481,18 @@ type DeploymentConfigRollbackSpec struct {
|
||||
IncludeStrategy bool
|
||||
}
|
||||
|
||||
// DeploymentRequest is a request to a deployment config for a new deployment.
|
||||
type DeploymentRequest struct {
|
||||
unversioned.TypeMeta
|
||||
// Name of the deployment config for requesting a new deployment.
|
||||
Name string
|
||||
// Latest will update the deployment config with the latest state from all triggers.
|
||||
Latest bool
|
||||
// Force will try to force a new deployment to run. If the deployment config is paused,
|
||||
// then setting this to true will return an Invalid error.
|
||||
Force bool
|
||||
}
|
||||
|
||||
// DeploymentLog represents the logs for a deployment
|
||||
type DeploymentLog struct {
|
||||
unversioned.TypeMeta
|
||||
|
||||
+8
-27
@@ -1,8 +1,6 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
@@ -60,7 +58,6 @@ func Convert_v1_RollingDeploymentStrategyParams_To_api_RollingDeploymentStrategy
|
||||
out.UpdatePeriodSeconds = in.UpdatePeriodSeconds
|
||||
out.IntervalSeconds = in.IntervalSeconds
|
||||
out.TimeoutSeconds = in.TimeoutSeconds
|
||||
out.UpdatePercent = in.UpdatePercent
|
||||
|
||||
if in.Pre != nil {
|
||||
if err := s.Convert(&in.Pre, &out.Pre, 0); err != nil {
|
||||
@@ -72,18 +69,12 @@ func Convert_v1_RollingDeploymentStrategyParams_To_api_RollingDeploymentStrategy
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if in.UpdatePercent != nil {
|
||||
pct := intstr.FromString(fmt.Sprintf("%d%%", int(math.Abs(float64(*in.UpdatePercent)))))
|
||||
if *in.UpdatePercent > 0 {
|
||||
out.MaxSurge = pct
|
||||
} else {
|
||||
out.MaxUnavailable = pct
|
||||
}
|
||||
} else {
|
||||
if in.MaxUnavailable != nil {
|
||||
if err := s.Convert(in.MaxUnavailable, &out.MaxUnavailable, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if in.MaxSurge != nil {
|
||||
if err := s.Convert(in.MaxSurge, &out.MaxSurge, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -95,7 +86,6 @@ func Convert_api_RollingDeploymentStrategyParams_To_v1_RollingDeploymentStrategy
|
||||
out.UpdatePeriodSeconds = in.UpdatePeriodSeconds
|
||||
out.IntervalSeconds = in.IntervalSeconds
|
||||
out.TimeoutSeconds = in.TimeoutSeconds
|
||||
out.UpdatePercent = in.UpdatePercent
|
||||
|
||||
if in.Pre != nil {
|
||||
if err := s.Convert(&in.Pre, &out.Pre, 0); err != nil {
|
||||
@@ -114,20 +104,11 @@ func Convert_api_RollingDeploymentStrategyParams_To_v1_RollingDeploymentStrategy
|
||||
if out.MaxSurge == nil {
|
||||
out.MaxSurge = &intstr.IntOrString{}
|
||||
}
|
||||
if in.UpdatePercent != nil {
|
||||
pct := intstr.FromString(fmt.Sprintf("%d%%", int(math.Abs(float64(*in.UpdatePercent)))))
|
||||
if *in.UpdatePercent > 0 {
|
||||
out.MaxSurge = &pct
|
||||
} else {
|
||||
out.MaxUnavailable = &pct
|
||||
}
|
||||
} else {
|
||||
if err := s.Convert(&in.MaxUnavailable, out.MaxUnavailable, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.Convert(&in.MaxSurge, out.MaxSurge, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.Convert(&in.MaxUnavailable, out.MaxUnavailable, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.Convert(&in.MaxSurge, out.MaxSurge, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+19
-10
@@ -71,6 +71,7 @@ func SetDefaults_RecreateDeploymentStrategyParams(obj *RecreateDeploymentStrateg
|
||||
obj.TimeoutSeconds = mkintp(deployapi.DefaultRollingTimeoutSeconds)
|
||||
}
|
||||
}
|
||||
|
||||
func SetDefaults_RollingDeploymentStrategyParams(obj *RollingDeploymentStrategyParams) {
|
||||
if obj.IntervalSeconds == nil {
|
||||
obj.IntervalSeconds = mkintp(deployapi.DefaultRollingIntervalSeconds)
|
||||
@@ -84,16 +85,24 @@ func SetDefaults_RollingDeploymentStrategyParams(obj *RollingDeploymentStrategyP
|
||||
obj.TimeoutSeconds = mkintp(deployapi.DefaultRollingTimeoutSeconds)
|
||||
}
|
||||
|
||||
if obj.UpdatePercent == nil {
|
||||
// Apply defaults.
|
||||
if obj.MaxUnavailable == nil {
|
||||
maxUnavailable := intstr.FromString("25%")
|
||||
obj.MaxUnavailable = &maxUnavailable
|
||||
}
|
||||
if obj.MaxSurge == nil {
|
||||
maxSurge := intstr.FromString("25%")
|
||||
obj.MaxSurge = &maxSurge
|
||||
}
|
||||
if obj.MaxUnavailable == nil && obj.MaxSurge == nil {
|
||||
maxUnavailable := intstr.FromString("25%")
|
||||
obj.MaxUnavailable = &maxUnavailable
|
||||
|
||||
maxSurge := intstr.FromString("25%")
|
||||
obj.MaxSurge = &maxSurge
|
||||
}
|
||||
|
||||
if obj.MaxUnavailable == nil && obj.MaxSurge != nil &&
|
||||
(*obj.MaxSurge == intstr.FromInt(0) || *obj.MaxSurge == intstr.FromString("0%")) {
|
||||
maxUnavailable := intstr.FromString("25%")
|
||||
obj.MaxUnavailable = &maxUnavailable
|
||||
}
|
||||
|
||||
if obj.MaxSurge == nil && obj.MaxUnavailable != nil &&
|
||||
(*obj.MaxUnavailable == intstr.FromInt(0) || *obj.MaxUnavailable == intstr.FromString("0%")) {
|
||||
maxSurge := intstr.FromString("25%")
|
||||
obj.MaxSurge = &maxSurge
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+767
-279
File diff suppressed because it is too large
Load Diff
-410
@@ -1,410 +0,0 @@
|
||||
|
||||
// This file was autogenerated by go-to-protobuf. Do not edit it manually!
|
||||
|
||||
syntax = 'proto2';
|
||||
|
||||
package github.com.openshift.origin.pkg.deploy.api.v1;
|
||||
|
||||
import "k8s.io/kubernetes/pkg/api/unversioned/generated.proto";
|
||||
import "k8s.io/kubernetes/pkg/api/v1/generated.proto";
|
||||
import "k8s.io/kubernetes/pkg/apis/extensions/v1beta1/generated.proto";
|
||||
import "k8s.io/kubernetes/pkg/runtime/generated.proto";
|
||||
import "k8s.io/kubernetes/pkg/util/intstr/generated.proto";
|
||||
|
||||
// Package-wide variables from generator "generated".
|
||||
option go_package = "v1";
|
||||
|
||||
// CustomDeploymentStrategyParams are the input to the Custom deployment strategy.
|
||||
message CustomDeploymentStrategyParams {
|
||||
// Image specifies a Docker image which can carry out a deployment.
|
||||
optional string image = 1;
|
||||
|
||||
// Environment holds the environment which will be given to the container for Image.
|
||||
repeated k8s.io.kubernetes.pkg.api.v1.EnvVar environment = 2;
|
||||
|
||||
// Command is optional and overrides CMD in the container Image.
|
||||
repeated string command = 3;
|
||||
}
|
||||
|
||||
// DeploymentCause captures information about a particular cause of a deployment.
|
||||
message DeploymentCause {
|
||||
// Type of the trigger that resulted in the creation of a new deployment
|
||||
optional string type = 1;
|
||||
|
||||
// ImageTrigger contains the image trigger details, if this trigger was fired based on an image change
|
||||
optional DeploymentCauseImageTrigger imageTrigger = 2;
|
||||
}
|
||||
|
||||
// DeploymentCauseImageTrigger represents details about the cause of a deployment originating
|
||||
// from an image change trigger
|
||||
message DeploymentCauseImageTrigger {
|
||||
// From is a reference to the changed object which triggered a deployment. The field may have
|
||||
// the kinds DockerImage, ImageStreamTag, or ImageStreamImage.
|
||||
optional k8s.io.kubernetes.pkg.api.v1.ObjectReference from = 1;
|
||||
}
|
||||
|
||||
// DeploymentConfig represents a configuration for a single deployment (represented as a
|
||||
// ReplicationController). It also contains details about changes which resulted in the current
|
||||
// state of the DeploymentConfig. Each change to the DeploymentConfig which should result in
|
||||
// a new deployment results in an increment of LatestVersion.
|
||||
message DeploymentConfig {
|
||||
// Standard object's metadata.
|
||||
optional k8s.io.kubernetes.pkg.api.v1.ObjectMeta metadata = 1;
|
||||
|
||||
// Spec represents a desired deployment state and how to deploy to it.
|
||||
optional DeploymentConfigSpec spec = 2;
|
||||
|
||||
// Status represents the current deployment state.
|
||||
optional DeploymentConfigStatus status = 3;
|
||||
}
|
||||
|
||||
// DeploymentConfigList is a collection of deployment configs.
|
||||
message DeploymentConfigList {
|
||||
// Standard object's metadata.
|
||||
optional k8s.io.kubernetes.pkg.api.unversioned.ListMeta metadata = 1;
|
||||
|
||||
// Items is a list of deployment configs
|
||||
repeated DeploymentConfig items = 2;
|
||||
}
|
||||
|
||||
// DeploymentConfigRollback provides the input to rollback generation.
|
||||
message DeploymentConfigRollback {
|
||||
// Name of the deployment config that will be rolled back.
|
||||
optional string name = 1;
|
||||
|
||||
// UpdatedAnnotations is a set of new annotations that will be added in the deployment config.
|
||||
map<string, string> updatedAnnotations = 2;
|
||||
|
||||
// Spec defines the options to rollback generation.
|
||||
optional DeploymentConfigRollbackSpec spec = 3;
|
||||
}
|
||||
|
||||
// DeploymentConfigRollbackSpec represents the options for rollback generation.
|
||||
message DeploymentConfigRollbackSpec {
|
||||
// From points to a ReplicationController which is a deployment.
|
||||
optional k8s.io.kubernetes.pkg.api.v1.ObjectReference from = 1;
|
||||
|
||||
// Revision to rollback to. If set to 0, rollback to the last revision.
|
||||
optional int64 revision = 2;
|
||||
|
||||
// IncludeTriggers specifies whether to include config Triggers.
|
||||
optional bool includeTriggers = 3;
|
||||
|
||||
// IncludeTemplate specifies whether to include the PodTemplateSpec.
|
||||
optional bool includeTemplate = 4;
|
||||
|
||||
// IncludeReplicationMeta specifies whether to include the replica count and selector.
|
||||
optional bool includeReplicationMeta = 5;
|
||||
|
||||
// IncludeStrategy specifies whether to include the deployment Strategy.
|
||||
optional bool includeStrategy = 6;
|
||||
}
|
||||
|
||||
// DeploymentConfigSpec represents the desired state of the deployment.
|
||||
message DeploymentConfigSpec {
|
||||
// Strategy describes how a deployment is executed.
|
||||
optional DeploymentStrategy strategy = 1;
|
||||
|
||||
// MinReadySeconds is the minimum number of seconds for which a newly created pod should
|
||||
// be ready without any of its container crashing, for it to be considered available.
|
||||
// Defaults to 0 (pod will be considered available as soon as it is ready)
|
||||
optional int32 minReadySeconds = 9;
|
||||
|
||||
// Triggers determine how updates to a DeploymentConfig result in new deployments. If no triggers
|
||||
// are defined, a new deployment can only occur as a result of an explicit client update to the
|
||||
// DeploymentConfig with a new LatestVersion. If null, defaults to having a config change trigger.
|
||||
optional DeploymentTriggerPolicies triggers = 2;
|
||||
|
||||
// Replicas is the number of desired replicas.
|
||||
optional int32 replicas = 3;
|
||||
|
||||
// RevisionHistoryLimit is the number of old ReplicationControllers to retain to allow for rollbacks.
|
||||
// This field is a pointer to allow for differentiation between an explicit zero and not specified.
|
||||
optional int32 revisionHistoryLimit = 4;
|
||||
|
||||
// Test ensures that this deployment config will have zero replicas except while a deployment is running. This allows the
|
||||
// deployment config to be used as a continuous deployment test - triggering on images, running the deployment, and then succeeding
|
||||
// or failing. Post strategy hooks and After actions can be used to integrate successful deployment with an action.
|
||||
optional bool test = 5;
|
||||
|
||||
// Paused indicates that the deployment config is paused resulting in no new deployments on template
|
||||
// changes or changes in the template caused by other triggers.
|
||||
optional bool paused = 6;
|
||||
|
||||
// Selector is a label query over pods that should match the Replicas count.
|
||||
map<string, string> selector = 7;
|
||||
|
||||
// Template is the object that describes the pod that will be created if
|
||||
// insufficient replicas are detected.
|
||||
optional k8s.io.kubernetes.pkg.api.v1.PodTemplateSpec template = 8;
|
||||
}
|
||||
|
||||
// DeploymentConfigStatus represents the current deployment state.
|
||||
message DeploymentConfigStatus {
|
||||
// LatestVersion is used to determine whether the current deployment associated with a deployment
|
||||
// config is out of sync.
|
||||
optional int64 latestVersion = 1;
|
||||
|
||||
// ObservedGeneration is the most recent generation observed by the deployment config controller.
|
||||
optional int64 observedGeneration = 2;
|
||||
|
||||
// Replicas is the total number of pods targeted by this deployment config.
|
||||
optional int32 replicas = 3;
|
||||
|
||||
// UpdatedReplicas is the total number of non-terminated pods targeted by this deployment config
|
||||
// that have the desired template spec.
|
||||
optional int32 updatedReplicas = 4;
|
||||
|
||||
// AvailableReplicas is the total number of available pods targeted by this deployment config.
|
||||
optional int32 availableReplicas = 5;
|
||||
|
||||
// UnavailableReplicas is the total number of unavailable pods targeted by this deployment config.
|
||||
optional int32 unavailableReplicas = 6;
|
||||
|
||||
// Details are the reasons for the update to this deployment config.
|
||||
// This could be based on a change made by the user or caused by an automatic trigger
|
||||
optional DeploymentDetails details = 7;
|
||||
}
|
||||
|
||||
// DeploymentDetails captures information about the causes of a deployment.
|
||||
message DeploymentDetails {
|
||||
// Message is the user specified change message, if this deployment was triggered manually by the user
|
||||
optional string message = 1;
|
||||
|
||||
// Causes are extended data associated with all the causes for creating a new deployment
|
||||
repeated DeploymentCause causes = 2;
|
||||
}
|
||||
|
||||
// DeploymentLog represents the logs for a deployment
|
||||
message DeploymentLog {
|
||||
}
|
||||
|
||||
// DeploymentLogOptions is the REST options for a deployment log
|
||||
message DeploymentLogOptions {
|
||||
// The container for which to stream logs. Defaults to only container if there is one container in the pod.
|
||||
optional string container = 1;
|
||||
|
||||
// Follow if true indicates that the build log should be streamed until
|
||||
// the build terminates.
|
||||
optional bool follow = 2;
|
||||
|
||||
// Return previous deployment logs. Defaults to false.
|
||||
optional bool previous = 3;
|
||||
|
||||
// A relative time in seconds before the current time from which to show logs. If this value
|
||||
// precedes the time a pod was started, only logs since the pod start will be returned.
|
||||
// If this value is in the future, no logs will be returned.
|
||||
// Only one of sinceSeconds or sinceTime may be specified.
|
||||
optional int64 sinceSeconds = 4;
|
||||
|
||||
// An RFC3339 timestamp from which to show logs. If this value
|
||||
// precedes the time a pod was started, only logs since the pod start will be returned.
|
||||
// If this value is in the future, no logs will be returned.
|
||||
// Only one of sinceSeconds or sinceTime may be specified.
|
||||
optional k8s.io.kubernetes.pkg.api.unversioned.Time sinceTime = 5;
|
||||
|
||||
// If true, add an RFC3339 or RFC3339Nano timestamp at the beginning of every line
|
||||
// of log output. Defaults to false.
|
||||
optional bool timestamps = 6;
|
||||
|
||||
// If set, the number of lines from the end of the logs to show. If not specified,
|
||||
// logs are shown from the creation of the container or sinceSeconds or sinceTime
|
||||
optional int64 tailLines = 7;
|
||||
|
||||
// If set, the number of bytes to read from the server before terminating the
|
||||
// log output. This may not display a complete final line of logging, and may return
|
||||
// slightly more or slightly less than the specified limit.
|
||||
optional int64 limitBytes = 8;
|
||||
|
||||
// NoWait if true causes the call to return immediately even if the deployment
|
||||
// is not available yet. Otherwise the server will wait until the deployment has started.
|
||||
// TODO: Fix the tag to 'noWait' in v2
|
||||
optional bool nowait = 9;
|
||||
|
||||
// Version of the deployment for which to view logs.
|
||||
optional int64 version = 10;
|
||||
}
|
||||
|
||||
// DeploymentStrategy describes how to perform a deployment.
|
||||
message DeploymentStrategy {
|
||||
// Type is the name of a deployment strategy.
|
||||
optional string type = 1;
|
||||
|
||||
// CustomParams are the input to the Custom deployment strategy.
|
||||
optional CustomDeploymentStrategyParams customParams = 2;
|
||||
|
||||
// RecreateParams are the input to the Recreate deployment strategy.
|
||||
optional RecreateDeploymentStrategyParams recreateParams = 3;
|
||||
|
||||
// RollingParams are the input to the Rolling deployment strategy.
|
||||
optional RollingDeploymentStrategyParams rollingParams = 4;
|
||||
|
||||
// Resources contains resource requirements to execute the deployment and any hooks
|
||||
optional k8s.io.kubernetes.pkg.api.v1.ResourceRequirements resources = 5;
|
||||
|
||||
// Labels is a set of key, value pairs added to custom deployer and lifecycle pre/post hook pods.
|
||||
map<string, string> labels = 6;
|
||||
|
||||
// Annotations is a set of key, value pairs added to custom deployer and lifecycle pre/post hook pods.
|
||||
map<string, string> annotations = 7;
|
||||
}
|
||||
|
||||
// DeploymentTriggerImageChangeParams represents the parameters to the ImageChange trigger.
|
||||
message DeploymentTriggerImageChangeParams {
|
||||
// Automatic means that the detection of a new tag value should result in an image update
|
||||
// inside the pod template. Deployment configs that haven't been deployed yet will always
|
||||
// have their images updated. Deployment configs that have been deployed at least once, will
|
||||
// have their images updated only if this is set to true.
|
||||
optional bool automatic = 1;
|
||||
|
||||
// ContainerNames is used to restrict tag updates to the specified set of container names in a pod.
|
||||
repeated string containerNames = 2;
|
||||
|
||||
// From is a reference to an image stream tag to watch for changes. From.Name is the only
|
||||
// required subfield - if From.Namespace is blank, the namespace of the current deployment
|
||||
// trigger will be used.
|
||||
optional k8s.io.kubernetes.pkg.api.v1.ObjectReference from = 3;
|
||||
|
||||
// LastTriggeredImage is the last image to be triggered.
|
||||
optional string lastTriggeredImage = 4;
|
||||
}
|
||||
|
||||
// DeploymentTriggerPolicies is a list of policies where nil values and different from empty arrays.
|
||||
// +protobuf.nullable=true
|
||||
// +protobuf.options.(gogoproto.goproto_stringer)=false
|
||||
message DeploymentTriggerPolicies {
|
||||
// items, if empty, will result in an empty slice
|
||||
|
||||
repeated DeploymentTriggerPolicy items = 1;
|
||||
}
|
||||
|
||||
// DeploymentTriggerPolicy describes a policy for a single trigger that results in a new deployment.
|
||||
message DeploymentTriggerPolicy {
|
||||
// Type of the trigger
|
||||
optional string type = 1;
|
||||
|
||||
// ImageChangeParams represents the parameters for the ImageChange trigger.
|
||||
optional DeploymentTriggerImageChangeParams imageChangeParams = 2;
|
||||
}
|
||||
|
||||
// ExecNewPodHook is a hook implementation which runs a command in a new pod
|
||||
// based on the specified container which is assumed to be part of the
|
||||
// deployment template.
|
||||
message ExecNewPodHook {
|
||||
// Command is the action command and its arguments.
|
||||
repeated string command = 1;
|
||||
|
||||
// Env is a set of environment variables to supply to the hook pod's container.
|
||||
repeated k8s.io.kubernetes.pkg.api.v1.EnvVar env = 2;
|
||||
|
||||
// ContainerName is the name of a container in the deployment pod template
|
||||
// whose Docker image will be used for the hook pod's container.
|
||||
optional string containerName = 3;
|
||||
|
||||
// Volumes is a list of named volumes from the pod template which should be
|
||||
// copied to the hook pod. Volumes names not found in pod spec are ignored.
|
||||
// An empty list means no volumes will be copied.
|
||||
repeated string volumes = 4;
|
||||
}
|
||||
|
||||
// LifecycleHook defines a specific deployment lifecycle action. Only one type of action may be specified at any time.
|
||||
message LifecycleHook {
|
||||
// FailurePolicy specifies what action to take if the hook fails.
|
||||
optional string failurePolicy = 1;
|
||||
|
||||
// ExecNewPod specifies the options for a lifecycle hook backed by a pod.
|
||||
optional ExecNewPodHook execNewPod = 2;
|
||||
|
||||
// TagImages instructs the deployer to tag the current image referenced under a container onto an image stream tag.
|
||||
repeated TagImageHook tagImages = 3;
|
||||
}
|
||||
|
||||
// RecreateDeploymentStrategyParams are the input to the Recreate deployment
|
||||
// strategy.
|
||||
message RecreateDeploymentStrategyParams {
|
||||
// TimeoutSeconds is the time to wait for updates before giving up. If the
|
||||
// value is nil, a default will be used.
|
||||
optional int64 timeoutSeconds = 1;
|
||||
|
||||
// Pre is a lifecycle hook which is executed before the strategy manipulates
|
||||
// the deployment. All LifecycleHookFailurePolicy values are supported.
|
||||
optional LifecycleHook pre = 2;
|
||||
|
||||
// Mid is a lifecycle hook which is executed while the deployment is scaled down to zero before the first new
|
||||
// pod is created. All LifecycleHookFailurePolicy values are supported.
|
||||
optional LifecycleHook mid = 3;
|
||||
|
||||
// Post is a lifecycle hook which is executed after the strategy has
|
||||
// finished all deployment logic. All LifecycleHookFailurePolicy values are supported.
|
||||
optional LifecycleHook post = 4;
|
||||
}
|
||||
|
||||
// RollingDeploymentStrategyParams are the input to the Rolling deployment
|
||||
// strategy.
|
||||
message RollingDeploymentStrategyParams {
|
||||
// UpdatePeriodSeconds is the time to wait between individual pod updates.
|
||||
// If the value is nil, a default will be used.
|
||||
optional int64 updatePeriodSeconds = 1;
|
||||
|
||||
// IntervalSeconds is the time to wait between polling deployment status
|
||||
// after update. If the value is nil, a default will be used.
|
||||
optional int64 intervalSeconds = 2;
|
||||
|
||||
// TimeoutSeconds is the time to wait for updates before giving up. If the
|
||||
// value is nil, a default will be used.
|
||||
optional int64 timeoutSeconds = 3;
|
||||
|
||||
// MaxUnavailable is the maximum number of pods that can be unavailable
|
||||
// during the update. Value can be an absolute number (ex: 5) or a
|
||||
// percentage of total pods at the start of update (ex: 10%). Absolute
|
||||
// number is calculated from percentage by rounding up.
|
||||
//
|
||||
// This cannot be 0 if MaxSurge is 0. By default, 25% is used.
|
||||
//
|
||||
// Example: when this is set to 30%, the old RC can be scaled down by 30%
|
||||
// immediately when the rolling update starts. Once new pods are ready, old
|
||||
// RC can be scaled down further, followed by scaling up the new RC,
|
||||
// ensuring that at least 70% of original number of pods are available at
|
||||
// all times during the update.
|
||||
optional k8s.io.kubernetes.pkg.util.intstr.IntOrString maxUnavailable = 4;
|
||||
|
||||
// MaxSurge is the maximum number of pods that can be scheduled above the
|
||||
// original number of pods. Value can be an absolute number (ex: 5) or a
|
||||
// percentage of total pods at the start of the update (ex: 10%). Absolute
|
||||
// number is calculated from percentage by rounding up.
|
||||
//
|
||||
// This cannot be 0 if MaxUnavailable is 0. By default, 25% is used.
|
||||
//
|
||||
// Example: when this is set to 30%, the new RC can be scaled up by 30%
|
||||
// immediately when the rolling update starts. Once old pods have been
|
||||
// killed, new RC can be scaled up further, ensuring that total number of
|
||||
// pods running at any time during the update is atmost 130% of original
|
||||
// pods.
|
||||
optional k8s.io.kubernetes.pkg.util.intstr.IntOrString maxSurge = 5;
|
||||
|
||||
// UpdatePercent is the percentage of replicas to scale up or down each
|
||||
// interval. If nil, one replica will be scaled up and down each interval.
|
||||
// If negative, the scale order will be down/up instead of up/down.
|
||||
// DEPRECATED: Use MaxUnavailable/MaxSurge instead.
|
||||
optional int32 updatePercent = 6;
|
||||
|
||||
// Pre is a lifecycle hook which is executed before the deployment process
|
||||
// begins. All LifecycleHookFailurePolicy values are supported.
|
||||
optional LifecycleHook pre = 7;
|
||||
|
||||
// Post is a lifecycle hook which is executed after the strategy has
|
||||
// finished all deployment logic. The LifecycleHookFailurePolicyAbort policy
|
||||
// is NOT supported.
|
||||
optional LifecycleHook post = 8;
|
||||
}
|
||||
|
||||
// TagImageHook is a request to tag the image in a particular container onto an ImageStreamTag.
|
||||
message TagImageHook {
|
||||
// ContainerName is the name of a container in the deployment config whose image value will be used as the source of the tag. If there is only a single
|
||||
// container this value will be defaulted to the name of that container.
|
||||
optional string containerName = 1;
|
||||
|
||||
// To is the target ImageStreamTag to set the container's image onto.
|
||||
optional k8s.io.kubernetes.pkg.api.v1.ObjectReference to = 2;
|
||||
}
|
||||
|
||||
+2
@@ -21,6 +21,7 @@ func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
&DeploymentConfig{},
|
||||
&DeploymentConfigList{},
|
||||
&DeploymentConfigRollback{},
|
||||
&DeploymentRequest{},
|
||||
&DeploymentLog{},
|
||||
&DeploymentLogOptions{},
|
||||
)
|
||||
@@ -30,5 +31,6 @@ func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
func (obj *DeploymentConfig) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
func (obj *DeploymentConfigList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
func (obj *DeploymentConfigRollback) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
func (obj *DeploymentRequest) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
func (obj *DeploymentLog) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
func (obj *DeploymentLogOptions) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
|
||||
+30
-6
@@ -35,8 +35,21 @@ func (DeploymentCauseImageTrigger) SwaggerDoc() map[string]string {
|
||||
return map_DeploymentCauseImageTrigger
|
||||
}
|
||||
|
||||
var map_DeploymentCondition = map[string]string{
|
||||
"": "DeploymentCondition describes the state of a deployment config at a certain point.",
|
||||
"type": "Type of deployment condition.",
|
||||
"status": "Status of the condition, one of True, False, Unknown.",
|
||||
"lastTransitionTime": "The last time the condition transitioned from one status to another.",
|
||||
"reason": "The reason for the condition's last transition.",
|
||||
"message": "A human readable message indicating details about the transition.",
|
||||
}
|
||||
|
||||
func (DeploymentCondition) SwaggerDoc() map[string]string {
|
||||
return map_DeploymentCondition
|
||||
}
|
||||
|
||||
var map_DeploymentConfig = map[string]string{
|
||||
"": "DeploymentConfig represents a configuration for a single deployment (represented as a ReplicationController). It also contains details about changes which resulted in the current state of the DeploymentConfig. Each change to the DeploymentConfig which should result in a new deployment results in an increment of LatestVersion.",
|
||||
"": "Deployment Configs define the template for a pod and manages deploying new images or configuration changes. A single deployment configuration is usually analogous to a single micro-service. Can support many different deployment patterns, including full restart, customizable rolling updates, and fully custom behaviors, as well as pre- and post- deployment hooks. Each individual deployment is represented as a replication controller.\n\nA deployment is \"triggered\" when its configuration is changed or a tag in an Image Stream is changed. Triggers can be disabled to allow manual control over a deployment. The \"strategy\" determines how the deployment is carried out and may be changed at any time. The `latestVersion` field is updated when a new deployment is triggered by any means.",
|
||||
"metadata": "Standard object's metadata.",
|
||||
"spec": "Spec represents a desired deployment state and how to deploy to it.",
|
||||
"status": "Status represents the current deployment state.",
|
||||
@@ -107,6 +120,7 @@ var map_DeploymentConfigStatus = map[string]string{
|
||||
"availableReplicas": "AvailableReplicas is the total number of available pods targeted by this deployment config.",
|
||||
"unavailableReplicas": "UnavailableReplicas is the total number of unavailable pods targeted by this deployment config.",
|
||||
"details": "Details are the reasons for the update to this deployment config. This could be based on a change made by the user or caused by an automatic trigger",
|
||||
"conditions": "Conditions represents the latest available observations of a deployment config's current state.",
|
||||
}
|
||||
|
||||
func (DeploymentConfigStatus) SwaggerDoc() map[string]string {
|
||||
@@ -149,13 +163,24 @@ func (DeploymentLogOptions) SwaggerDoc() map[string]string {
|
||||
return map_DeploymentLogOptions
|
||||
}
|
||||
|
||||
var map_DeploymentRequest = map[string]string{
|
||||
"": "DeploymentRequest is a request to a deployment config for a new deployment.",
|
||||
"name": "Name of the deployment config for requesting a new deployment.",
|
||||
"latest": "Latest will update the deployment config with the latest state from all triggers.",
|
||||
"force": "Force will try to force a new deployment to run. If the deployment config is paused, then setting this to true will return an Invalid error.",
|
||||
}
|
||||
|
||||
func (DeploymentRequest) SwaggerDoc() map[string]string {
|
||||
return map_DeploymentRequest
|
||||
}
|
||||
|
||||
var map_DeploymentStrategy = map[string]string{
|
||||
"": "DeploymentStrategy describes how to perform a deployment.",
|
||||
"type": "Type is the name of a deployment strategy.",
|
||||
"customParams": "CustomParams are the input to the Custom deployment strategy.",
|
||||
"customParams": "CustomParams are the input to the Custom deployment strategy, and may also be specified for the Recreate and Rolling strategies to customize the execution process that runs the deployment.",
|
||||
"recreateParams": "RecreateParams are the input to the Recreate deployment strategy.",
|
||||
"rollingParams": "RollingParams are the input to the Rolling deployment strategy.",
|
||||
"resources": "Resources contains resource requirements to execute the deployment and any hooks",
|
||||
"resources": "Resources contains resource requirements to execute the deployment and any hooks.",
|
||||
"labels": "Labels is a set of key, value pairs added to custom deployer and lifecycle pre/post hook pods.",
|
||||
"annotations": "Annotations is a set of key, value pairs added to custom deployer and lifecycle pre/post hook pods.",
|
||||
}
|
||||
@@ -166,7 +191,7 @@ func (DeploymentStrategy) SwaggerDoc() map[string]string {
|
||||
|
||||
var map_DeploymentTriggerImageChangeParams = map[string]string{
|
||||
"": "DeploymentTriggerImageChangeParams represents the parameters to the ImageChange trigger.",
|
||||
"automatic": "Automatic means that the detection of a new tag value should result in an image update inside the pod template. Deployment configs that haven't been deployed yet will always have their images updated. Deployment configs that have been deployed at least once, will have their images updated only if this is set to true.",
|
||||
"automatic": "Automatic means that the detection of a new tag value should result in an image update inside the pod template.",
|
||||
"containerNames": "ContainerNames is used to restrict tag updates to the specified set of container names in a pod.",
|
||||
"from": "From is a reference to an image stream tag to watch for changes. From.Name is the only required subfield - if From.Namespace is blank, the namespace of the current deployment trigger will be used.",
|
||||
"lastTriggeredImage": "LastTriggeredImage is the last image to be triggered.",
|
||||
@@ -228,9 +253,8 @@ var map_RollingDeploymentStrategyParams = map[string]string{
|
||||
"timeoutSeconds": "TimeoutSeconds is the time to wait for updates before giving up. If the value is nil, a default will be used.",
|
||||
"maxUnavailable": "MaxUnavailable is the maximum number of pods that can be unavailable during the update. Value can be an absolute number (ex: 5) or a percentage of total pods at the start of update (ex: 10%). Absolute number is calculated from percentage by rounding up.\n\nThis cannot be 0 if MaxSurge is 0. By default, 25% is used.\n\nExample: when this is set to 30%, the old RC can be scaled down by 30% immediately when the rolling update starts. Once new pods are ready, old RC can be scaled down further, followed by scaling up the new RC, ensuring that at least 70% of original number of pods are available at all times during the update.",
|
||||
"maxSurge": "MaxSurge is the maximum number of pods that can be scheduled above the original number of pods. Value can be an absolute number (ex: 5) or a percentage of total pods at the start of the update (ex: 10%). Absolute number is calculated from percentage by rounding up.\n\nThis cannot be 0 if MaxUnavailable is 0. By default, 25% is used.\n\nExample: when this is set to 30%, the new RC can be scaled up by 30% immediately when the rolling update starts. Once old pods have been killed, new RC can be scaled up further, ensuring that total number of pods running at any time during the update is atmost 130% of original pods.",
|
||||
"updatePercent": "UpdatePercent is the percentage of replicas to scale up or down each interval. If nil, one replica will be scaled up and down each interval. If negative, the scale order will be down/up instead of up/down. DEPRECATED: Use MaxUnavailable/MaxSurge instead.",
|
||||
"pre": "Pre is a lifecycle hook which is executed before the deployment process begins. All LifecycleHookFailurePolicy values are supported.",
|
||||
"post": "Post is a lifecycle hook which is executed after the strategy has finished all deployment logic. The LifecycleHookFailurePolicyAbort policy is NOT supported.",
|
||||
"post": "Post is a lifecycle hook which is executed after the strategy has finished all deployment logic. All LifecycleHookFailurePolicy values are supported.",
|
||||
}
|
||||
|
||||
func (RollingDeploymentStrategyParams) SwaggerDoc() map[string]string {
|
||||
|
||||
+177
-199
@@ -8,37 +8,83 @@ import (
|
||||
"k8s.io/kubernetes/pkg/util/intstr"
|
||||
)
|
||||
|
||||
// DeploymentPhase describes the possible states a deployment can be in.
|
||||
type DeploymentPhase string
|
||||
// +genclient=true
|
||||
|
||||
const (
|
||||
// DeploymentPhaseNew means the deployment has been accepted but not yet acted upon.
|
||||
DeploymentPhaseNew DeploymentPhase = "New"
|
||||
// DeploymentPhasePending means the deployment been handed over to a deployment strategy,
|
||||
// but the strategy has not yet declared the deployment to be running.
|
||||
DeploymentPhasePending DeploymentPhase = "Pending"
|
||||
// DeploymentPhaseRunning means the deployment strategy has reported the deployment as
|
||||
// being in-progress.
|
||||
DeploymentPhaseRunning DeploymentPhase = "Running"
|
||||
// DeploymentPhaseComplete means the deployment finished without an error.
|
||||
DeploymentPhaseComplete DeploymentPhase = "Complete"
|
||||
// DeploymentPhaseFailed means the deployment finished with an error.
|
||||
DeploymentPhaseFailed DeploymentPhase = "Failed"
|
||||
)
|
||||
// Deployment Configs define the template for a pod and manages deploying new images or configuration changes.
|
||||
// A single deployment configuration is usually analogous to a single micro-service. Can support many different
|
||||
// deployment patterns, including full restart, customizable rolling updates, and fully custom behaviors, as
|
||||
// well as pre- and post- deployment hooks. Each individual deployment is represented as a replication controller.
|
||||
//
|
||||
// A deployment is "triggered" when its configuration is changed or a tag in an Image Stream is changed.
|
||||
// Triggers can be disabled to allow manual control over a deployment. The "strategy" determines how the deployment
|
||||
// is carried out and may be changed at any time. The `latestVersion` field is updated when a new deployment
|
||||
// is triggered by any means.
|
||||
type DeploymentConfig struct {
|
||||
unversioned.TypeMeta `json:",inline"`
|
||||
// Standard object's metadata.
|
||||
kapi.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||
|
||||
// Spec represents a desired deployment state and how to deploy to it.
|
||||
Spec DeploymentConfigSpec `json:"spec" protobuf:"bytes,2,opt,name=spec"`
|
||||
|
||||
// Status represents the current deployment state.
|
||||
Status DeploymentConfigStatus `json:"status" protobuf:"bytes,3,opt,name=status"`
|
||||
}
|
||||
|
||||
// DeploymentConfigSpec represents the desired state of the deployment.
|
||||
type DeploymentConfigSpec struct {
|
||||
// Strategy describes how a deployment is executed.
|
||||
Strategy DeploymentStrategy `json:"strategy" protobuf:"bytes,1,opt,name=strategy"`
|
||||
|
||||
// MinReadySeconds is the minimum number of seconds for which a newly created pod should
|
||||
// be ready without any of its container crashing, for it to be considered available.
|
||||
// Defaults to 0 (pod will be considered available as soon as it is ready)
|
||||
MinReadySeconds int32 `json:"minReadySeconds,omitempty" protobuf:"varint,9,opt,name=minReadySeconds"`
|
||||
|
||||
// Triggers determine how updates to a DeploymentConfig result in new deployments. If no triggers
|
||||
// are defined, a new deployment can only occur as a result of an explicit client update to the
|
||||
// DeploymentConfig with a new LatestVersion. If null, defaults to having a config change trigger.
|
||||
Triggers DeploymentTriggerPolicies `json:"triggers" protobuf:"bytes,2,rep,name=triggers"`
|
||||
|
||||
// Replicas is the number of desired replicas.
|
||||
Replicas int32 `json:"replicas" protobuf:"varint,3,opt,name=replicas"`
|
||||
|
||||
// RevisionHistoryLimit is the number of old ReplicationControllers to retain to allow for rollbacks.
|
||||
// This field is a pointer to allow for differentiation between an explicit zero and not specified.
|
||||
RevisionHistoryLimit *int32 `json:"revisionHistoryLimit,omitempty" protobuf:"varint,4,opt,name=revisionHistoryLimit"`
|
||||
|
||||
// Test ensures that this deployment config will have zero replicas except while a deployment is running. This allows the
|
||||
// deployment config to be used as a continuous deployment test - triggering on images, running the deployment, and then succeeding
|
||||
// or failing. Post strategy hooks and After actions can be used to integrate successful deployment with an action.
|
||||
Test bool `json:"test" protobuf:"varint,5,opt,name=test"`
|
||||
|
||||
// Paused indicates that the deployment config is paused resulting in no new deployments on template
|
||||
// changes or changes in the template caused by other triggers.
|
||||
Paused bool `json:"paused,omitempty" protobuf:"varint,6,opt,name=paused"`
|
||||
|
||||
// Selector is a label query over pods that should match the Replicas count.
|
||||
Selector map[string]string `json:"selector,omitempty" protobuf:"bytes,7,rep,name=selector"`
|
||||
|
||||
// Template is the object that describes the pod that will be created if
|
||||
// insufficient replicas are detected.
|
||||
Template *kapi.PodTemplateSpec `json:"template,omitempty" protobuf:"bytes,8,opt,name=template"`
|
||||
}
|
||||
|
||||
// DeploymentStrategy describes how to perform a deployment.
|
||||
type DeploymentStrategy struct {
|
||||
// Type is the name of a deployment strategy.
|
||||
Type DeploymentStrategyType `json:"type,omitempty" protobuf:"bytes,1,opt,name=type,casttype=DeploymentStrategyType"`
|
||||
|
||||
// CustomParams are the input to the Custom deployment strategy.
|
||||
// CustomParams are the input to the Custom deployment strategy, and may also
|
||||
// be specified for the Recreate and Rolling strategies to customize the execution
|
||||
// process that runs the deployment.
|
||||
CustomParams *CustomDeploymentStrategyParams `json:"customParams,omitempty" protobuf:"bytes,2,opt,name=customParams"`
|
||||
// RecreateParams are the input to the Recreate deployment strategy.
|
||||
RecreateParams *RecreateDeploymentStrategyParams `json:"recreateParams,omitempty" protobuf:"bytes,3,opt,name=recreateParams"`
|
||||
// RollingParams are the input to the Rolling deployment strategy.
|
||||
RollingParams *RollingDeploymentStrategyParams `json:"rollingParams,omitempty" protobuf:"bytes,4,opt,name=rollingParams"`
|
||||
|
||||
// Resources contains resource requirements to execute the deployment and any hooks
|
||||
// Resources contains resource requirements to execute the deployment and any hooks.
|
||||
Resources kapi.ResourceRequirements `json:"resources,omitempty" protobuf:"bytes,5,opt,name=resources"`
|
||||
// Labels is a set of key, value pairs added to custom deployer and lifecycle pre/post hook pods.
|
||||
Labels map[string]string `json:"labels,omitempty" protobuf:"bytes,6,rep,name=labels"`
|
||||
@@ -85,56 +131,6 @@ type RecreateDeploymentStrategyParams struct {
|
||||
Post *LifecycleHook `json:"post,omitempty" protobuf:"bytes,4,opt,name=post"`
|
||||
}
|
||||
|
||||
// LifecycleHook defines a specific deployment lifecycle action. Only one type of action may be specified at any time.
|
||||
type LifecycleHook struct {
|
||||
// FailurePolicy specifies what action to take if the hook fails.
|
||||
FailurePolicy LifecycleHookFailurePolicy `json:"failurePolicy" protobuf:"bytes,1,opt,name=failurePolicy,casttype=LifecycleHookFailurePolicy"`
|
||||
|
||||
// ExecNewPod specifies the options for a lifecycle hook backed by a pod.
|
||||
ExecNewPod *ExecNewPodHook `json:"execNewPod,omitempty" protobuf:"bytes,2,opt,name=execNewPod"`
|
||||
|
||||
// TagImages instructs the deployer to tag the current image referenced under a container onto an image stream tag.
|
||||
TagImages []TagImageHook `json:"tagImages,omitempty" protobuf:"bytes,3,rep,name=tagImages"`
|
||||
}
|
||||
|
||||
// LifecycleHookFailurePolicy describes possibles actions to take if a hook fails.
|
||||
type LifecycleHookFailurePolicy string
|
||||
|
||||
const (
|
||||
// LifecycleHookFailurePolicyRetry means retry the hook until it succeeds.
|
||||
LifecycleHookFailurePolicyRetry LifecycleHookFailurePolicy = "Retry"
|
||||
// LifecycleHookFailurePolicyAbort means abort the deployment (if possible).
|
||||
LifecycleHookFailurePolicyAbort LifecycleHookFailurePolicy = "Abort"
|
||||
// LifecycleHookFailurePolicyIgnore means ignore failure and continue the deployment.
|
||||
LifecycleHookFailurePolicyIgnore LifecycleHookFailurePolicy = "Ignore"
|
||||
)
|
||||
|
||||
// ExecNewPodHook is a hook implementation which runs a command in a new pod
|
||||
// based on the specified container which is assumed to be part of the
|
||||
// deployment template.
|
||||
type ExecNewPodHook struct {
|
||||
// Command is the action command and its arguments.
|
||||
Command []string `json:"command" protobuf:"bytes,1,rep,name=command"`
|
||||
// Env is a set of environment variables to supply to the hook pod's container.
|
||||
Env []kapi.EnvVar `json:"env,omitempty" protobuf:"bytes,2,rep,name=env"`
|
||||
// ContainerName is the name of a container in the deployment pod template
|
||||
// whose Docker image will be used for the hook pod's container.
|
||||
ContainerName string `json:"containerName" protobuf:"bytes,3,opt,name=containerName"`
|
||||
// Volumes is a list of named volumes from the pod template which should be
|
||||
// copied to the hook pod. Volumes names not found in pod spec are ignored.
|
||||
// An empty list means no volumes will be copied.
|
||||
Volumes []string `json:"volumes,omitempty" protobuf:"bytes,4,rep,name=volumes"`
|
||||
}
|
||||
|
||||
// TagImageHook is a request to tag the image in a particular container onto an ImageStreamTag.
|
||||
type TagImageHook struct {
|
||||
// ContainerName is the name of a container in the deployment config whose image value will be used as the source of the tag. If there is only a single
|
||||
// container this value will be defaulted to the name of that container.
|
||||
ContainerName string `json:"containerName" protobuf:"bytes,1,opt,name=containerName"`
|
||||
// To is the target ImageStreamTag to set the container's image onto.
|
||||
To kapi.ObjectReference `json:"to" protobuf:"bytes,2,opt,name=to"`
|
||||
}
|
||||
|
||||
// RollingDeploymentStrategyParams are the input to the Rolling deployment
|
||||
// strategy.
|
||||
type RollingDeploymentStrategyParams struct {
|
||||
@@ -173,85 +169,63 @@ type RollingDeploymentStrategyParams struct {
|
||||
// pods running at any time during the update is atmost 130% of original
|
||||
// pods.
|
||||
MaxSurge *intstr.IntOrString `json:"maxSurge,omitempty" protobuf:"bytes,5,opt,name=maxSurge"`
|
||||
// UpdatePercent is the percentage of replicas to scale up or down each
|
||||
// interval. If nil, one replica will be scaled up and down each interval.
|
||||
// If negative, the scale order will be down/up instead of up/down.
|
||||
// DEPRECATED: Use MaxUnavailable/MaxSurge instead.
|
||||
UpdatePercent *int32 `json:"updatePercent,omitempty" protobuf:"varint,6,opt,name=updatePercent"`
|
||||
// Pre is a lifecycle hook which is executed before the deployment process
|
||||
// begins. All LifecycleHookFailurePolicy values are supported.
|
||||
Pre *LifecycleHook `json:"pre,omitempty" protobuf:"bytes,7,opt,name=pre"`
|
||||
// Post is a lifecycle hook which is executed after the strategy has
|
||||
// finished all deployment logic. The LifecycleHookFailurePolicyAbort policy
|
||||
// is NOT supported.
|
||||
// finished all deployment logic. All LifecycleHookFailurePolicy values
|
||||
// are supported.
|
||||
Post *LifecycleHook `json:"post,omitempty" protobuf:"bytes,8,opt,name=post"`
|
||||
}
|
||||
|
||||
// These constants represent keys used for correlating objects related to deployments.
|
||||
// LifecycleHook defines a specific deployment lifecycle action. Only one type of action may be specified at any time.
|
||||
type LifecycleHook struct {
|
||||
// FailurePolicy specifies what action to take if the hook fails.
|
||||
FailurePolicy LifecycleHookFailurePolicy `json:"failurePolicy" protobuf:"bytes,1,opt,name=failurePolicy,casttype=LifecycleHookFailurePolicy"`
|
||||
|
||||
// ExecNewPod specifies the options for a lifecycle hook backed by a pod.
|
||||
ExecNewPod *ExecNewPodHook `json:"execNewPod,omitempty" protobuf:"bytes,2,opt,name=execNewPod"`
|
||||
|
||||
// TagImages instructs the deployer to tag the current image referenced under a container onto an image stream tag.
|
||||
TagImages []TagImageHook `json:"tagImages,omitempty" protobuf:"bytes,3,rep,name=tagImages"`
|
||||
}
|
||||
|
||||
// LifecycleHookFailurePolicy describes possibles actions to take if a hook fails.
|
||||
type LifecycleHookFailurePolicy string
|
||||
|
||||
const (
|
||||
// DeploymentConfigAnnotation is an annotation name used to correlate a deployment with the
|
||||
// DeploymentConfig on which the deployment is based.
|
||||
DeploymentConfigAnnotation = "openshift.io/deployment-config.name"
|
||||
// DeploymentAnnotation is an annotation on a deployer Pod. The annotation value is the name
|
||||
// of the deployment (a ReplicationController) on which the deployer Pod acts.
|
||||
DeploymentAnnotation = "openshift.io/deployment.name"
|
||||
// DeploymentPodAnnotation is an annotation on a deployment (a ReplicationController). The
|
||||
// annotation value is the name of the deployer Pod which will act upon the ReplicationController
|
||||
// to implement the deployment behavior.
|
||||
DeploymentPodAnnotation = "openshift.io/deployer-pod.name"
|
||||
// DeploymentPodTypeLabel is a label with which contains a type of deployment pod.
|
||||
DeploymentPodTypeLabel = "openshift.io/deployer-pod.type"
|
||||
// DeployerPodForDeploymentLabel is a label which groups pods related to a
|
||||
// deployment. The value is a deployment name. The deployer pod and hook pods
|
||||
// created by the internal strategies will have this label. Custom
|
||||
// strategies can apply this label to any pods they create, enabling
|
||||
// platform-provided cancellation and garbage collection support.
|
||||
DeployerPodForDeploymentLabel = "openshift.io/deployer-pod-for.name"
|
||||
// DeploymentPhaseAnnotation is an annotation name used to retrieve the DeploymentPhase of
|
||||
// a deployment.
|
||||
DeploymentPhaseAnnotation = "openshift.io/deployment.phase"
|
||||
// DeploymentEncodedConfigAnnotation is an annotation name used to retrieve specific encoded
|
||||
// DeploymentConfig on which a given deployment is based.
|
||||
DeploymentEncodedConfigAnnotation = "openshift.io/encoded-deployment-config"
|
||||
// DeploymentVersionAnnotation is an annotation on a deployment (a ReplicationController). The
|
||||
// annotation value is the LatestVersion value of the DeploymentConfig which was the basis for
|
||||
// the deployment.
|
||||
DeploymentVersionAnnotation = "openshift.io/deployment-config.latest-version"
|
||||
// DeploymentLabel is the name of a label used to correlate a deployment with the Pod created
|
||||
// to execute the deployment logic.
|
||||
// TODO: This is a workaround for upstream's lack of annotation support on PodTemplate. Once
|
||||
// annotations are available on PodTemplate, audit this constant with the goal of removing it.
|
||||
DeploymentLabel = "deployment"
|
||||
// DeploymentConfigLabel is the name of a label used to correlate a deployment with the
|
||||
// DeploymentConfigs on which the deployment is based.
|
||||
DeploymentConfigLabel = "deploymentconfig"
|
||||
// DeploymentStatusReasonAnnotation represents the reason for deployment being in a given state
|
||||
// Used for specifying the reason for cancellation or failure of a deployment
|
||||
DeploymentStatusReasonAnnotation = "openshift.io/deployment.status-reason"
|
||||
// DeploymentCancelledAnnotation indicates that the deployment has been cancelled
|
||||
// The annotation value does not matter and its mere presence indicates cancellation
|
||||
DeploymentCancelledAnnotation = "openshift.io/deployment.cancelled"
|
||||
// DeploymentInstantiatedAnnotation indicates that the deployment has been instantiated.
|
||||
// The annotation value does not matter and its mere presence indicates instantiation.
|
||||
DeploymentInstantiatedAnnotation = "openshift.io/deployment.instantiated"
|
||||
// LifecycleHookFailurePolicyRetry means retry the hook until it succeeds.
|
||||
LifecycleHookFailurePolicyRetry LifecycleHookFailurePolicy = "Retry"
|
||||
// LifecycleHookFailurePolicyAbort means abort the deployment.
|
||||
LifecycleHookFailurePolicyAbort LifecycleHookFailurePolicy = "Abort"
|
||||
// LifecycleHookFailurePolicyIgnore means ignore failure and continue the deployment.
|
||||
LifecycleHookFailurePolicyIgnore LifecycleHookFailurePolicy = "Ignore"
|
||||
)
|
||||
|
||||
// +genclient=true
|
||||
// ExecNewPodHook is a hook implementation which runs a command in a new pod
|
||||
// based on the specified container which is assumed to be part of the
|
||||
// deployment template.
|
||||
type ExecNewPodHook struct {
|
||||
// Command is the action command and its arguments.
|
||||
Command []string `json:"command" protobuf:"bytes,1,rep,name=command"`
|
||||
// Env is a set of environment variables to supply to the hook pod's container.
|
||||
Env []kapi.EnvVar `json:"env,omitempty" protobuf:"bytes,2,rep,name=env"`
|
||||
// ContainerName is the name of a container in the deployment pod template
|
||||
// whose Docker image will be used for the hook pod's container.
|
||||
ContainerName string `json:"containerName" protobuf:"bytes,3,opt,name=containerName"`
|
||||
// Volumes is a list of named volumes from the pod template which should be
|
||||
// copied to the hook pod. Volumes names not found in pod spec are ignored.
|
||||
// An empty list means no volumes will be copied.
|
||||
Volumes []string `json:"volumes,omitempty" protobuf:"bytes,4,rep,name=volumes"`
|
||||
}
|
||||
|
||||
// DeploymentConfig represents a configuration for a single deployment (represented as a
|
||||
// ReplicationController). It also contains details about changes which resulted in the current
|
||||
// state of the DeploymentConfig. Each change to the DeploymentConfig which should result in
|
||||
// a new deployment results in an increment of LatestVersion.
|
||||
type DeploymentConfig struct {
|
||||
unversioned.TypeMeta `json:",inline"`
|
||||
// Standard object's metadata.
|
||||
kapi.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||
|
||||
// Spec represents a desired deployment state and how to deploy to it.
|
||||
Spec DeploymentConfigSpec `json:"spec" protobuf:"bytes,2,opt,name=spec"`
|
||||
|
||||
// Status represents the current deployment state.
|
||||
Status DeploymentConfigStatus `json:"status" protobuf:"bytes,3,opt,name=status"`
|
||||
// TagImageHook is a request to tag the image in a particular container onto an ImageStreamTag.
|
||||
type TagImageHook struct {
|
||||
// ContainerName is the name of a container in the deployment config whose image value will be used as the source of the tag. If there is only a single
|
||||
// container this value will be defaulted to the name of that container.
|
||||
ContainerName string `json:"containerName" protobuf:"bytes,1,opt,name=containerName"`
|
||||
// To is the target ImageStreamTag to set the container's image onto.
|
||||
To kapi.ObjectReference `json:"to" protobuf:"bytes,2,opt,name=to"`
|
||||
}
|
||||
|
||||
// DeploymentTriggerPolicies is a list of policies where nil values and different from empty arrays.
|
||||
@@ -263,66 +237,6 @@ func (t DeploymentTriggerPolicies) String() string {
|
||||
return fmt.Sprintf("%v", []DeploymentTriggerPolicy(t))
|
||||
}
|
||||
|
||||
// DeploymentConfigSpec represents the desired state of the deployment.
|
||||
type DeploymentConfigSpec struct {
|
||||
// Strategy describes how a deployment is executed.
|
||||
Strategy DeploymentStrategy `json:"strategy" protobuf:"bytes,1,opt,name=strategy"`
|
||||
|
||||
// MinReadySeconds is the minimum number of seconds for which a newly created pod should
|
||||
// be ready without any of its container crashing, for it to be considered available.
|
||||
// Defaults to 0 (pod will be considered available as soon as it is ready)
|
||||
MinReadySeconds int32 `json:"minReadySeconds,omitempty" protobuf:"varint,9,opt,name=minReadySeconds"`
|
||||
|
||||
// Triggers determine how updates to a DeploymentConfig result in new deployments. If no triggers
|
||||
// are defined, a new deployment can only occur as a result of an explicit client update to the
|
||||
// DeploymentConfig with a new LatestVersion. If null, defaults to having a config change trigger.
|
||||
Triggers DeploymentTriggerPolicies `json:"triggers" protobuf:"bytes,2,rep,name=triggers"`
|
||||
|
||||
// Replicas is the number of desired replicas.
|
||||
Replicas int32 `json:"replicas" protobuf:"varint,3,opt,name=replicas"`
|
||||
|
||||
// RevisionHistoryLimit is the number of old ReplicationControllers to retain to allow for rollbacks.
|
||||
// This field is a pointer to allow for differentiation between an explicit zero and not specified.
|
||||
RevisionHistoryLimit *int32 `json:"revisionHistoryLimit,omitempty" protobuf:"varint,4,opt,name=revisionHistoryLimit"`
|
||||
|
||||
// Test ensures that this deployment config will have zero replicas except while a deployment is running. This allows the
|
||||
// deployment config to be used as a continuous deployment test - triggering on images, running the deployment, and then succeeding
|
||||
// or failing. Post strategy hooks and After actions can be used to integrate successful deployment with an action.
|
||||
Test bool `json:"test" protobuf:"varint,5,opt,name=test"`
|
||||
|
||||
// Paused indicates that the deployment config is paused resulting in no new deployments on template
|
||||
// changes or changes in the template caused by other triggers.
|
||||
Paused bool `json:"paused,omitempty" protobuf:"varint,6,opt,name=paused"`
|
||||
|
||||
// Selector is a label query over pods that should match the Replicas count.
|
||||
Selector map[string]string `json:"selector,omitempty" protobuf:"bytes,7,rep,name=selector"`
|
||||
|
||||
// Template is the object that describes the pod that will be created if
|
||||
// insufficient replicas are detected.
|
||||
Template *kapi.PodTemplateSpec `json:"template,omitempty" protobuf:"bytes,8,opt,name=template"`
|
||||
}
|
||||
|
||||
// DeploymentConfigStatus represents the current deployment state.
|
||||
type DeploymentConfigStatus struct {
|
||||
// LatestVersion is used to determine whether the current deployment associated with a deployment
|
||||
// config is out of sync.
|
||||
LatestVersion int64 `json:"latestVersion,omitempty" protobuf:"varint,1,opt,name=latestVersion"`
|
||||
// ObservedGeneration is the most recent generation observed by the deployment config controller.
|
||||
ObservedGeneration int64 `json:"observedGeneration,omitempty" protobuf:"varint,2,opt,name=observedGeneration"`
|
||||
// Replicas is the total number of pods targeted by this deployment config.
|
||||
Replicas int32 `json:"replicas,omitempty" protobuf:"varint,3,opt,name=replicas"`
|
||||
// UpdatedReplicas is the total number of non-terminated pods targeted by this deployment config
|
||||
// that have the desired template spec.
|
||||
UpdatedReplicas int32 `json:"updatedReplicas,omitempty" protobuf:"varint,4,opt,name=updatedReplicas"`
|
||||
// AvailableReplicas is the total number of available pods targeted by this deployment config.
|
||||
AvailableReplicas int32 `json:"availableReplicas,omitempty" protobuf:"varint,5,opt,name=availableReplicas"`
|
||||
// UnavailableReplicas is the total number of unavailable pods targeted by this deployment config.
|
||||
UnavailableReplicas int32 `json:"unavailableReplicas,omitempty" protobuf:"varint,6,opt,name=unavailableReplicas"`
|
||||
// Details are the reasons for the update to this deployment config.
|
||||
// This could be based on a change made by the user or caused by an automatic trigger
|
||||
Details *DeploymentDetails `json:"details,omitempty" protobuf:"bytes,7,opt,name=details"`
|
||||
}
|
||||
|
||||
// DeploymentTriggerPolicy describes a policy for a single trigger that results in a new deployment.
|
||||
type DeploymentTriggerPolicy struct {
|
||||
// Type of the trigger
|
||||
@@ -346,9 +260,7 @@ const (
|
||||
// DeploymentTriggerImageChangeParams represents the parameters to the ImageChange trigger.
|
||||
type DeploymentTriggerImageChangeParams struct {
|
||||
// Automatic means that the detection of a new tag value should result in an image update
|
||||
// inside the pod template. Deployment configs that haven't been deployed yet will always
|
||||
// have their images updated. Deployment configs that have been deployed at least once, will
|
||||
// have their images updated only if this is set to true.
|
||||
// inside the pod template.
|
||||
Automatic bool `json:"automatic,omitempty" protobuf:"varint,1,opt,name=automatic"`
|
||||
// ContainerNames is used to restrict tag updates to the specified set of container names in a pod.
|
||||
ContainerNames []string `json:"containerNames,omitempty" protobuf:"bytes,2,rep,name=containerNames"`
|
||||
@@ -360,6 +272,29 @@ type DeploymentTriggerImageChangeParams struct {
|
||||
LastTriggeredImage string `json:"lastTriggeredImage,omitempty" protobuf:"bytes,4,opt,name=lastTriggeredImage"`
|
||||
}
|
||||
|
||||
// DeploymentConfigStatus represents the current deployment state.
|
||||
type DeploymentConfigStatus struct {
|
||||
// LatestVersion is used to determine whether the current deployment associated with a deployment
|
||||
// config is out of sync.
|
||||
LatestVersion int64 `json:"latestVersion,omitempty" protobuf:"varint,1,opt,name=latestVersion"`
|
||||
// ObservedGeneration is the most recent generation observed by the deployment config controller.
|
||||
ObservedGeneration int64 `json:"observedGeneration,omitempty" protobuf:"varint,2,opt,name=observedGeneration"`
|
||||
// Replicas is the total number of pods targeted by this deployment config.
|
||||
Replicas int32 `json:"replicas,omitempty" protobuf:"varint,3,opt,name=replicas"`
|
||||
// UpdatedReplicas is the total number of non-terminated pods targeted by this deployment config
|
||||
// that have the desired template spec.
|
||||
UpdatedReplicas int32 `json:"updatedReplicas,omitempty" protobuf:"varint,4,opt,name=updatedReplicas"`
|
||||
// AvailableReplicas is the total number of available pods targeted by this deployment config.
|
||||
AvailableReplicas int32 `json:"availableReplicas,omitempty" protobuf:"varint,5,opt,name=availableReplicas"`
|
||||
// UnavailableReplicas is the total number of unavailable pods targeted by this deployment config.
|
||||
UnavailableReplicas int32 `json:"unavailableReplicas,omitempty" protobuf:"varint,6,opt,name=unavailableReplicas"`
|
||||
// Details are the reasons for the update to this deployment config.
|
||||
// This could be based on a change made by the user or caused by an automatic trigger
|
||||
Details *DeploymentDetails `json:"details,omitempty" protobuf:"bytes,7,opt,name=details"`
|
||||
// Conditions represents the latest available observations of a deployment config's current state.
|
||||
Conditions []DeploymentCondition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,8,rep,name=conditions"`
|
||||
}
|
||||
|
||||
// DeploymentDetails captures information about the causes of a deployment.
|
||||
type DeploymentDetails struct {
|
||||
// Message is the user specified change message, if this deployment was triggered manually by the user
|
||||
@@ -384,6 +319,37 @@ type DeploymentCauseImageTrigger struct {
|
||||
From kapi.ObjectReference `json:"from" protobuf:"bytes,1,opt,name=from"`
|
||||
}
|
||||
|
||||
type DeploymentConditionType string
|
||||
|
||||
// These are valid conditions of a deployment config.
|
||||
const (
|
||||
// DeploymentAvailable means the deployment config is available, ie. at least the minimum available
|
||||
// replicas required are up and running for at least minReadySeconds.
|
||||
DeploymentAvailable DeploymentConditionType = "Available"
|
||||
// DeploymentProgressing means the deployment config is progressing. Progress for a deployment
|
||||
// config is considered when a new replica set is created or adopted, and when new pods scale up or
|
||||
// old pods scale down. Progress is not estimated for paused deployment configs, when the deployment
|
||||
// config needs to rollback, or when progressDeadlineSeconds is not specified.
|
||||
DeploymentProgressing DeploymentConditionType = "Progressing"
|
||||
// DeploymentReplicaFailure is added in a deployment config when one of its pods
|
||||
// fails to be created or deleted.
|
||||
DeploymentReplicaFailure DeploymentConditionType = "ReplicaFailure"
|
||||
)
|
||||
|
||||
// DeploymentCondition describes the state of a deployment config at a certain point.
|
||||
type DeploymentCondition struct {
|
||||
// Type of deployment condition.
|
||||
Type DeploymentConditionType `json:"type" protobuf:"bytes,1,opt,name=type,casttype=DeploymentConditionType"`
|
||||
// Status of the condition, one of True, False, Unknown.
|
||||
Status kapi.ConditionStatus `json:"status" protobuf:"bytes,2,opt,name=status,casttype=k8s.io/kubernetes/pkg/api/v1.ConditionStatus"`
|
||||
// The last time the condition transitioned from one status to another.
|
||||
LastTransitionTime unversioned.Time `json:"lastTransitionTime,omitempty" protobuf:"bytes,3,opt,name=lastTransitionTime"`
|
||||
// The reason for the condition's last transition.
|
||||
Reason string `json:"reason,omitempty" protobuf:"bytes,4,opt,name=reason"`
|
||||
// A human readable message indicating details about the transition.
|
||||
Message string `json:"message,omitempty" protobuf:"bytes,5,opt,name=message"`
|
||||
}
|
||||
|
||||
// DeploymentConfigList is a collection of deployment configs.
|
||||
type DeploymentConfigList struct {
|
||||
unversioned.TypeMeta `json:",inline"`
|
||||
@@ -421,6 +387,18 @@ type DeploymentConfigRollbackSpec struct {
|
||||
IncludeStrategy bool `json:"includeStrategy" protobuf:"varint,6,opt,name=includeStrategy"`
|
||||
}
|
||||
|
||||
// DeploymentRequest is a request to a deployment config for a new deployment.
|
||||
type DeploymentRequest struct {
|
||||
unversioned.TypeMeta `json:",inline"`
|
||||
// Name of the deployment config for requesting a new deployment.
|
||||
Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
|
||||
// Latest will update the deployment config with the latest state from all triggers.
|
||||
Latest bool `json:"latest" protobuf:"varint,2,opt,name=latest"`
|
||||
// Force will try to force a new deployment to run. If the deployment config is paused,
|
||||
// then setting this to true will return an Invalid error.
|
||||
Force bool `json:"force" protobuf:"varint,3,opt,name=force"`
|
||||
}
|
||||
|
||||
// DeploymentLog represents the logs for a deployment
|
||||
type DeploymentLog struct {
|
||||
unversioned.TypeMeta `json:",inline"`
|
||||
|
||||
+93
-9
@@ -26,6 +26,8 @@ func RegisterConversions(scheme *runtime.Scheme) error {
|
||||
Convert_api_DeploymentCause_To_v1_DeploymentCause,
|
||||
Convert_v1_DeploymentCauseImageTrigger_To_api_DeploymentCauseImageTrigger,
|
||||
Convert_api_DeploymentCauseImageTrigger_To_v1_DeploymentCauseImageTrigger,
|
||||
Convert_v1_DeploymentCondition_To_api_DeploymentCondition,
|
||||
Convert_api_DeploymentCondition_To_v1_DeploymentCondition,
|
||||
Convert_v1_DeploymentConfig_To_api_DeploymentConfig,
|
||||
Convert_api_DeploymentConfig_To_v1_DeploymentConfig,
|
||||
Convert_v1_DeploymentConfigList_To_api_DeploymentConfigList,
|
||||
@@ -44,6 +46,8 @@ func RegisterConversions(scheme *runtime.Scheme) error {
|
||||
Convert_api_DeploymentLog_To_v1_DeploymentLog,
|
||||
Convert_v1_DeploymentLogOptions_To_api_DeploymentLogOptions,
|
||||
Convert_api_DeploymentLogOptions_To_v1_DeploymentLogOptions,
|
||||
Convert_v1_DeploymentRequest_To_api_DeploymentRequest,
|
||||
Convert_api_DeploymentRequest_To_v1_DeploymentRequest,
|
||||
Convert_v1_DeploymentStrategy_To_api_DeploymentStrategy,
|
||||
Convert_api_DeploymentStrategy_To_v1_DeploymentStrategy,
|
||||
Convert_v1_DeploymentTriggerImageChangeParams_To_api_DeploymentTriggerImageChangeParams,
|
||||
@@ -163,6 +167,36 @@ func Convert_api_DeploymentCauseImageTrigger_To_v1_DeploymentCauseImageTrigger(i
|
||||
return autoConvert_api_DeploymentCauseImageTrigger_To_v1_DeploymentCauseImageTrigger(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_DeploymentCondition_To_api_DeploymentCondition(in *DeploymentCondition, out *api.DeploymentCondition, s conversion.Scope) error {
|
||||
out.Type = api.DeploymentConditionType(in.Type)
|
||||
out.Status = pkg_api.ConditionStatus(in.Status)
|
||||
if err := pkg_api.Convert_unversioned_Time_To_unversioned_Time(&in.LastTransitionTime, &out.LastTransitionTime, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.Reason = in.Reason
|
||||
out.Message = in.Message
|
||||
return nil
|
||||
}
|
||||
|
||||
func Convert_v1_DeploymentCondition_To_api_DeploymentCondition(in *DeploymentCondition, out *api.DeploymentCondition, s conversion.Scope) error {
|
||||
return autoConvert_v1_DeploymentCondition_To_api_DeploymentCondition(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_api_DeploymentCondition_To_v1_DeploymentCondition(in *api.DeploymentCondition, out *DeploymentCondition, s conversion.Scope) error {
|
||||
out.Type = DeploymentConditionType(in.Type)
|
||||
out.Status = api_v1.ConditionStatus(in.Status)
|
||||
if err := pkg_api.Convert_unversioned_Time_To_unversioned_Time(&in.LastTransitionTime, &out.LastTransitionTime, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.Reason = in.Reason
|
||||
out.Message = in.Message
|
||||
return nil
|
||||
}
|
||||
|
||||
func Convert_api_DeploymentCondition_To_v1_DeploymentCondition(in *api.DeploymentCondition, out *DeploymentCondition, s conversion.Scope) error {
|
||||
return autoConvert_api_DeploymentCondition_To_v1_DeploymentCondition(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_DeploymentConfig_To_api_DeploymentConfig(in *DeploymentConfig, out *api.DeploymentConfig, s conversion.Scope) error {
|
||||
SetDefaults_DeploymentConfig(in)
|
||||
if err := pkg_api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
|
||||
@@ -409,6 +443,17 @@ func autoConvert_v1_DeploymentConfigStatus_To_api_DeploymentConfigStatus(in *Dep
|
||||
} else {
|
||||
out.Details = nil
|
||||
}
|
||||
if in.Conditions != nil {
|
||||
in, out := &in.Conditions, &out.Conditions
|
||||
*out = make([]api.DeploymentCondition, len(*in))
|
||||
for i := range *in {
|
||||
if err := Convert_v1_DeploymentCondition_To_api_DeploymentCondition(&(*in)[i], &(*out)[i], s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out.Conditions = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -432,6 +477,17 @@ func autoConvert_api_DeploymentConfigStatus_To_v1_DeploymentConfigStatus(in *api
|
||||
} else {
|
||||
out.Details = nil
|
||||
}
|
||||
if in.Conditions != nil {
|
||||
in, out := &in.Conditions, &out.Conditions
|
||||
*out = make([]DeploymentCondition, len(*in))
|
||||
for i := range *in {
|
||||
if err := Convert_api_DeploymentCondition_To_v1_DeploymentCondition(&(*in)[i], &(*out)[i], s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out.Conditions = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -543,6 +599,34 @@ func Convert_api_DeploymentLogOptions_To_v1_DeploymentLogOptions(in *api.Deploym
|
||||
return autoConvert_api_DeploymentLogOptions_To_v1_DeploymentLogOptions(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_DeploymentRequest_To_api_DeploymentRequest(in *DeploymentRequest, out *api.DeploymentRequest, s conversion.Scope) error {
|
||||
if err := pkg_api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.Name = in.Name
|
||||
out.Latest = in.Latest
|
||||
out.Force = in.Force
|
||||
return nil
|
||||
}
|
||||
|
||||
func Convert_v1_DeploymentRequest_To_api_DeploymentRequest(in *DeploymentRequest, out *api.DeploymentRequest, s conversion.Scope) error {
|
||||
return autoConvert_v1_DeploymentRequest_To_api_DeploymentRequest(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_api_DeploymentRequest_To_v1_DeploymentRequest(in *api.DeploymentRequest, out *DeploymentRequest, s conversion.Scope) error {
|
||||
if err := pkg_api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.Name = in.Name
|
||||
out.Latest = in.Latest
|
||||
out.Force = in.Force
|
||||
return nil
|
||||
}
|
||||
|
||||
func Convert_api_DeploymentRequest_To_v1_DeploymentRequest(in *api.DeploymentRequest, out *DeploymentRequest, s conversion.Scope) error {
|
||||
return autoConvert_api_DeploymentRequest_To_v1_DeploymentRequest(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_DeploymentStrategy_To_api_DeploymentStrategy(in *DeploymentStrategy, out *api.DeploymentStrategy, s conversion.Scope) error {
|
||||
SetDefaults_DeploymentStrategy(in)
|
||||
out.Type = api.DeploymentStrategyType(in.Type)
|
||||
@@ -587,6 +671,15 @@ func Convert_v1_DeploymentStrategy_To_api_DeploymentStrategy(in *DeploymentStrat
|
||||
|
||||
func autoConvert_api_DeploymentStrategy_To_v1_DeploymentStrategy(in *api.DeploymentStrategy, out *DeploymentStrategy, s conversion.Scope) error {
|
||||
out.Type = DeploymentStrategyType(in.Type)
|
||||
if in.CustomParams != nil {
|
||||
in, out := &in.CustomParams, &out.CustomParams
|
||||
*out = new(CustomDeploymentStrategyParams)
|
||||
if err := Convert_api_CustomDeploymentStrategyParams_To_v1_CustomDeploymentStrategyParams(*in, *out, s); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
out.CustomParams = nil
|
||||
}
|
||||
if in.RecreateParams != nil {
|
||||
in, out := &in.RecreateParams, &out.RecreateParams
|
||||
*out = new(RecreateDeploymentStrategyParams)
|
||||
@@ -605,15 +698,6 @@ func autoConvert_api_DeploymentStrategy_To_v1_DeploymentStrategy(in *api.Deploym
|
||||
} else {
|
||||
out.RollingParams = nil
|
||||
}
|
||||
if in.CustomParams != nil {
|
||||
in, out := &in.CustomParams, &out.CustomParams
|
||||
*out = new(CustomDeploymentStrategyParams)
|
||||
if err := Convert_api_CustomDeploymentStrategyParams_To_v1_CustomDeploymentStrategyParams(*in, *out, s); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
out.CustomParams = nil
|
||||
}
|
||||
if err := api_v1.Convert_api_ResourceRequirements_To_v1_ResourceRequirements(&in.Resources, &out.Resources, s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+38
-7
@@ -24,6 +24,7 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error {
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_CustomDeploymentStrategyParams, InType: reflect.TypeOf(&CustomDeploymentStrategyParams{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_DeploymentCause, InType: reflect.TypeOf(&DeploymentCause{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_DeploymentCauseImageTrigger, InType: reflect.TypeOf(&DeploymentCauseImageTrigger{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_DeploymentCondition, InType: reflect.TypeOf(&DeploymentCondition{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_DeploymentConfig, InType: reflect.TypeOf(&DeploymentConfig{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_DeploymentConfigList, InType: reflect.TypeOf(&DeploymentConfigList{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_DeploymentConfigRollback, InType: reflect.TypeOf(&DeploymentConfigRollback{})},
|
||||
@@ -33,6 +34,7 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error {
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_DeploymentDetails, InType: reflect.TypeOf(&DeploymentDetails{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_DeploymentLog, InType: reflect.TypeOf(&DeploymentLog{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_DeploymentLogOptions, InType: reflect.TypeOf(&DeploymentLogOptions{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_DeploymentRequest, InType: reflect.TypeOf(&DeploymentRequest{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_DeploymentStrategy, InType: reflect.TypeOf(&DeploymentStrategy{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_DeploymentTriggerImageChangeParams, InType: reflect.TypeOf(&DeploymentTriggerImageChangeParams{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_DeploymentTriggerPolicy, InType: reflect.TypeOf(&DeploymentTriggerPolicy{})},
|
||||
@@ -96,6 +98,19 @@ func DeepCopy_v1_DeploymentCauseImageTrigger(in interface{}, out interface{}, c
|
||||
}
|
||||
}
|
||||
|
||||
func DeepCopy_v1_DeploymentCondition(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*DeploymentCondition)
|
||||
out := out.(*DeploymentCondition)
|
||||
out.Type = in.Type
|
||||
out.Status = in.Status
|
||||
out.LastTransitionTime = in.LastTransitionTime.DeepCopy()
|
||||
out.Reason = in.Reason
|
||||
out.Message = in.Message
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func DeepCopy_v1_DeploymentConfig(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*DeploymentConfig)
|
||||
@@ -239,6 +254,17 @@ func DeepCopy_v1_DeploymentConfigStatus(in interface{}, out interface{}, c *conv
|
||||
} else {
|
||||
out.Details = nil
|
||||
}
|
||||
if in.Conditions != nil {
|
||||
in, out := &in.Conditions, &out.Conditions
|
||||
*out = make([]DeploymentCondition, len(*in))
|
||||
for i := range *in {
|
||||
if err := DeepCopy_v1_DeploymentCondition(&(*in)[i], &(*out)[i], c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out.Conditions = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -321,6 +347,18 @@ func DeepCopy_v1_DeploymentLogOptions(in interface{}, out interface{}, c *conver
|
||||
}
|
||||
}
|
||||
|
||||
func DeepCopy_v1_DeploymentRequest(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*DeploymentRequest)
|
||||
out := out.(*DeploymentRequest)
|
||||
out.TypeMeta = in.TypeMeta
|
||||
out.Name = in.Name
|
||||
out.Latest = in.Latest
|
||||
out.Force = in.Force
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func DeepCopy_v1_DeploymentStrategy(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*DeploymentStrategy)
|
||||
@@ -556,13 +594,6 @@ func DeepCopy_v1_RollingDeploymentStrategyParams(in interface{}, out interface{}
|
||||
} else {
|
||||
out.MaxSurge = nil
|
||||
}
|
||||
if in.UpdatePercent != nil {
|
||||
in, out := &in.UpdatePercent, &out.UpdatePercent
|
||||
*out = new(int32)
|
||||
**out = **in
|
||||
} else {
|
||||
out.UpdatePercent = nil
|
||||
}
|
||||
if in.Pre != nil {
|
||||
in, out := &in.Pre, &out.Pre
|
||||
*out = new(LifecycleHook)
|
||||
|
||||
+47
-16
@@ -24,6 +24,7 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error {
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_CustomDeploymentStrategyParams, InType: reflect.TypeOf(&CustomDeploymentStrategyParams{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_DeploymentCause, InType: reflect.TypeOf(&DeploymentCause{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_DeploymentCauseImageTrigger, InType: reflect.TypeOf(&DeploymentCauseImageTrigger{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_DeploymentCondition, InType: reflect.TypeOf(&DeploymentCondition{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_DeploymentConfig, InType: reflect.TypeOf(&DeploymentConfig{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_DeploymentConfigList, InType: reflect.TypeOf(&DeploymentConfigList{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_DeploymentConfigRollback, InType: reflect.TypeOf(&DeploymentConfigRollback{})},
|
||||
@@ -33,6 +34,7 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error {
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_DeploymentDetails, InType: reflect.TypeOf(&DeploymentDetails{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_DeploymentLog, InType: reflect.TypeOf(&DeploymentLog{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_DeploymentLogOptions, InType: reflect.TypeOf(&DeploymentLogOptions{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_DeploymentRequest, InType: reflect.TypeOf(&DeploymentRequest{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_DeploymentStrategy, InType: reflect.TypeOf(&DeploymentStrategy{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_DeploymentTriggerImageChangeParams, InType: reflect.TypeOf(&DeploymentTriggerImageChangeParams{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_DeploymentTriggerPolicy, InType: reflect.TypeOf(&DeploymentTriggerPolicy{})},
|
||||
@@ -97,6 +99,19 @@ func DeepCopy_api_DeploymentCauseImageTrigger(in interface{}, out interface{}, c
|
||||
}
|
||||
}
|
||||
|
||||
func DeepCopy_api_DeploymentCondition(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*DeploymentCondition)
|
||||
out := out.(*DeploymentCondition)
|
||||
out.Type = in.Type
|
||||
out.Status = in.Status
|
||||
out.LastTransitionTime = in.LastTransitionTime.DeepCopy()
|
||||
out.Reason = in.Reason
|
||||
out.Message = in.Message
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func DeepCopy_api_DeploymentConfig(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*DeploymentConfig)
|
||||
@@ -240,6 +255,17 @@ func DeepCopy_api_DeploymentConfigStatus(in interface{}, out interface{}, c *con
|
||||
} else {
|
||||
out.Details = nil
|
||||
}
|
||||
if in.Conditions != nil {
|
||||
in, out := &in.Conditions, &out.Conditions
|
||||
*out = make([]DeploymentCondition, len(*in))
|
||||
for i := range *in {
|
||||
if err := DeepCopy_api_DeploymentCondition(&(*in)[i], &(*out)[i], c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out.Conditions = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -322,11 +348,32 @@ func DeepCopy_api_DeploymentLogOptions(in interface{}, out interface{}, c *conve
|
||||
}
|
||||
}
|
||||
|
||||
func DeepCopy_api_DeploymentRequest(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*DeploymentRequest)
|
||||
out := out.(*DeploymentRequest)
|
||||
out.TypeMeta = in.TypeMeta
|
||||
out.Name = in.Name
|
||||
out.Latest = in.Latest
|
||||
out.Force = in.Force
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func DeepCopy_api_DeploymentStrategy(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*DeploymentStrategy)
|
||||
out := out.(*DeploymentStrategy)
|
||||
out.Type = in.Type
|
||||
if in.CustomParams != nil {
|
||||
in, out := &in.CustomParams, &out.CustomParams
|
||||
*out = new(CustomDeploymentStrategyParams)
|
||||
if err := DeepCopy_api_CustomDeploymentStrategyParams(*in, *out, c); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
out.CustomParams = nil
|
||||
}
|
||||
if in.RecreateParams != nil {
|
||||
in, out := &in.RecreateParams, &out.RecreateParams
|
||||
*out = new(RecreateDeploymentStrategyParams)
|
||||
@@ -345,15 +392,6 @@ func DeepCopy_api_DeploymentStrategy(in interface{}, out interface{}, c *convers
|
||||
} else {
|
||||
out.RollingParams = nil
|
||||
}
|
||||
if in.CustomParams != nil {
|
||||
in, out := &in.CustomParams, &out.CustomParams
|
||||
*out = new(CustomDeploymentStrategyParams)
|
||||
if err := DeepCopy_api_CustomDeploymentStrategyParams(*in, *out, c); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
out.CustomParams = nil
|
||||
}
|
||||
if err := pkg_api.DeepCopy_api_ResourceRequirements(&in.Resources, &out.Resources, c); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -545,13 +583,6 @@ func DeepCopy_api_RollingDeploymentStrategyParams(in interface{}, out interface{
|
||||
}
|
||||
out.MaxUnavailable = in.MaxUnavailable
|
||||
out.MaxSurge = in.MaxSurge
|
||||
if in.UpdatePercent != nil {
|
||||
in, out := &in.UpdatePercent, &out.UpdatePercent
|
||||
*out = new(int32)
|
||||
**out = **in
|
||||
} else {
|
||||
out.UpdatePercent = nil
|
||||
}
|
||||
if in.Pre != nil {
|
||||
in, out := &in.Pre, &out.Pre
|
||||
*out = new(LifecycleHook)
|
||||
|
||||
-136
@@ -1,136 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
kapi "k8s.io/kubernetes/pkg/api"
|
||||
kerrors "k8s.io/kubernetes/pkg/api/errors"
|
||||
kclient "k8s.io/kubernetes/pkg/client/unversioned"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
kutil "k8s.io/kubernetes/pkg/util"
|
||||
"k8s.io/kubernetes/pkg/util/wait"
|
||||
|
||||
"github.com/openshift/origin/pkg/client"
|
||||
deployapi "github.com/openshift/origin/pkg/deploy/api"
|
||||
"github.com/openshift/origin/pkg/deploy/util"
|
||||
)
|
||||
|
||||
// NewDeploymentConfigReaper returns a new reaper for deploymentConfigs
|
||||
func NewDeploymentConfigReaper(oc client.Interface, kc kclient.Interface) kubectl.Reaper {
|
||||
return &DeploymentConfigReaper{oc: oc, kc: kc, pollInterval: kubectl.Interval, timeout: kubectl.Timeout}
|
||||
}
|
||||
|
||||
// DeploymentConfigReaper implements the Reaper interface for deploymentConfigs
|
||||
type DeploymentConfigReaper struct {
|
||||
oc client.Interface
|
||||
kc kclient.Interface
|
||||
pollInterval, timeout time.Duration
|
||||
}
|
||||
|
||||
// pause marks the deployment configuration as paused to avoid triggering new
|
||||
// deployments.
|
||||
func (reaper *DeploymentConfigReaper) pause(namespace, name string) (*deployapi.DeploymentConfig, error) {
|
||||
return client.UpdateConfigWithRetries(reaper.oc, namespace, name, func(d *deployapi.DeploymentConfig) {
|
||||
d.Spec.RevisionHistoryLimit = kutil.Int32Ptr(0)
|
||||
d.Spec.Replicas = 0
|
||||
d.Spec.Paused = true
|
||||
})
|
||||
}
|
||||
|
||||
// Stop scales a replication controller via its deployment configuration down to
|
||||
// zero replicas, waits for all of them to get deleted and then deletes both the
|
||||
// replication controller and its deployment configuration.
|
||||
func (reaper *DeploymentConfigReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *kapi.DeleteOptions) error {
|
||||
// Pause the deployment configuration to prevent the new deployments from
|
||||
// being triggered.
|
||||
config, err := reaper.pause(namespace, name)
|
||||
configNotFound := kerrors.IsNotFound(err)
|
||||
if err != nil && !configNotFound {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
isPaused bool
|
||||
legacy bool
|
||||
)
|
||||
// Determine if the deployment config controller noticed the pause.
|
||||
if !configNotFound {
|
||||
if err := wait.Poll(1*time.Second, 1*time.Minute, func() (bool, error) {
|
||||
dc, err := reaper.oc.DeploymentConfigs(namespace).Get(name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
isPaused = dc.Spec.Paused
|
||||
return dc.Status.ObservedGeneration >= config.Generation, nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If we failed to pause the deployment config, it means we are talking to
|
||||
// old API that does not support pausing. In that case, we delete the
|
||||
// deployment config to stay backward compatible.
|
||||
if !isPaused {
|
||||
if err := reaper.oc.DeploymentConfigs(namespace).Delete(name); err != nil {
|
||||
return err
|
||||
}
|
||||
// Setting this to true avoid deleting the config at the end.
|
||||
legacy = true
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up deployments related to the config. Even if the deployment
|
||||
// configuration has been deleted, we want to sweep the existing replication
|
||||
// controllers and clean them up.
|
||||
options := kapi.ListOptions{LabelSelector: util.ConfigSelector(name)}
|
||||
rcList, err := reaper.kc.ReplicationControllers(namespace).List(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rcReaper, err := kubectl.ReaperFor(kapi.Kind("ReplicationController"), reaper.kc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If there is neither a config nor any deployments, nor any deployer pods, we can return NotFound.
|
||||
deployments := rcList.Items
|
||||
|
||||
if configNotFound && len(deployments) == 0 {
|
||||
return kerrors.NewNotFound(kapi.Resource("deploymentconfig"), name)
|
||||
}
|
||||
|
||||
for _, rc := range deployments {
|
||||
if err = rcReaper.Stop(rc.Namespace, rc.Name, timeout, gracePeriod); err != nil {
|
||||
// Better not error out here...
|
||||
glog.Infof("Cannot delete ReplicationController %s/%s for deployment config %s/%s: %v", rc.Namespace, rc.Name, namespace, name, err)
|
||||
}
|
||||
|
||||
// Only remove deployer pods when the deployment was failed. For completed
|
||||
// deployment the pods should be already deleted.
|
||||
if !util.IsFailedDeployment(&rc) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Delete all deployer and hook pods
|
||||
options = kapi.ListOptions{LabelSelector: util.DeployerPodSelector(rc.Name)}
|
||||
podList, err := reaper.kc.Pods(rc.Namespace).List(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, pod := range podList.Items {
|
||||
err := reaper.kc.Pods(pod.Namespace).Delete(pod.Name, gracePeriod)
|
||||
if err != nil {
|
||||
// Better not error out here...
|
||||
glog.Infof("Cannot delete lifecycle Pod %s/%s for deployment config %s/%s: %v", pod.Namespace, pod.Name, namespace, name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Nothing to delete or we already deleted the deployment config because we
|
||||
// failed to pause.
|
||||
if configNotFound || legacy {
|
||||
return nil
|
||||
}
|
||||
|
||||
return reaper.oc.DeploymentConfigs(namespace).Delete(name)
|
||||
}
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
// Package cmd contains various interface implementations for command-line tools
|
||||
// associated with deploymentconfigs.
|
||||
package cmd
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
kapi "k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
|
||||
deployapi "github.com/openshift/origin/pkg/deploy/api"
|
||||
)
|
||||
|
||||
var basic = kubectl.BasicReplicationController{}
|
||||
|
||||
type BasicDeploymentConfigController struct{}
|
||||
|
||||
func (BasicDeploymentConfigController) ParamNames() []kubectl.GeneratorParam {
|
||||
return basic.ParamNames()
|
||||
}
|
||||
|
||||
func (BasicDeploymentConfigController) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
||||
obj, err := basic.Generate(genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch t := obj.(type) {
|
||||
case *kapi.ReplicationController:
|
||||
obj = &deployapi.DeploymentConfig{
|
||||
ObjectMeta: t.ObjectMeta,
|
||||
Spec: deployapi.DeploymentConfigSpec{
|
||||
Selector: t.Spec.Selector,
|
||||
Replicas: t.Spec.Replicas,
|
||||
Template: t.Spec.Template,
|
||||
},
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unrecognized object type: %v", reflect.TypeOf(t))
|
||||
}
|
||||
return obj, nil
|
||||
}
|
||||
-99
@@ -1,99 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sort"
|
||||
"text/tabwriter"
|
||||
|
||||
kapi "k8s.io/kubernetes/pkg/api"
|
||||
kclient "k8s.io/kubernetes/pkg/client/unversioned"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
|
||||
"github.com/openshift/origin/pkg/client"
|
||||
deployapi "github.com/openshift/origin/pkg/deploy/api"
|
||||
deployutil "github.com/openshift/origin/pkg/deploy/util"
|
||||
)
|
||||
|
||||
func NewDeploymentConfigHistoryViewer(oc client.Interface, kc kclient.Interface) kubectl.HistoryViewer {
|
||||
return &DeploymentConfigHistoryViewer{dn: oc, rn: kc}
|
||||
}
|
||||
|
||||
// DeploymentConfigHistoryViewer is an implementation of the kubectl HistoryViewer interface
|
||||
// for deployment configs.
|
||||
type DeploymentConfigHistoryViewer struct {
|
||||
rn kclient.ReplicationControllersNamespacer
|
||||
dn client.DeploymentConfigsNamespacer
|
||||
}
|
||||
|
||||
var _ kubectl.HistoryViewer = &DeploymentConfigHistoryViewer{}
|
||||
|
||||
// ViewHistory returns a description of all the history it can find for a deployment config.
|
||||
func (h *DeploymentConfigHistoryViewer) ViewHistory(namespace, name string, revision int64) (string, error) {
|
||||
opts := kapi.ListOptions{LabelSelector: deployutil.ConfigSelector(name)}
|
||||
deploymentList, err := h.rn.ReplicationControllers(namespace).List(opts)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
history := deploymentList.Items
|
||||
|
||||
if len(deploymentList.Items) == 0 {
|
||||
return "No rollout history found.", nil
|
||||
}
|
||||
|
||||
// Print details of a specific revision
|
||||
if revision > 0 {
|
||||
var desired *kapi.PodTemplateSpec
|
||||
// We could use a binary search here but brute-force is always faster to write
|
||||
for i := range history {
|
||||
rc := history[i]
|
||||
|
||||
if deployutil.DeploymentVersionFor(&rc) == revision {
|
||||
desired = rc.Spec.Template
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if desired == nil {
|
||||
return "", fmt.Errorf("unable to find the specified revision")
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
kubectl.DescribePodTemplate(desired, buf)
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
sort.Sort(deployutil.ByLatestVersionAsc(history))
|
||||
|
||||
return tabbedString(func(out *tabwriter.Writer) error {
|
||||
fmt.Fprintf(out, "REVISION\tSTATUS\tCAUSE\n")
|
||||
for i := range history {
|
||||
rc := history[i]
|
||||
|
||||
rev := deployutil.DeploymentVersionFor(&rc)
|
||||
status := deployutil.DeploymentStatusFor(&rc)
|
||||
cause := rc.Annotations[deployapi.DeploymentStatusReasonAnnotation]
|
||||
if len(cause) == 0 {
|
||||
cause = "<unknown>"
|
||||
}
|
||||
fmt.Fprintf(out, "%d\t%s\t%s\n", rev, status, cause)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: Re-use from an utility package
|
||||
func tabbedString(f func(*tabwriter.Writer) error) (string, error) {
|
||||
out := new(tabwriter.Writer)
|
||||
buf := &bytes.Buffer{}
|
||||
out.Init(buf, 0, 8, 1, '\t', 0)
|
||||
|
||||
err := f(out)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
out.Flush()
|
||||
str := string(buf.String())
|
||||
return str, nil
|
||||
}
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
|
||||
"github.com/openshift/origin/pkg/client"
|
||||
deployapi "github.com/openshift/origin/pkg/deploy/api"
|
||||
)
|
||||
|
||||
func NewDeploymentConfigRollbacker(oc client.Interface) kubectl.Rollbacker {
|
||||
return &DeploymentConfigRollbacker{dn: oc}
|
||||
}
|
||||
|
||||
// DeploymentConfigRollbacker is an implementation of the kubectl Rollbacker interface
|
||||
// for deployment configs.
|
||||
type DeploymentConfigRollbacker struct {
|
||||
dn client.DeploymentConfigsNamespacer
|
||||
}
|
||||
|
||||
var _ kubectl.Rollbacker = &DeploymentConfigRollbacker{}
|
||||
|
||||
// Rollback the provided deployment config to a specific revision. If revision is zero, we will
|
||||
// rollback to the previous deployment.
|
||||
func (r *DeploymentConfigRollbacker) Rollback(obj runtime.Object, updatedAnnotations map[string]string, toRevision int64) (string, error) {
|
||||
config, ok := obj.(*deployapi.DeploymentConfig)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("passed object is not a deployment config: %#v", obj)
|
||||
}
|
||||
if config.Spec.Paused {
|
||||
return "", fmt.Errorf("cannot rollback a paused config; resume it first with 'rollout resume dc/%s' and try again", config.Name)
|
||||
}
|
||||
|
||||
rollback := &deployapi.DeploymentConfigRollback{
|
||||
Name: config.Name,
|
||||
UpdatedAnnotations: updatedAnnotations,
|
||||
Spec: deployapi.DeploymentConfigRollbackSpec{
|
||||
Revision: toRevision,
|
||||
IncludeTemplate: true,
|
||||
},
|
||||
}
|
||||
|
||||
rolledback, err := r.dn.DeploymentConfigs(config.Namespace).Rollback(rollback)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
_, err = r.dn.DeploymentConfigs(config.Namespace).Update(rolledback)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return "rolled back", nil
|
||||
}
|
||||
-98
@@ -1,98 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
kapi "k8s.io/kubernetes/pkg/api"
|
||||
kclient "k8s.io/kubernetes/pkg/client/unversioned"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"k8s.io/kubernetes/pkg/util/wait"
|
||||
|
||||
"github.com/openshift/origin/pkg/client"
|
||||
"github.com/openshift/origin/pkg/deploy/util"
|
||||
)
|
||||
|
||||
// NewDeploymentConfigScaler returns a new scaler for deploymentConfigs
|
||||
func NewDeploymentConfigScaler(oc client.Interface, kc kclient.Interface) kubectl.Scaler {
|
||||
return &DeploymentConfigScaler{rcClient: kc, dcClient: oc, clientInterface: kc}
|
||||
}
|
||||
|
||||
// DeploymentConfigScaler is a wrapper for the kubectl Scaler client
|
||||
type DeploymentConfigScaler struct {
|
||||
rcClient kclient.ReplicationControllersNamespacer
|
||||
dcClient client.DeploymentConfigsNamespacer
|
||||
|
||||
clientInterface kclient.Interface
|
||||
}
|
||||
|
||||
// Scale updates the DeploymentConfig with the provided namespace/name, to a
|
||||
// new size, with optional precondition check (if preconditions is not nil),
|
||||
// optional retries (if retry is not nil), and then optionally waits for its
|
||||
// deployment replica count to reach the new value (if wait is not nil).
|
||||
func (scaler *DeploymentConfigScaler) Scale(namespace, name string, newSize uint, preconditions *kubectl.ScalePrecondition, retry, waitForReplicas *kubectl.RetryParams) error {
|
||||
if preconditions == nil {
|
||||
preconditions = &kubectl.ScalePrecondition{Size: -1, ResourceVersion: ""}
|
||||
}
|
||||
if retry == nil {
|
||||
// Make it try only once, immediately
|
||||
retry = &kubectl.RetryParams{Interval: time.Millisecond, Timeout: time.Millisecond}
|
||||
}
|
||||
cond := kubectl.ScaleCondition(scaler, preconditions, namespace, name, newSize, nil)
|
||||
if err := wait.Poll(retry.Interval, retry.Timeout, cond); err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO: convert to a watch and use resource version from the ScaleCondition - kubernetes/kubernetes#31051
|
||||
if waitForReplicas != nil {
|
||||
dc, err := scaler.dcClient.DeploymentConfigs(namespace).Get(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rc, err := scaler.rcClient.ReplicationControllers(namespace).Get(util.LatestDeploymentNameForConfig(dc))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return wait.Poll(waitForReplicas.Interval, waitForReplicas.Timeout, controllerHasSpecifiedReplicas(scaler.clientInterface, rc, dc.Spec.Replicas))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ScaleSimple does a simple one-shot attempt at scaling - not useful on its
|
||||
// own, but a necessary building block for Scale.
|
||||
func (scaler *DeploymentConfigScaler) ScaleSimple(namespace, name string, preconditions *kubectl.ScalePrecondition, newSize uint) (string, error) {
|
||||
scale, err := scaler.dcClient.DeploymentConfigs(namespace).GetScale(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
scale.Spec.Replicas = int32(newSize)
|
||||
updated, err := scaler.dcClient.DeploymentConfigs(namespace).UpdateScale(scale)
|
||||
if err != nil {
|
||||
return "", kubectl.ScaleError{FailureType: kubectl.ScaleUpdateFailure, ResourceVersion: "Unknown", ActualError: err}
|
||||
}
|
||||
return updated.ResourceVersion, nil
|
||||
}
|
||||
|
||||
// controllerHasSpecifiedReplicas returns a condition that will be true if and
|
||||
// only if the specified replica count for a controller's ReplicaSelector
|
||||
// equals the Replicas count.
|
||||
//
|
||||
// This is a slightly modified version of
|
||||
// unversioned.ControllerHasDesiredReplicas. This is necessary because when
|
||||
// scaling an RC via a DC, the RC spec replica count is not immediately
|
||||
// updated to match the owning DC.
|
||||
func controllerHasSpecifiedReplicas(c kclient.Interface, controller *kapi.ReplicationController, specifiedReplicas int32) wait.ConditionFunc {
|
||||
// If we're given a controller where the status lags the spec, it either means that the controller is stale,
|
||||
// or that the rc manager hasn't noticed the update yet. Polling status.Replicas is not safe in the latter case.
|
||||
desiredGeneration := controller.Generation
|
||||
|
||||
return func() (bool, error) {
|
||||
ctrl, err := c.ReplicationControllers(controller.Namespace).Get(controller.Name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
// There's a chance a concurrent update modifies the Spec.Replicas causing this check to pass,
|
||||
// or, after this check has passed, a modification causes the rc manager to create more pods.
|
||||
// This will not be an issue once we've implemented graceful delete for rcs, but till then
|
||||
// concurrent stop operations on the same rc might have unintended side effects.
|
||||
return ctrl.Status.ObservedGeneration >= desiredGeneration && ctrl.Status.Replicas == specifiedReplicas, nil
|
||||
}
|
||||
}
|
||||
-126
@@ -1,126 +0,0 @@
|
||||
package analysis
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gonum/graph"
|
||||
|
||||
osgraph "github.com/openshift/origin/pkg/api/graph"
|
||||
buildedges "github.com/openshift/origin/pkg/build/graph"
|
||||
buildutil "github.com/openshift/origin/pkg/build/util"
|
||||
deployedges "github.com/openshift/origin/pkg/deploy/graph"
|
||||
deploygraph "github.com/openshift/origin/pkg/deploy/graph/nodes"
|
||||
imageedges "github.com/openshift/origin/pkg/image/graph"
|
||||
imagegraph "github.com/openshift/origin/pkg/image/graph/nodes"
|
||||
)
|
||||
|
||||
const (
|
||||
MissingImageStreamErr = "MissingImageStream"
|
||||
MissingImageStreamTagWarning = "MissingImageStreamTag"
|
||||
MissingReadinessProbeWarning = "MissingReadinessProbe"
|
||||
)
|
||||
|
||||
// FindDeploymentConfigTriggerErrors checks for possible failures in deployment config
|
||||
// image change triggers.
|
||||
//
|
||||
// Precedence of failures:
|
||||
// 1. The image stream for the tag of interest does not exist.
|
||||
// 2. The image stream tag does not exist.
|
||||
func FindDeploymentConfigTriggerErrors(g osgraph.Graph, f osgraph.Namer) []osgraph.Marker {
|
||||
markers := []osgraph.Marker{}
|
||||
|
||||
for _, uncastDcNode := range g.NodesByKind(deploygraph.DeploymentConfigNodeKind) {
|
||||
dcNode := uncastDcNode.(*deploygraph.DeploymentConfigNode)
|
||||
marker := ictMarker(g, f, dcNode)
|
||||
if marker != nil {
|
||||
markers = append(markers, *marker)
|
||||
}
|
||||
}
|
||||
|
||||
return markers
|
||||
}
|
||||
|
||||
// ictMarker inspects the image change triggers for the provided deploymentconfig and returns
|
||||
// a marker in case of the following two scenarios:
|
||||
//
|
||||
// 1. The image stream pointed by the dc trigger doen not exist.
|
||||
// 2. The image stream tag pointed by the dc trigger does not exist and there is no build in
|
||||
// flight that could push to the tag.
|
||||
func ictMarker(g osgraph.Graph, f osgraph.Namer, dcNode *deploygraph.DeploymentConfigNode) *osgraph.Marker {
|
||||
for _, uncastIstNode := range g.PredecessorNodesByEdgeKind(dcNode, deployedges.TriggersDeploymentEdgeKind) {
|
||||
if istNode := uncastIstNode.(*imagegraph.ImageStreamTagNode); !istNode.Found() {
|
||||
// The image stream for the tag of interest does not exist.
|
||||
if isNode, exists := doesImageStreamExist(g, uncastIstNode); !exists {
|
||||
return &osgraph.Marker{
|
||||
Node: dcNode,
|
||||
RelatedNodes: []graph.Node{uncastIstNode, isNode},
|
||||
|
||||
Severity: osgraph.ErrorSeverity,
|
||||
Key: MissingImageStreamErr,
|
||||
Message: fmt.Sprintf("The image trigger for %s will have no effect because %s does not exist.",
|
||||
f.ResourceName(dcNode), f.ResourceName(isNode)),
|
||||
// TODO: Suggest `oc create imagestream` once we have that.
|
||||
}
|
||||
}
|
||||
|
||||
for _, bcNode := range buildedges.BuildConfigsForTag(g, istNode) {
|
||||
// Avoid warning for the dc image trigger in case there is a build in flight.
|
||||
if latestBuild := buildedges.GetLatestBuild(g, bcNode); latestBuild != nil && !buildutil.IsBuildComplete(latestBuild.Build) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// The image stream tag of interest does not exist.
|
||||
return &osgraph.Marker{
|
||||
Node: dcNode,
|
||||
RelatedNodes: []graph.Node{uncastIstNode},
|
||||
|
||||
Severity: osgraph.WarningSeverity,
|
||||
Key: MissingImageStreamTagWarning,
|
||||
Message: fmt.Sprintf("The image trigger for %s will have no effect until %s is imported or created by a build.",
|
||||
f.ResourceName(dcNode), f.ResourceName(istNode)),
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func doesImageStreamExist(g osgraph.Graph, istag graph.Node) (graph.Node, bool) {
|
||||
for _, imagestream := range g.SuccessorNodesByEdgeKind(istag, imageedges.ReferencedImageStreamGraphEdgeKind) {
|
||||
return imagestream, imagestream.(*imagegraph.ImageStreamNode).Found()
|
||||
}
|
||||
for _, imagestream := range g.SuccessorNodesByEdgeKind(istag, imageedges.ReferencedImageStreamImageGraphEdgeKind) {
|
||||
return imagestream, imagestream.(*imagegraph.ImageStreamNode).Found()
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// FindDeploymentConfigReadinessWarnings inspects deploymentconfigs and reports those that
|
||||
// don't have readiness probes set up.
|
||||
func FindDeploymentConfigReadinessWarnings(g osgraph.Graph, f osgraph.Namer, setProbeCommand string) []osgraph.Marker {
|
||||
markers := []osgraph.Marker{}
|
||||
|
||||
Node:
|
||||
for _, uncastDcNode := range g.NodesByKind(deploygraph.DeploymentConfigNodeKind) {
|
||||
dcNode := uncastDcNode.(*deploygraph.DeploymentConfigNode)
|
||||
if t := dcNode.DeploymentConfig.Spec.Template; t != nil && len(t.Spec.Containers) > 0 {
|
||||
for _, container := range t.Spec.Containers {
|
||||
if container.ReadinessProbe != nil {
|
||||
continue Node
|
||||
}
|
||||
}
|
||||
// All of the containers in the deployment config lack a readiness probe
|
||||
markers = append(markers, osgraph.Marker{
|
||||
Node: uncastDcNode,
|
||||
Severity: osgraph.WarningSeverity,
|
||||
Key: MissingReadinessProbeWarning,
|
||||
Message: fmt.Sprintf("%s has no readiness probe to verify pods are ready to accept traffic or ensure deployment is successful.",
|
||||
f.ResourceName(dcNode)),
|
||||
Suggestion: osgraph.Suggestion(fmt.Sprintf("%s %s --readiness ...", setProbeCommand, f.ResourceName(dcNode))),
|
||||
})
|
||||
continue Node
|
||||
}
|
||||
}
|
||||
|
||||
return markers
|
||||
}
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
// Package analysis provides functions that analyse deployment configurations and setup markers
|
||||
// that will be reported by oc status
|
||||
package analysis
|
||||
-85
@@ -1,85 +0,0 @@
|
||||
package graph
|
||||
|
||||
import (
|
||||
"github.com/gonum/graph"
|
||||
|
||||
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"
|
||||
imageapi "github.com/openshift/origin/pkg/image/api"
|
||||
imagegraph "github.com/openshift/origin/pkg/image/graph/nodes"
|
||||
)
|
||||
|
||||
const (
|
||||
// TriggersDeploymentEdgeKind points from DeploymentConfigs to ImageStreamTags that trigger the deployment
|
||||
TriggersDeploymentEdgeKind = "TriggersDeployment"
|
||||
// UsedInDeploymentEdgeKind points from DeploymentConfigs to DockerImageReferences that are used in the deployment
|
||||
UsedInDeploymentEdgeKind = "UsedInDeployment"
|
||||
// DeploymentEdgeKind points from DeploymentConfigs to the ReplicationControllers that are fulfilling the deployment
|
||||
DeploymentEdgeKind = "Deployment"
|
||||
)
|
||||
|
||||
// AddTriggerEdges creates edges that point to named Docker image repositories for each image used in the deployment.
|
||||
func AddTriggerEdges(g osgraph.MutableUniqueGraph, node *deploygraph.DeploymentConfigNode) *deploygraph.DeploymentConfigNode {
|
||||
podTemplate := node.DeploymentConfig.Spec.Template
|
||||
if podTemplate == nil {
|
||||
return node
|
||||
}
|
||||
|
||||
deployapi.EachTemplateImage(
|
||||
&podTemplate.Spec,
|
||||
deployapi.DeploymentConfigHasTrigger(node.DeploymentConfig),
|
||||
func(image deployapi.TemplateImage, err error) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if image.From != nil {
|
||||
if len(image.From.Name) == 0 {
|
||||
return
|
||||
}
|
||||
name, tag, _ := imageapi.SplitImageStreamTag(image.From.Name)
|
||||
in := imagegraph.FindOrCreateSyntheticImageStreamTagNode(g, imagegraph.MakeImageStreamTagObjectMeta(image.From.Namespace, name, tag))
|
||||
g.AddEdge(in, node, TriggersDeploymentEdgeKind)
|
||||
return
|
||||
}
|
||||
|
||||
tag := image.Ref.Tag
|
||||
image.Ref.Tag = ""
|
||||
in := imagegraph.EnsureDockerRepositoryNode(g, image.Ref.String(), tag)
|
||||
g.AddEdge(in, node, UsedInDeploymentEdgeKind)
|
||||
})
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
func AddAllTriggerEdges(g osgraph.MutableUniqueGraph) {
|
||||
for _, node := range g.(graph.Graph).Nodes() {
|
||||
if dcNode, ok := node.(*deploygraph.DeploymentConfigNode); ok {
|
||||
AddTriggerEdges(g, dcNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func AddDeploymentEdges(g osgraph.MutableUniqueGraph, node *deploygraph.DeploymentConfigNode) *deploygraph.DeploymentConfigNode {
|
||||
for _, n := range g.(graph.Graph).Nodes() {
|
||||
if rcNode, ok := n.(*kubegraph.ReplicationControllerNode); ok {
|
||||
if rcNode.ReplicationController.Namespace != node.DeploymentConfig.Namespace {
|
||||
continue
|
||||
}
|
||||
if BelongsToDeploymentConfig(node.DeploymentConfig, rcNode.ReplicationController) {
|
||||
g.AddEdge(node, rcNode, DeploymentEdgeKind)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
func AddAllDeploymentEdges(g osgraph.MutableUniqueGraph) {
|
||||
for _, node := range g.(graph.Graph).Nodes() {
|
||||
if dcNode, ok := node.(*deploygraph.DeploymentConfigNode); ok {
|
||||
AddDeploymentEdges(g, dcNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
package graph
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
kapi "k8s.io/kubernetes/pkg/api"
|
||||
|
||||
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"
|
||||
deployutil "github.com/openshift/origin/pkg/deploy/util"
|
||||
)
|
||||
|
||||
// RelevantDeployments returns the active deployment and a list of inactive deployments (in order from newest to oldest)
|
||||
func RelevantDeployments(g osgraph.Graph, dcNode *deploygraph.DeploymentConfigNode) (*kubegraph.ReplicationControllerNode, []*kubegraph.ReplicationControllerNode) {
|
||||
allDeployments := []*kubegraph.ReplicationControllerNode{}
|
||||
uncastDeployments := g.SuccessorNodesByEdgeKind(dcNode, DeploymentEdgeKind)
|
||||
if len(uncastDeployments) == 0 {
|
||||
return nil, []*kubegraph.ReplicationControllerNode{}
|
||||
}
|
||||
|
||||
for i := range uncastDeployments {
|
||||
allDeployments = append(allDeployments, uncastDeployments[i].(*kubegraph.ReplicationControllerNode))
|
||||
}
|
||||
|
||||
sort.Sort(RecentDeploymentReferences(allDeployments))
|
||||
|
||||
if dcNode.DeploymentConfig.Status.LatestVersion == deployutil.DeploymentVersionFor(allDeployments[0].ReplicationController) {
|
||||
return allDeployments[0], allDeployments[1:]
|
||||
}
|
||||
|
||||
return nil, allDeployments
|
||||
}
|
||||
|
||||
func BelongsToDeploymentConfig(config *deployapi.DeploymentConfig, b *kapi.ReplicationController) bool {
|
||||
if b.Annotations != nil {
|
||||
return config.Name == deployutil.DeploymentConfigNameFor(b)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type RecentDeploymentReferences []*kubegraph.ReplicationControllerNode
|
||||
|
||||
func (m RecentDeploymentReferences) Len() int { return len(m) }
|
||||
func (m RecentDeploymentReferences) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
|
||||
func (m RecentDeploymentReferences) Less(i, j int) bool {
|
||||
return deployutil.DeploymentVersionFor(m[i].ReplicationController) > deployutil.DeploymentVersionFor(m[j].ReplicationController)
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
package nodes
|
||||
|
||||
import (
|
||||
"github.com/gonum/graph"
|
||||
|
||||
osgraph "github.com/openshift/origin/pkg/api/graph"
|
||||
kubegraph "github.com/openshift/origin/pkg/api/kubegraph/nodes"
|
||||
depoyapi "github.com/openshift/origin/pkg/deploy/api"
|
||||
)
|
||||
|
||||
// EnsureDeploymentConfigNode adds the provided deployment config to the graph if it does not exist
|
||||
func EnsureDeploymentConfigNode(g osgraph.MutableUniqueGraph, dc *depoyapi.DeploymentConfig) *DeploymentConfigNode {
|
||||
dcName := DeploymentConfigNodeName(dc)
|
||||
dcNode := osgraph.EnsureUnique(
|
||||
g,
|
||||
dcName,
|
||||
func(node osgraph.Node) graph.Node {
|
||||
return &DeploymentConfigNode{Node: node, DeploymentConfig: dc, IsFound: true}
|
||||
},
|
||||
).(*DeploymentConfigNode)
|
||||
|
||||
if dc.Spec.Template != nil {
|
||||
podTemplateSpecNode := kubegraph.EnsurePodTemplateSpecNode(g, dc.Spec.Template, dc.Namespace, dcName)
|
||||
g.AddEdge(dcNode, podTemplateSpecNode, osgraph.ContainsEdgeKind)
|
||||
}
|
||||
|
||||
return dcNode
|
||||
}
|
||||
|
||||
func FindOrCreateSyntheticDeploymentConfigNode(g osgraph.MutableUniqueGraph, dc *depoyapi.DeploymentConfig) *DeploymentConfigNode {
|
||||
return osgraph.EnsureUnique(
|
||||
g,
|
||||
DeploymentConfigNodeName(dc),
|
||||
func(node osgraph.Node) graph.Node {
|
||||
return &DeploymentConfigNode{Node: node, DeploymentConfig: dc, IsFound: false}
|
||||
},
|
||||
).(*DeploymentConfigNode)
|
||||
}
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
package nodes
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
osgraph "github.com/openshift/origin/pkg/api/graph"
|
||||
deployapi "github.com/openshift/origin/pkg/deploy/api"
|
||||
)
|
||||
|
||||
var (
|
||||
DeploymentConfigNodeKind = reflect.TypeOf(deployapi.DeploymentConfig{}).Name()
|
||||
)
|
||||
|
||||
func DeploymentConfigNodeName(o *deployapi.DeploymentConfig) osgraph.UniqueName {
|
||||
return osgraph.GetUniqueRuntimeObjectNodeName(DeploymentConfigNodeKind, o)
|
||||
}
|
||||
|
||||
type DeploymentConfigNode struct {
|
||||
osgraph.Node
|
||||
DeploymentConfig *deployapi.DeploymentConfig
|
||||
|
||||
IsFound bool
|
||||
}
|
||||
|
||||
func (n DeploymentConfigNode) Found() bool {
|
||||
return n.IsFound
|
||||
}
|
||||
|
||||
func (n DeploymentConfigNode) Object() interface{} {
|
||||
return n.DeploymentConfig
|
||||
}
|
||||
|
||||
func (n DeploymentConfigNode) String() string {
|
||||
return string(DeploymentConfigNodeName(n.DeploymentConfig))
|
||||
}
|
||||
|
||||
func (*DeploymentConfigNode) Kind() string {
|
||||
return DeploymentConfigNodeKind
|
||||
}
|
||||
-496
@@ -1,496 +0,0 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
kdeplutil "k8s.io/kubernetes/pkg/controller/deployment/util"
|
||||
"k8s.io/kubernetes/pkg/fields"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/watch"
|
||||
|
||||
deployapi "github.com/openshift/origin/pkg/deploy/api"
|
||||
"github.com/openshift/origin/pkg/util/namer"
|
||||
kclient "k8s.io/kubernetes/pkg/client/unversioned"
|
||||
)
|
||||
|
||||
// LatestDeploymentNameForConfig returns a stable identifier for config based on its version.
|
||||
func LatestDeploymentNameForConfig(config *deployapi.DeploymentConfig) string {
|
||||
return fmt.Sprintf("%s-%d", config.Name, config.Status.LatestVersion)
|
||||
}
|
||||
|
||||
// LatestDeploymentInfo returns info about the latest deployment for a config,
|
||||
// or nil if there is no latest deployment. The latest deployment is not
|
||||
// always the same as the active deployment.
|
||||
func LatestDeploymentInfo(config *deployapi.DeploymentConfig, deployments []api.ReplicationController) (bool, *api.ReplicationController) {
|
||||
if config.Status.LatestVersion == 0 || len(deployments) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
sort.Sort(ByLatestVersionDesc(deployments))
|
||||
candidate := &deployments[0]
|
||||
return DeploymentVersionFor(candidate) == config.Status.LatestVersion, candidate
|
||||
}
|
||||
|
||||
// ActiveDeployment returns the latest complete deployment, or nil if there is
|
||||
// no such deployment. The active deployment is not always the same as the
|
||||
// latest deployment.
|
||||
func ActiveDeployment(config *deployapi.DeploymentConfig, input []api.ReplicationController) *api.ReplicationController {
|
||||
var activeDeployment *api.ReplicationController
|
||||
var lastCompleteDeploymentVersion int64 = 0
|
||||
for i := range input {
|
||||
deployment := &input[i]
|
||||
deploymentVersion := DeploymentVersionFor(deployment)
|
||||
if DeploymentStatusFor(deployment) == deployapi.DeploymentStatusComplete && deploymentVersion > lastCompleteDeploymentVersion {
|
||||
activeDeployment = deployment
|
||||
lastCompleteDeploymentVersion = deploymentVersion
|
||||
}
|
||||
}
|
||||
return activeDeployment
|
||||
}
|
||||
|
||||
// DeployerPodSuffix is the suffix added to pods created from a deployment
|
||||
const DeployerPodSuffix = "deploy"
|
||||
|
||||
// DeployerPodNameForDeployment returns the name of a pod for a given deployment
|
||||
func DeployerPodNameForDeployment(deployment string) string {
|
||||
return namer.GetPodName(deployment, DeployerPodSuffix)
|
||||
}
|
||||
|
||||
// LabelForDeployment builds a string identifier for a Deployment.
|
||||
func LabelForDeployment(deployment *api.ReplicationController) string {
|
||||
return fmt.Sprintf("%s/%s", deployment.Namespace, deployment.Name)
|
||||
}
|
||||
|
||||
// LabelForDeploymentConfig builds a string identifier for a DeploymentConfig.
|
||||
func LabelForDeploymentConfig(config *deployapi.DeploymentConfig) string {
|
||||
return fmt.Sprintf("%s/%s", config.Namespace, config.Name)
|
||||
}
|
||||
|
||||
// DeploymentNameForConfigVersion returns the name of the version-th deployment
|
||||
// for the config that has the provided name
|
||||
func DeploymentNameForConfigVersion(name string, version int64) string {
|
||||
return fmt.Sprintf("%s-%d", name, version)
|
||||
}
|
||||
|
||||
// ConfigSelector returns a label Selector which can be used to find all
|
||||
// deployments for a DeploymentConfig.
|
||||
//
|
||||
// TODO: Using the annotation constant for now since the value is correct
|
||||
// but we could consider adding a new constant to the public types.
|
||||
func ConfigSelector(name string) labels.Selector {
|
||||
return labels.Set{deployapi.DeploymentConfigAnnotation: name}.AsSelector()
|
||||
}
|
||||
|
||||
// DeployerPodSelector returns a label Selector which can be used to find all
|
||||
// deployer pods associated with a deployment with name.
|
||||
func DeployerPodSelector(name string) labels.Selector {
|
||||
return labels.Set{deployapi.DeployerPodForDeploymentLabel: name}.AsSelector()
|
||||
}
|
||||
|
||||
// AnyDeployerPodSelector returns a label Selector which can be used to find
|
||||
// all deployer pods across all deployments, including hook and custom
|
||||
// deployer pods.
|
||||
func AnyDeployerPodSelector() labels.Selector {
|
||||
sel, _ := labels.Parse(deployapi.DeployerPodForDeploymentLabel)
|
||||
return sel
|
||||
}
|
||||
|
||||
// HasChangeTrigger returns whether the provided deployment configuration has
|
||||
// a config change trigger or not
|
||||
func HasChangeTrigger(config *deployapi.DeploymentConfig) bool {
|
||||
for _, trigger := range config.Spec.Triggers {
|
||||
if trigger.Type == deployapi.DeploymentTriggerOnConfigChange {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func DeploymentConfigDeepCopy(dc *deployapi.DeploymentConfig) (*deployapi.DeploymentConfig, error) {
|
||||
objCopy, err := api.Scheme.DeepCopy(dc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copied, ok := objCopy.(*deployapi.DeploymentConfig)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected DeploymentConfig, got %#v", objCopy)
|
||||
}
|
||||
return copied, nil
|
||||
}
|
||||
|
||||
func DeploymentDeepCopy(rc *api.ReplicationController) (*api.ReplicationController, error) {
|
||||
objCopy, err := api.Scheme.DeepCopy(rc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copied, ok := objCopy.(*api.ReplicationController)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected ReplicationController, got %#v", objCopy)
|
||||
}
|
||||
return copied, nil
|
||||
}
|
||||
|
||||
// DecodeDeploymentConfig decodes a DeploymentConfig from controller using codec. An error is returned
|
||||
// if the controller doesn't contain an encoded config.
|
||||
func DecodeDeploymentConfig(controller *api.ReplicationController, decoder runtime.Decoder) (*deployapi.DeploymentConfig, error) {
|
||||
encodedConfig := []byte(EncodedDeploymentConfigFor(controller))
|
||||
decoded, err := runtime.Decode(decoder, encodedConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode DeploymentConfig from controller: %v", err)
|
||||
}
|
||||
config, ok := decoded.(*deployapi.DeploymentConfig)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("decoded object from controller is not a DeploymentConfig")
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// EncodeDeploymentConfig encodes config as a string using codec.
|
||||
func EncodeDeploymentConfig(config *deployapi.DeploymentConfig, codec runtime.Codec) (string, error) {
|
||||
bytes, err := runtime.Encode(codec, config)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(bytes[:]), nil
|
||||
}
|
||||
|
||||
// MakeDeployment creates a deployment represented as a ReplicationController and based on the given
|
||||
// DeploymentConfig. The controller replica count will be zero.
|
||||
func MakeDeployment(config *deployapi.DeploymentConfig, codec runtime.Codec) (*api.ReplicationController, error) {
|
||||
var err error
|
||||
var encodedConfig string
|
||||
|
||||
if encodedConfig, err = EncodeDeploymentConfig(config, codec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deploymentName := LatestDeploymentNameForConfig(config)
|
||||
|
||||
podSpec := api.PodSpec{}
|
||||
if err := api.Scheme.Convert(&config.Spec.Template.Spec, &podSpec, nil); err != nil {
|
||||
return nil, fmt.Errorf("couldn't clone podSpec: %v", err)
|
||||
}
|
||||
|
||||
controllerLabels := make(labels.Set)
|
||||
for k, v := range config.Labels {
|
||||
controllerLabels[k] = v
|
||||
}
|
||||
// Correlate the deployment with the config.
|
||||
// TODO: Using the annotation constant for now since the value is correct
|
||||
// but we could consider adding a new constant to the public types.
|
||||
controllerLabels[deployapi.DeploymentConfigAnnotation] = config.Name
|
||||
|
||||
// Ensure that pods created by this deployment controller can be safely associated back
|
||||
// to the controller, and that multiple deployment controllers for the same config don't
|
||||
// manipulate each others' pods.
|
||||
selector := map[string]string{}
|
||||
for k, v := range config.Spec.Selector {
|
||||
selector[k] = v
|
||||
}
|
||||
selector[deployapi.DeploymentConfigLabel] = config.Name
|
||||
selector[deployapi.DeploymentLabel] = deploymentName
|
||||
|
||||
podLabels := make(labels.Set)
|
||||
for k, v := range config.Spec.Template.Labels {
|
||||
podLabels[k] = v
|
||||
}
|
||||
podLabels[deployapi.DeploymentConfigLabel] = config.Name
|
||||
podLabels[deployapi.DeploymentLabel] = deploymentName
|
||||
|
||||
podAnnotations := make(labels.Set)
|
||||
for k, v := range config.Spec.Template.Annotations {
|
||||
podAnnotations[k] = v
|
||||
}
|
||||
podAnnotations[deployapi.DeploymentAnnotation] = deploymentName
|
||||
podAnnotations[deployapi.DeploymentConfigAnnotation] = config.Name
|
||||
podAnnotations[deployapi.DeploymentVersionAnnotation] = strconv.FormatInt(config.Status.LatestVersion, 10)
|
||||
|
||||
deployment := &api.ReplicationController{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: deploymentName,
|
||||
Namespace: config.Namespace,
|
||||
Annotations: map[string]string{
|
||||
deployapi.DeploymentConfigAnnotation: config.Name,
|
||||
deployapi.DeploymentStatusAnnotation: string(deployapi.DeploymentStatusNew),
|
||||
deployapi.DeploymentEncodedConfigAnnotation: encodedConfig,
|
||||
deployapi.DeploymentVersionAnnotation: strconv.FormatInt(config.Status.LatestVersion, 10),
|
||||
// This is the target replica count for the new deployment.
|
||||
deployapi.DesiredReplicasAnnotation: strconv.Itoa(int(config.Spec.Replicas)),
|
||||
deployapi.DeploymentReplicasAnnotation: strconv.Itoa(0),
|
||||
},
|
||||
Labels: controllerLabels,
|
||||
},
|
||||
Spec: api.ReplicationControllerSpec{
|
||||
// The deployment should be inactive initially
|
||||
Replicas: 0,
|
||||
Selector: selector,
|
||||
Template: &api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: podLabels,
|
||||
Annotations: podAnnotations,
|
||||
},
|
||||
Spec: podSpec,
|
||||
},
|
||||
},
|
||||
}
|
||||
if config.Status.Details != nil && len(config.Status.Details.Message) > 0 {
|
||||
deployment.Annotations[deployapi.DeploymentStatusReasonAnnotation] = config.Status.Details.Message
|
||||
}
|
||||
if value, ok := config.Annotations[deployapi.DeploymentIgnorePodAnnotation]; ok {
|
||||
deployment.Annotations[deployapi.DeploymentIgnorePodAnnotation] = value
|
||||
}
|
||||
|
||||
return deployment, nil
|
||||
}
|
||||
|
||||
// GetReplicaCountForDeployments returns the sum of all replicas for the
|
||||
// given deployments.
|
||||
func GetReplicaCountForDeployments(deployments []api.ReplicationController) int32 {
|
||||
totalReplicaCount := int32(0)
|
||||
for _, deployment := range deployments {
|
||||
totalReplicaCount += deployment.Spec.Replicas
|
||||
}
|
||||
return totalReplicaCount
|
||||
}
|
||||
|
||||
// GetStatusReplicaCountForDeployments returns the sum of the replicas reported in the
|
||||
// status of the given deployments.
|
||||
func GetStatusReplicaCountForDeployments(deployments []api.ReplicationController) int32 {
|
||||
totalReplicaCount := int32(0)
|
||||
for _, deployment := range deployments {
|
||||
totalReplicaCount += deployment.Status.Replicas
|
||||
}
|
||||
return totalReplicaCount
|
||||
}
|
||||
|
||||
// GetAvailablePods returns all the available pods from the provided pod list.
|
||||
func GetAvailablePods(pods []*api.Pod, minReadySeconds int32) int32 {
|
||||
available := int32(0)
|
||||
for i := range pods {
|
||||
pod := pods[i]
|
||||
if kdeplutil.IsPodAvailable(pod, minReadySeconds, time.Now()) {
|
||||
available++
|
||||
}
|
||||
}
|
||||
return available
|
||||
}
|
||||
|
||||
func DeploymentConfigNameFor(obj runtime.Object) string {
|
||||
return annotationFor(obj, deployapi.DeploymentConfigAnnotation)
|
||||
}
|
||||
|
||||
func DeploymentNameFor(obj runtime.Object) string {
|
||||
return annotationFor(obj, deployapi.DeploymentAnnotation)
|
||||
}
|
||||
|
||||
func DeployerPodNameFor(obj runtime.Object) string {
|
||||
return annotationFor(obj, deployapi.DeploymentPodAnnotation)
|
||||
}
|
||||
|
||||
func DeploymentStatusFor(obj runtime.Object) deployapi.DeploymentStatus {
|
||||
return deployapi.DeploymentStatus(annotationFor(obj, deployapi.DeploymentStatusAnnotation))
|
||||
}
|
||||
|
||||
func DeploymentStatusReasonFor(obj runtime.Object) string {
|
||||
return annotationFor(obj, deployapi.DeploymentStatusReasonAnnotation)
|
||||
}
|
||||
|
||||
func DeploymentDesiredReplicas(obj runtime.Object) (int32, bool) {
|
||||
return int32AnnotationFor(obj, deployapi.DesiredReplicasAnnotation)
|
||||
}
|
||||
|
||||
func DeploymentReplicas(obj runtime.Object) (int32, bool) {
|
||||
return int32AnnotationFor(obj, deployapi.DeploymentReplicasAnnotation)
|
||||
}
|
||||
|
||||
func EncodedDeploymentConfigFor(obj runtime.Object) string {
|
||||
return annotationFor(obj, deployapi.DeploymentEncodedConfigAnnotation)
|
||||
}
|
||||
|
||||
func DeploymentVersionFor(obj runtime.Object) int64 {
|
||||
v, err := strconv.ParseInt(annotationFor(obj, deployapi.DeploymentVersionAnnotation), 10, 64)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func IsDeploymentCancelled(deployment *api.ReplicationController) bool {
|
||||
value := annotationFor(deployment, deployapi.DeploymentCancelledAnnotation)
|
||||
return strings.EqualFold(value, deployapi.DeploymentCancelledAnnotationValue)
|
||||
}
|
||||
|
||||
func HasSynced(dc *deployapi.DeploymentConfig) bool {
|
||||
return dc.Status.ObservedGeneration >= dc.Generation
|
||||
}
|
||||
|
||||
// IsOwnedByConfig checks whether the provided replication controller is part of a
|
||||
// deployment configuration.
|
||||
// TODO: Switch to use owner references once we got those working.
|
||||
func IsOwnedByConfig(deployment *api.ReplicationController) bool {
|
||||
_, ok := deployment.Annotations[deployapi.DeploymentConfigAnnotation]
|
||||
return ok
|
||||
}
|
||||
|
||||
// IsTerminatedDeployment returns true if the passed deployment has terminated (either
|
||||
// complete or failed).
|
||||
func IsTerminatedDeployment(deployment *api.ReplicationController) bool {
|
||||
current := DeploymentStatusFor(deployment)
|
||||
return current == deployapi.DeploymentStatusComplete || current == deployapi.DeploymentStatusFailed
|
||||
}
|
||||
|
||||
// IsFailedDeployment returns true if the passed deployment failed.
|
||||
func IsFailedDeployment(deployment *api.ReplicationController) bool {
|
||||
current := DeploymentStatusFor(deployment)
|
||||
return current == deployapi.DeploymentStatusFailed
|
||||
}
|
||||
|
||||
// CanTransitionPhase returns whether it is allowed to go from the current to the next phase.
|
||||
func CanTransitionPhase(current, next deployapi.DeploymentStatus) bool {
|
||||
switch current {
|
||||
case deployapi.DeploymentStatusNew:
|
||||
switch next {
|
||||
case deployapi.DeploymentStatusPending,
|
||||
deployapi.DeploymentStatusRunning,
|
||||
deployapi.DeploymentStatusFailed,
|
||||
deployapi.DeploymentStatusComplete:
|
||||
return true
|
||||
}
|
||||
case deployapi.DeploymentStatusPending:
|
||||
switch next {
|
||||
case deployapi.DeploymentStatusRunning,
|
||||
deployapi.DeploymentStatusFailed,
|
||||
deployapi.DeploymentStatusComplete:
|
||||
return true
|
||||
}
|
||||
case deployapi.DeploymentStatusRunning:
|
||||
switch next {
|
||||
case deployapi.DeploymentStatusFailed, deployapi.DeploymentStatusComplete:
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// annotationFor returns the annotation with key for obj.
|
||||
func annotationFor(obj runtime.Object, key string) string {
|
||||
meta, err := api.ObjectMetaFor(obj)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return meta.Annotations[key]
|
||||
}
|
||||
|
||||
func int32AnnotationFor(obj runtime.Object, key string) (int32, bool) {
|
||||
s := annotationFor(obj, key)
|
||||
if len(s) == 0 {
|
||||
return 0, false
|
||||
}
|
||||
i, err := strconv.ParseInt(s, 10, 32)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
return int32(i), true
|
||||
}
|
||||
|
||||
// DeploymentsForCleanup determines which deployments for a configuration are relevant for the
|
||||
// revision history limit quota
|
||||
func DeploymentsForCleanup(configuration *deployapi.DeploymentConfig, deployments []api.ReplicationController) []api.ReplicationController {
|
||||
// if the past deployment quota has been exceeded, we need to prune the oldest deployments
|
||||
// until we are not exceeding the quota any longer, so we sort oldest first
|
||||
sort.Sort(ByLatestVersionAsc(deployments))
|
||||
|
||||
relevantDeployments := []api.ReplicationController{}
|
||||
activeDeployment := ActiveDeployment(configuration, deployments)
|
||||
if activeDeployment == nil {
|
||||
// if cleanup policy is set but no successful deployments have happened, there will be
|
||||
// no active deployment. We can consider all of the deployments in this case except for
|
||||
// the latest one
|
||||
for i := range deployments {
|
||||
deployment := &deployments[i]
|
||||
if DeploymentVersionFor(deployment) != configuration.Status.LatestVersion {
|
||||
relevantDeployments = append(relevantDeployments, *deployment)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// if there is an active deployment, we need to filter out any deployments that we don't
|
||||
// care about, namely the active deployment and any newer deployments
|
||||
for i := range deployments {
|
||||
deployment := &deployments[i]
|
||||
if deployment != activeDeployment && DeploymentVersionFor(deployment) < DeploymentVersionFor(activeDeployment) {
|
||||
relevantDeployments = append(relevantDeployments, *deployment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return relevantDeployments
|
||||
}
|
||||
|
||||
// WaitForRunningDeployerPod waits a given period of time until the deployer pod
|
||||
// for given replication controller is not running.
|
||||
func WaitForRunningDeployerPod(podClient kclient.PodsNamespacer, rc *api.ReplicationController, timeout time.Duration) error {
|
||||
podName := DeployerPodNameForDeployment(rc.Name)
|
||||
canGetLogs := func(p *api.Pod) bool {
|
||||
return api.PodSucceeded == p.Status.Phase || api.PodFailed == p.Status.Phase || api.PodRunning == p.Status.Phase
|
||||
}
|
||||
pod, err := podClient.Pods(rc.Namespace).Get(podName)
|
||||
if err == nil && canGetLogs(pod) {
|
||||
return nil
|
||||
}
|
||||
watcher, err := podClient.Pods(rc.Namespace).Watch(
|
||||
api.ListOptions{
|
||||
FieldSelector: fields.Set{"metadata.name": podName}.AsSelector(),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer watcher.Stop()
|
||||
if _, err := watch.Until(timeout, watcher, func(e watch.Event) (bool, error) {
|
||||
if e.Type == watch.Error {
|
||||
return false, fmt.Errorf("encountered error while watching for pod: %v", e.Object)
|
||||
}
|
||||
obj, isPod := e.Object.(*api.Pod)
|
||||
if !isPod {
|
||||
return false, errors.New("received unknown object while watching for pods")
|
||||
}
|
||||
return canGetLogs(obj), nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ByLatestVersionAsc sorts deployments by LatestVersion ascending.
|
||||
type ByLatestVersionAsc []api.ReplicationController
|
||||
|
||||
func (d ByLatestVersionAsc) Len() int { return len(d) }
|
||||
func (d ByLatestVersionAsc) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
|
||||
func (d ByLatestVersionAsc) Less(i, j int) bool {
|
||||
return DeploymentVersionFor(&d[i]) < DeploymentVersionFor(&d[j])
|
||||
}
|
||||
|
||||
// ByLatestVersionDesc sorts deployments by LatestVersion descending.
|
||||
type ByLatestVersionDesc []api.ReplicationController
|
||||
|
||||
func (d ByLatestVersionDesc) Len() int { return len(d) }
|
||||
func (d ByLatestVersionDesc) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
|
||||
func (d ByLatestVersionDesc) Less(i, j int) bool {
|
||||
return DeploymentVersionFor(&d[j]) < DeploymentVersionFor(&d[i])
|
||||
}
|
||||
|
||||
// ByMostRecent sorts deployments by most recently created.
|
||||
type ByMostRecent []*api.ReplicationController
|
||||
|
||||
func (s ByMostRecent) Len() int { return len(s) }
|
||||
func (s ByMostRecent) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
func (s ByMostRecent) Less(i, j int) bool {
|
||||
return !s[i].CreationTimestamp.Before(s[j].CreationTimestamp)
|
||||
}
|
||||
+11
-79
@@ -18,6 +18,8 @@ import (
|
||||
"github.com/docker/distribution/manifest/schema1"
|
||||
"github.com/docker/distribution/manifest/schema2"
|
||||
"github.com/golang/glog"
|
||||
|
||||
"github.com/openshift/origin/pkg/image/reference"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -48,24 +50,6 @@ func (fn DefaultRegistryFunc) DefaultRegistry() (string, bool) {
|
||||
return fn()
|
||||
}
|
||||
|
||||
// parseRepositoryTag splits a string into its name component and either tag or id if present.
|
||||
// TODO remove
|
||||
func parseRepositoryTag(repos string) (base string, tag string, id string) {
|
||||
n := strings.Index(repos, "@")
|
||||
if n >= 0 {
|
||||
parts := strings.Split(repos, "@")
|
||||
return parts[0], "", parts[1]
|
||||
}
|
||||
n = strings.LastIndex(repos, ":")
|
||||
if n < 0 {
|
||||
return repos, "", ""
|
||||
}
|
||||
if tag := repos[n+1:]; !strings.Contains(tag, "/") {
|
||||
return repos[:n], tag, ""
|
||||
}
|
||||
return repos, "", ""
|
||||
}
|
||||
|
||||
// ParseImageStreamImageName splits a string into its name component and ID component, and returns an error
|
||||
// if the string is not in the right form.
|
||||
func ParseImageStreamImageName(input string) (name string, id string, err error) {
|
||||
@@ -109,16 +93,6 @@ func MakeImageStreamImageName(name, id string) string {
|
||||
return fmt.Sprintf("%s@%s", name, id)
|
||||
}
|
||||
|
||||
func isRegistryName(str string) bool {
|
||||
switch {
|
||||
case strings.Contains(str, ":"),
|
||||
strings.Contains(str, "."),
|
||||
str == "localhost":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsRegistryDockerHub returns true if the given registry name belongs to
|
||||
// Docker hub.
|
||||
func IsRegistryDockerHub(registry string) bool {
|
||||
@@ -134,60 +108,18 @@ func IsRegistryDockerHub(registry string) bool {
|
||||
// DockerImageReference.
|
||||
func ParseDockerImageReference(spec string) (DockerImageReference, error) {
|
||||
var ref DockerImageReference
|
||||
// TODO replace with docker version once docker/docker PR11109 is merged upstream
|
||||
stream, tag, id := parseRepositoryTag(spec)
|
||||
|
||||
repoParts := strings.Split(stream, "/")
|
||||
switch len(repoParts) {
|
||||
case 2:
|
||||
if isRegistryName(repoParts[0]) {
|
||||
// registry/name
|
||||
ref.Registry = repoParts[0]
|
||||
if IsRegistryDockerHub(ref.Registry) {
|
||||
ref.Namespace = DockerDefaultNamespace
|
||||
}
|
||||
if len(repoParts[1]) == 0 {
|
||||
return ref, fmt.Errorf("the docker pull spec %q must be two or three segments separated by slashes", spec)
|
||||
}
|
||||
ref.Name = repoParts[1]
|
||||
ref.Tag = tag
|
||||
ref.ID = id
|
||||
break
|
||||
}
|
||||
// namespace/name
|
||||
ref.Namespace = repoParts[0]
|
||||
if len(repoParts[1]) == 0 {
|
||||
return ref, fmt.Errorf("the docker pull spec %q must be two or three segments separated by slashes", spec)
|
||||
}
|
||||
ref.Name = repoParts[1]
|
||||
ref.Tag = tag
|
||||
ref.ID = id
|
||||
break
|
||||
case 3:
|
||||
// registry/namespace/name
|
||||
ref.Registry = repoParts[0]
|
||||
ref.Namespace = repoParts[1]
|
||||
if len(repoParts[2]) == 0 {
|
||||
return ref, fmt.Errorf("the docker pull spec %q must be two or three segments separated by slashes", spec)
|
||||
}
|
||||
ref.Name = repoParts[2]
|
||||
ref.Tag = tag
|
||||
ref.ID = id
|
||||
break
|
||||
case 1:
|
||||
// name
|
||||
if len(repoParts[0]) == 0 {
|
||||
return ref, fmt.Errorf("the docker pull spec %q must be two or three segments separated by slashes", spec)
|
||||
}
|
||||
ref.Name = repoParts[0]
|
||||
ref.Tag = tag
|
||||
ref.ID = id
|
||||
break
|
||||
default:
|
||||
// TODO: this is no longer true with V2
|
||||
return ref, fmt.Errorf("the docker pull spec %q must be two or three segments separated by slashes", spec)
|
||||
namedRef, err := reference.ParseNamedDockerImageReference(spec)
|
||||
if err != nil {
|
||||
return ref, err
|
||||
}
|
||||
|
||||
ref.Registry = namedRef.Registry
|
||||
ref.Namespace = namedRef.Namespace
|
||||
ref.Name = namedRef.Name
|
||||
ref.Tag = namedRef.Tag
|
||||
ref.ID = namedRef.ID
|
||||
|
||||
return ref, nil
|
||||
}
|
||||
|
||||
|
||||
-435
@@ -1,435 +0,0 @@
|
||||
|
||||
// This file was autogenerated by go-to-protobuf. Do not edit it manually!
|
||||
|
||||
syntax = 'proto2';
|
||||
|
||||
package github.com.openshift.origin.pkg.image.api.v1;
|
||||
|
||||
import "k8s.io/kubernetes/pkg/api/unversioned/generated.proto";
|
||||
import "k8s.io/kubernetes/pkg/api/v1/generated.proto";
|
||||
import "k8s.io/kubernetes/pkg/runtime/generated.proto";
|
||||
import "k8s.io/kubernetes/pkg/util/intstr/generated.proto";
|
||||
|
||||
// Package-wide variables from generator "generated".
|
||||
option go_package = "v1";
|
||||
|
||||
// DockerImageReference points to a Docker image.
|
||||
message DockerImageReference {
|
||||
// Registry is the registry that contains the Docker image
|
||||
optional string registry = 1;
|
||||
|
||||
// Namespace is the namespace that contains the Docker image
|
||||
optional string namespace = 2;
|
||||
|
||||
// Name is the name of the Docker image
|
||||
optional string name = 3;
|
||||
|
||||
// Tag is which tag of the Docker image is being referenced
|
||||
optional string tag = 4;
|
||||
|
||||
// ID is the identifier for the Docker image
|
||||
optional string iD = 5;
|
||||
}
|
||||
|
||||
// Image is an immutable representation of a Docker image and metadata at a point in time.
|
||||
message Image {
|
||||
// Standard object's metadata.
|
||||
optional k8s.io.kubernetes.pkg.api.v1.ObjectMeta metadata = 1;
|
||||
|
||||
// DockerImageReference is the string that can be used to pull this image.
|
||||
optional string dockerImageReference = 2;
|
||||
|
||||
// DockerImageMetadata contains metadata about this image
|
||||
optional k8s.io.kubernetes.pkg.runtime.RawExtension dockerImageMetadata = 3;
|
||||
|
||||
// DockerImageMetadataVersion conveys the version of the object, which if empty defaults to "1.0"
|
||||
optional string dockerImageMetadataVersion = 4;
|
||||
|
||||
// DockerImageManifest is the raw JSON of the manifest
|
||||
optional string dockerImageManifest = 5;
|
||||
|
||||
// DockerImageLayers represents the layers in the image. May not be set if the image does not define that data.
|
||||
repeated ImageLayer dockerImageLayers = 6;
|
||||
|
||||
// Signatures holds all signatures of the image.
|
||||
repeated ImageSignature signatures = 7;
|
||||
|
||||
// DockerImageSignatures provides the signatures as opaque blobs. This is a part of manifest schema v1.
|
||||
repeated bytes dockerImageSignatures = 8;
|
||||
|
||||
// DockerImageManifestMediaType specifies the mediaType of manifest. This is a part of manifest schema v2.
|
||||
optional string dockerImageManifestMediaType = 9;
|
||||
|
||||
// DockerImageConfig is a JSON blob that the runtime uses to set up the container. This is a part of manifest schema v2.
|
||||
optional string dockerImageConfig = 10;
|
||||
}
|
||||
|
||||
// ImageImportSpec describes a request to import a specific image.
|
||||
message ImageImportSpec {
|
||||
// From is the source of an image to import; only kind DockerImage is allowed
|
||||
optional k8s.io.kubernetes.pkg.api.v1.ObjectReference from = 1;
|
||||
|
||||
// To is a tag in the current image stream to assign the imported image to, if name is not specified the default tag from from.name will be used
|
||||
optional k8s.io.kubernetes.pkg.api.v1.LocalObjectReference to = 2;
|
||||
|
||||
// ImportPolicy is the policy controlling how the image is imported
|
||||
optional TagImportPolicy importPolicy = 3;
|
||||
|
||||
// IncludeManifest determines if the manifest for each image is returned in the response
|
||||
optional bool includeManifest = 4;
|
||||
}
|
||||
|
||||
// ImageImportStatus describes the result of an image import.
|
||||
message ImageImportStatus {
|
||||
// Status is the status of the image import, including errors encountered while retrieving the image
|
||||
optional k8s.io.kubernetes.pkg.api.unversioned.Status status = 1;
|
||||
|
||||
// Image is the metadata of that image, if the image was located
|
||||
optional Image image = 2;
|
||||
|
||||
// Tag is the tag this image was located under, if any
|
||||
optional string tag = 3;
|
||||
}
|
||||
|
||||
// ImageLayer represents a single layer of the image. Some images may have multiple layers. Some may have none.
|
||||
message ImageLayer {
|
||||
// Name of the layer as defined by the underlying store.
|
||||
optional string name = 1;
|
||||
|
||||
// Size of the layer in bytes as defined by the underlying store.
|
||||
optional int64 size = 2;
|
||||
|
||||
// MediaType of the referenced object.
|
||||
optional string mediaType = 3;
|
||||
}
|
||||
|
||||
// ImageList is a list of Image objects.
|
||||
message ImageList {
|
||||
// Standard object's metadata.
|
||||
optional k8s.io.kubernetes.pkg.api.unversioned.ListMeta metadata = 1;
|
||||
|
||||
// Items is a list of images
|
||||
repeated Image items = 2;
|
||||
}
|
||||
|
||||
// ImageSignature holds a signature of an image. It allows to verify image identity and possibly other claims
|
||||
// as long as the signature is trusted. Based on this information it is possible to restrict runnable images
|
||||
// to those matching cluster-wide policy.
|
||||
// Mandatory fields should be parsed by clients doing image verification. The others are parsed from
|
||||
// signature's content by the server. They serve just an informative purpose.
|
||||
message ImageSignature {
|
||||
// Standard object's metadata.
|
||||
optional k8s.io.kubernetes.pkg.api.v1.ObjectMeta metadata = 1;
|
||||
|
||||
// Required: Describes a type of stored blob.
|
||||
optional string type = 2;
|
||||
|
||||
// Required: An opaque binary string which is an image's signature.
|
||||
optional bytes content = 3;
|
||||
|
||||
// Conditions represent the latest available observations of a signature's current state.
|
||||
repeated SignatureCondition conditions = 4;
|
||||
|
||||
// A human readable string representing image's identity. It could be a product name and version, or an
|
||||
// image pull spec (e.g. "registry.access.redhat.com/rhel7/rhel:7.2").
|
||||
optional string imageIdentity = 5;
|
||||
|
||||
// Contains claims from the signature.
|
||||
map<string, string> signedClaims = 6;
|
||||
|
||||
// If specified, it is the time of signature's creation.
|
||||
optional k8s.io.kubernetes.pkg.api.unversioned.Time created = 7;
|
||||
|
||||
// If specified, it holds information about an issuer of signing certificate or key (a person or entity
|
||||
// who signed the signing certificate or key).
|
||||
optional SignatureIssuer issuedBy = 8;
|
||||
|
||||
// If specified, it holds information about a subject of signing certificate or key (a person or entity
|
||||
// who signed the image).
|
||||
optional SignatureSubject issuedTo = 9;
|
||||
}
|
||||
|
||||
// ImageStream stores a mapping of tags to images, metadata overrides that are applied
|
||||
// when images are tagged in a stream, and an optional reference to a Docker image
|
||||
// repository on a registry.
|
||||
message ImageStream {
|
||||
// Standard object's metadata.
|
||||
optional k8s.io.kubernetes.pkg.api.v1.ObjectMeta metadata = 1;
|
||||
|
||||
// Spec describes the desired state of this stream
|
||||
optional ImageStreamSpec spec = 2;
|
||||
|
||||
// Status describes the current state of this stream
|
||||
optional ImageStreamStatus status = 3;
|
||||
}
|
||||
|
||||
// ImageStreamImage represents an Image that is retrieved by image name from an ImageStream.
|
||||
message ImageStreamImage {
|
||||
// Standard object's metadata.
|
||||
optional k8s.io.kubernetes.pkg.api.v1.ObjectMeta metadata = 1;
|
||||
|
||||
// Image associated with the ImageStream and image name.
|
||||
optional Image image = 2;
|
||||
}
|
||||
|
||||
// ImageStreamImport imports an image from remote repositories into OpenShift.
|
||||
message ImageStreamImport {
|
||||
// Standard object's metadata.
|
||||
optional k8s.io.kubernetes.pkg.api.v1.ObjectMeta metadata = 1;
|
||||
|
||||
// Spec is a description of the images that the user wishes to import
|
||||
optional ImageStreamImportSpec spec = 2;
|
||||
|
||||
// Status is the the result of importing the image
|
||||
optional ImageStreamImportStatus status = 3;
|
||||
}
|
||||
|
||||
// ImageStreamImportSpec defines what images should be imported.
|
||||
message ImageStreamImportSpec {
|
||||
// Import indicates whether to perform an import - if so, the specified tags are set on the spec
|
||||
// and status of the image stream defined by the type meta.
|
||||
optional bool import = 1;
|
||||
|
||||
// Repository is an optional import of an entire Docker image repository. A maximum limit on the
|
||||
// number of tags imported this way is imposed by the server.
|
||||
optional RepositoryImportSpec repository = 2;
|
||||
|
||||
// Images are a list of individual images to import.
|
||||
repeated ImageImportSpec images = 3;
|
||||
}
|
||||
|
||||
// ImageStreamImportStatus contains information about the status of an image stream import.
|
||||
message ImageStreamImportStatus {
|
||||
// Import is the image stream that was successfully updated or created when 'to' was set.
|
||||
optional ImageStream import = 1;
|
||||
|
||||
// Repository is set if spec.repository was set to the outcome of the import
|
||||
optional RepositoryImportStatus repository = 2;
|
||||
|
||||
// Images is set with the result of importing spec.images
|
||||
repeated ImageImportStatus images = 3;
|
||||
}
|
||||
|
||||
// ImageStreamList is a list of ImageStream objects.
|
||||
message ImageStreamList {
|
||||
// Standard object's metadata.
|
||||
optional k8s.io.kubernetes.pkg.api.unversioned.ListMeta metadata = 1;
|
||||
|
||||
// Items is a list of imageStreams
|
||||
repeated ImageStream items = 2;
|
||||
}
|
||||
|
||||
// ImageStreamMapping represents a mapping from a single tag to a Docker image as
|
||||
// well as the reference to the Docker image stream the image came from.
|
||||
message ImageStreamMapping {
|
||||
// Standard object's metadata.
|
||||
optional k8s.io.kubernetes.pkg.api.v1.ObjectMeta metadata = 1;
|
||||
|
||||
// Image is a Docker image.
|
||||
optional Image image = 2;
|
||||
|
||||
// Tag is a string value this image can be located with inside the stream.
|
||||
optional string tag = 3;
|
||||
}
|
||||
|
||||
// ImageStreamSpec represents options for ImageStreams.
|
||||
message ImageStreamSpec {
|
||||
// DockerImageRepository is optional, if specified this stream is backed by a Docker repository on this server
|
||||
optional string dockerImageRepository = 1;
|
||||
|
||||
// Tags map arbitrary string values to specific image locators
|
||||
repeated TagReference tags = 2;
|
||||
}
|
||||
|
||||
// ImageStreamStatus contains information about the state of this image stream.
|
||||
message ImageStreamStatus {
|
||||
// DockerImageRepository represents the effective location this stream may be accessed at.
|
||||
// May be empty until the server determines where the repository is located
|
||||
optional string dockerImageRepository = 1;
|
||||
|
||||
// Tags are a historical record of images associated with each tag. The first entry in the
|
||||
// TagEvent array is the currently tagged image.
|
||||
repeated NamedTagEventList tags = 2;
|
||||
}
|
||||
|
||||
// ImageStreamTag represents an Image that is retrieved by tag name from an ImageStream.
|
||||
message ImageStreamTag {
|
||||
// Standard object's metadata.
|
||||
optional k8s.io.kubernetes.pkg.api.v1.ObjectMeta metadata = 1;
|
||||
|
||||
// Tag is the spec tag associated with this image stream tag, and it may be null
|
||||
// if only pushes have occurred to this image stream.
|
||||
optional TagReference tag = 2;
|
||||
|
||||
// Generation is the current generation of the tagged image - if tag is provided
|
||||
// and this value is not equal to the tag generation, a user has requested an
|
||||
// import that has not completed, or Conditions will be filled out indicating any
|
||||
// error.
|
||||
optional int64 generation = 3;
|
||||
|
||||
// Conditions is an array of conditions that apply to the image stream tag.
|
||||
repeated TagEventCondition conditions = 4;
|
||||
|
||||
// Image associated with the ImageStream and tag.
|
||||
optional Image image = 5;
|
||||
}
|
||||
|
||||
// ImageStreamTagList is a list of ImageStreamTag objects.
|
||||
message ImageStreamTagList {
|
||||
// Standard object's metadata.
|
||||
optional k8s.io.kubernetes.pkg.api.unversioned.ListMeta metadata = 1;
|
||||
|
||||
// Items is the list of image stream tags
|
||||
repeated ImageStreamTag items = 2;
|
||||
}
|
||||
|
||||
// NamedTagEventList relates a tag to its image history.
|
||||
message NamedTagEventList {
|
||||
// Tag is the tag for which the history is recorded
|
||||
optional string tag = 1;
|
||||
|
||||
// Standard object's metadata.
|
||||
repeated TagEvent items = 2;
|
||||
|
||||
// Conditions is an array of conditions that apply to the tag event list.
|
||||
repeated TagEventCondition conditions = 3;
|
||||
}
|
||||
|
||||
// RepositoryImportSpec describes a request to import images from a Docker image repository.
|
||||
message RepositoryImportSpec {
|
||||
// From is the source for the image repository to import; only kind DockerImage and a name of a Docker image repository is allowed
|
||||
optional k8s.io.kubernetes.pkg.api.v1.ObjectReference from = 1;
|
||||
|
||||
// ImportPolicy is the policy controlling how the image is imported
|
||||
optional TagImportPolicy importPolicy = 2;
|
||||
|
||||
// IncludeManifest determines if the manifest for each image is returned in the response
|
||||
optional bool includeManifest = 3;
|
||||
}
|
||||
|
||||
// RepositoryImportStatus describes the result of an image repository import
|
||||
message RepositoryImportStatus {
|
||||
// Status reflects whether any failure occurred during import
|
||||
optional k8s.io.kubernetes.pkg.api.unversioned.Status status = 1;
|
||||
|
||||
// Images is a list of images successfully retrieved by the import of the repository.
|
||||
repeated ImageImportStatus images = 2;
|
||||
|
||||
// AdditionalTags are tags that exist in the repository but were not imported because
|
||||
// a maximum limit of automatic imports was applied.
|
||||
repeated string additionalTags = 3;
|
||||
}
|
||||
|
||||
// SignatureCondition describes an image signature condition of particular kind at particular probe time.
|
||||
message SignatureCondition {
|
||||
// Type of signature condition, Complete or Failed.
|
||||
optional string type = 1;
|
||||
|
||||
// Status of the condition, one of True, False, Unknown.
|
||||
optional string status = 2;
|
||||
|
||||
// Last time the condition was checked.
|
||||
optional k8s.io.kubernetes.pkg.api.unversioned.Time lastProbeTime = 3;
|
||||
|
||||
// Last time the condition transit from one status to another.
|
||||
optional k8s.io.kubernetes.pkg.api.unversioned.Time lastTransitionTime = 4;
|
||||
|
||||
// (brief) reason for the condition's last transition.
|
||||
optional string reason = 5;
|
||||
|
||||
// Human readable message indicating details about last transition.
|
||||
optional string message = 6;
|
||||
}
|
||||
|
||||
// SignatureGenericEntity holds a generic information about a person or entity who is an issuer or a subject
|
||||
// of signing certificate or key.
|
||||
message SignatureGenericEntity {
|
||||
// Organization name.
|
||||
optional string organization = 1;
|
||||
|
||||
// Common name (e.g. openshift-signing-service).
|
||||
optional string commonName = 2;
|
||||
}
|
||||
|
||||
// SignatureIssuer holds information about an issuer of signing certificate or key.
|
||||
message SignatureIssuer {
|
||||
optional SignatureGenericEntity signatureGenericEntity = 1;
|
||||
}
|
||||
|
||||
// SignatureSubject holds information about a person or entity who created the signature.
|
||||
message SignatureSubject {
|
||||
optional SignatureGenericEntity signatureGenericEntity = 1;
|
||||
|
||||
// If present, it is a human readable key id of public key belonging to the subject used to verify image
|
||||
// signature. It should contain at least 64 lowest bits of public key's fingerprint (e.g.
|
||||
// 0x685ebe62bf278440).
|
||||
optional string publicKeyID = 2;
|
||||
}
|
||||
|
||||
// TagEvent is used by ImageStreamStatus to keep a historical record of images associated with a tag.
|
||||
message TagEvent {
|
||||
// Created holds the time the TagEvent was created
|
||||
optional k8s.io.kubernetes.pkg.api.unversioned.Time created = 1;
|
||||
|
||||
// DockerImageReference is the string that can be used to pull this image
|
||||
optional string dockerImageReference = 2;
|
||||
|
||||
// Image is the image
|
||||
optional string image = 3;
|
||||
|
||||
// Generation is the spec tag generation that resulted in this tag being updated
|
||||
optional int64 generation = 4;
|
||||
}
|
||||
|
||||
// TagEventCondition contains condition information for a tag event.
|
||||
message TagEventCondition {
|
||||
// Type of tag event condition, currently only ImportSuccess
|
||||
optional string type = 1;
|
||||
|
||||
// Status of the condition, one of True, False, Unknown.
|
||||
optional string status = 2;
|
||||
|
||||
// LastTransitionTIme is the time the condition transitioned from one status to another.
|
||||
optional k8s.io.kubernetes.pkg.api.unversioned.Time lastTransitionTime = 3;
|
||||
|
||||
// Reason is a brief machine readable explanation for the condition's last transition.
|
||||
optional string reason = 4;
|
||||
|
||||
// Message is a human readable description of the details about last transition, complementing reason.
|
||||
optional string message = 5;
|
||||
|
||||
// Generation is the spec tag generation that this status corresponds to
|
||||
optional int64 generation = 6;
|
||||
}
|
||||
|
||||
// TagImportPolicy describes the tag import policy
|
||||
message TagImportPolicy {
|
||||
// Insecure is true if the server may bypass certificate verification or connect directly over HTTP during image import.
|
||||
optional bool insecure = 1;
|
||||
|
||||
// Scheduled indicates to the server that this tag should be periodically checked to ensure it is up to date, and imported
|
||||
optional bool scheduled = 2;
|
||||
}
|
||||
|
||||
// TagReference specifies optional annotations for images using this tag and an optional reference to an ImageStreamTag, ImageStreamImage, or DockerImage this tag should track.
|
||||
message TagReference {
|
||||
// Name of the tag
|
||||
optional string name = 1;
|
||||
|
||||
// Annotations associated with images using this tag
|
||||
map<string, string> annotations = 2;
|
||||
|
||||
// From is a reference to an image stream tag or image stream this tag should track
|
||||
optional k8s.io.kubernetes.pkg.api.v1.ObjectReference from = 3;
|
||||
|
||||
// Reference states if the tag will be imported. Default value is false, which means the tag will be imported.
|
||||
optional bool reference = 4;
|
||||
|
||||
// Generation is the image stream generation that updated this tag - setting it to 0 is an indication that the generation must be updated.
|
||||
// Legacy clients will send this as nil, which means the client doesn't know or care.
|
||||
optional int64 generation = 5;
|
||||
|
||||
// Import is information that controls how images may be imported by the server.
|
||||
optional TagImportPolicy importPolicy = 6;
|
||||
}
|
||||
|
||||
+1
-1
@@ -119,7 +119,7 @@ func (ImageStreamImage) SwaggerDoc() map[string]string {
|
||||
}
|
||||
|
||||
var map_ImageStreamImport = map[string]string{
|
||||
"": "ImageStreamImport imports an image from remote repositories into OpenShift.",
|
||||
"": "The image stream import resource provides an easy way for a user to find and import Docker images from other Docker registries into the server. Individual images or an entire image repository may be imported, and users may choose to see the results of the import prior to tagging the resulting images into the specified image stream.\n\nThis API is intended for end-user tools that need to see the metadata of the image prior to import (for instance, to generate an application from it). Clients that know the desired image can continue to create spec.tags directly into their image streams.",
|
||||
"metadata": "Standard object's metadata.",
|
||||
"spec": "Spec is a description of the images that the user wishes to import",
|
||||
"status": "Status is the the result of importing the image",
|
||||
|
||||
+8
-1
@@ -314,7 +314,14 @@ type DockerImageReference struct {
|
||||
ID string `protobuf:"bytes,5,opt,name=iD"`
|
||||
}
|
||||
|
||||
// ImageStreamImport imports an image from remote repositories into OpenShift.
|
||||
// The image stream import resource provides an easy way for a user to find and import Docker images
|
||||
// from other Docker registries into the server. Individual images or an entire image repository may
|
||||
// be imported, and users may choose to see the results of the import prior to tagging the resulting
|
||||
// images into the specified image stream.
|
||||
//
|
||||
// This API is intended for end-user tools that need to see the metadata of the image prior to import
|
||||
// (for instance, to generate an application from it). Clients that know the desired image can continue
|
||||
// to create spec.tags directly into their image streams.
|
||||
type ImageStreamImport struct {
|
||||
unversioned.TypeMeta `json:",inline"`
|
||||
// Standard object's metadata.
|
||||
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
package graph
|
||||
|
||||
import (
|
||||
"github.com/gonum/graph"
|
||||
|
||||
osgraph "github.com/openshift/origin/pkg/api/graph"
|
||||
imageapi "github.com/openshift/origin/pkg/image/api"
|
||||
imagegraph "github.com/openshift/origin/pkg/image/graph/nodes"
|
||||
)
|
||||
|
||||
const (
|
||||
// ReferencedImageStreamGraphEdgeKind is an edge that goes from an ImageStreamTag node back to an ImageStream
|
||||
ReferencedImageStreamGraphEdgeKind = "ReferencedImageStreamGraphEdge"
|
||||
// ReferencedImageStreamImageGraphEdgeKind is an edge that goes from an ImageStreamImage node back to an ImageStream
|
||||
ReferencedImageStreamImageGraphEdgeKind = "ReferencedImageStreamImageGraphEdgeKind"
|
||||
)
|
||||
|
||||
// AddImageStreamTagRefEdge ensures that a directed edge exists between an IST Node and the IS it references
|
||||
func AddImageStreamTagRefEdge(g osgraph.MutableUniqueGraph, node *imagegraph.ImageStreamTagNode) {
|
||||
isName, _, _ := imageapi.SplitImageStreamTag(node.Name)
|
||||
imageStream := &imageapi.ImageStream{}
|
||||
imageStream.Namespace = node.Namespace
|
||||
imageStream.Name = isName
|
||||
|
||||
imageStreamNode := imagegraph.FindOrCreateSyntheticImageStreamNode(g, imageStream)
|
||||
g.AddEdge(node, imageStreamNode, ReferencedImageStreamGraphEdgeKind)
|
||||
}
|
||||
|
||||
// AddImageStreamImageRefEdge ensures that a directed edge exists between an ImageStreamImage Node and the IS it references
|
||||
func AddImageStreamImageRefEdge(g osgraph.MutableUniqueGraph, node *imagegraph.ImageStreamImageNode) {
|
||||
dockImgRef, _ := imageapi.ParseDockerImageReference(node.Name)
|
||||
imageStream := &imageapi.ImageStream{}
|
||||
imageStream.Namespace = node.Namespace
|
||||
imageStream.Name = dockImgRef.Name
|
||||
|
||||
imageStreamNode := imagegraph.FindOrCreateSyntheticImageStreamNode(g, imageStream)
|
||||
g.AddEdge(node, imageStreamNode, ReferencedImageStreamImageGraphEdgeKind)
|
||||
}
|
||||
|
||||
// AddAllImageStreamRefEdges calls AddImageStreamRefEdge for every ImageStreamTagNode in the graph
|
||||
func AddAllImageStreamRefEdges(g osgraph.MutableUniqueGraph) {
|
||||
for _, node := range g.(graph.Graph).Nodes() {
|
||||
if istNode, ok := node.(*imagegraph.ImageStreamTagNode); ok {
|
||||
AddImageStreamTagRefEdge(g, istNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AddAllImageStreamImageRefEdges calls AddImageStreamImageRefEdge for every ImageStreamImageNode in the graph
|
||||
func AddAllImageStreamImageRefEdges(g osgraph.MutableUniqueGraph) {
|
||||
for _, node := range g.(graph.Graph).Nodes() {
|
||||
if isimageNode, ok := node.(*imagegraph.ImageStreamImageNode); ok {
|
||||
AddImageStreamImageRefEdge(g, isimageNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
-172
@@ -1,172 +0,0 @@
|
||||
package nodes
|
||||
|
||||
import (
|
||||
"github.com/gonum/graph"
|
||||
|
||||
kapi "k8s.io/kubernetes/pkg/api"
|
||||
|
||||
osgraph "github.com/openshift/origin/pkg/api/graph"
|
||||
imageapi "github.com/openshift/origin/pkg/image/api"
|
||||
)
|
||||
|
||||
func EnsureImageNode(g osgraph.MutableUniqueGraph, img *imageapi.Image) graph.Node {
|
||||
return osgraph.EnsureUnique(g,
|
||||
ImageNodeName(img),
|
||||
func(node osgraph.Node) graph.Node {
|
||||
return &ImageNode{node, img}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// EnsureAllImageStreamTagNodes creates all the ImageStreamTagNodes that are guaranteed to be present based on the ImageStream.
|
||||
// This is different than inferring the presence of an object, since the IST is an object derived from a join between the ImageStream
|
||||
// and the Image it references.
|
||||
func EnsureAllImageStreamTagNodes(g osgraph.MutableUniqueGraph, is *imageapi.ImageStream) []*ImageStreamTagNode {
|
||||
ret := []*ImageStreamTagNode{}
|
||||
|
||||
for tag := range is.Status.Tags {
|
||||
ist := &imageapi.ImageStreamTag{}
|
||||
ist.Namespace = is.Namespace
|
||||
ist.Name = imageapi.JoinImageStreamTag(is.Name, tag)
|
||||
|
||||
istNode := EnsureImageStreamTagNode(g, ist)
|
||||
ret = append(ret, istNode)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func FindImage(g osgraph.MutableUniqueGraph, imageName string) graph.Node {
|
||||
return g.Find(ImageNodeName(&imageapi.Image{ObjectMeta: kapi.ObjectMeta{Name: imageName}}))
|
||||
}
|
||||
|
||||
// EnsureDockerRepositoryNode adds the named Docker repository tag reference to the graph if it does
|
||||
// not already exist. If the reference is invalid, the Name field of the graph will be used directly.
|
||||
func EnsureDockerRepositoryNode(g osgraph.MutableUniqueGraph, name, tag string) graph.Node {
|
||||
ref, err := imageapi.ParseDockerImageReference(name)
|
||||
if err == nil {
|
||||
if len(tag) != 0 {
|
||||
ref.Tag = tag
|
||||
}
|
||||
ref = ref.DockerClientDefaults()
|
||||
} else {
|
||||
ref = imageapi.DockerImageReference{Name: name}
|
||||
}
|
||||
|
||||
return osgraph.EnsureUnique(g,
|
||||
DockerImageRepositoryNodeName(ref),
|
||||
func(node osgraph.Node) graph.Node {
|
||||
return &DockerImageRepositoryNode{node, ref}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// MakeImageStreamTagObjectMeta returns an ImageStreamTag that has enough information to join the graph, but it is not
|
||||
// based on a full IST object. This can be used to properly initialize the graph without having to retrieve all ISTs
|
||||
func MakeImageStreamTagObjectMeta(namespace, name, tag string) *imageapi.ImageStreamTag {
|
||||
return &imageapi.ImageStreamTag{
|
||||
ObjectMeta: kapi.ObjectMeta{
|
||||
Namespace: namespace,
|
||||
Name: imageapi.JoinImageStreamTag(name, tag),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// MakeImageStreamTagObjectMeta2 returns an ImageStreamTag that has enough information to join the graph, but it is not
|
||||
// based on a full IST object. This can be used to properly initialize the graph without having to retrieve all ISTs
|
||||
func MakeImageStreamTagObjectMeta2(namespace, name string) *imageapi.ImageStreamTag {
|
||||
return &imageapi.ImageStreamTag{
|
||||
ObjectMeta: kapi.ObjectMeta{
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// EnsureImageStreamTagNode adds a graph node for the specific tag in an Image Stream if it does not already exist.
|
||||
func EnsureImageStreamTagNode(g osgraph.MutableUniqueGraph, ist *imageapi.ImageStreamTag) *ImageStreamTagNode {
|
||||
return osgraph.EnsureUnique(g,
|
||||
ImageStreamTagNodeName(ist),
|
||||
func(node osgraph.Node) graph.Node {
|
||||
return &ImageStreamTagNode{node, ist, true}
|
||||
},
|
||||
).(*ImageStreamTagNode)
|
||||
}
|
||||
|
||||
// FindOrCreateSyntheticImageStreamTagNode returns the existing ISTNode or creates a synthetic node in its place
|
||||
func FindOrCreateSyntheticImageStreamTagNode(g osgraph.MutableUniqueGraph, ist *imageapi.ImageStreamTag) *ImageStreamTagNode {
|
||||
return osgraph.EnsureUnique(g,
|
||||
ImageStreamTagNodeName(ist),
|
||||
func(node osgraph.Node) graph.Node {
|
||||
return &ImageStreamTagNode{node, ist, false}
|
||||
},
|
||||
).(*ImageStreamTagNode)
|
||||
}
|
||||
|
||||
// MakeImageStreamImageObjectMeta returns an ImageStreamImage that has enough information to join the graph, but it is not
|
||||
// based on a full ISI object. This can be used to properly initialize the graph without having to retrieve all ISIs
|
||||
func MakeImageStreamImageObjectMeta(namespace, name string) *imageapi.ImageStreamImage {
|
||||
return &imageapi.ImageStreamImage{
|
||||
ObjectMeta: kapi.ObjectMeta{
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// EnsureImageStreamImageNode adds a graph node for the specific ImageStreamImage if it
|
||||
// does not already exist.
|
||||
func EnsureImageStreamImageNode(g osgraph.MutableUniqueGraph, namespace, name string) graph.Node {
|
||||
isi := &imageapi.ImageStreamImage{
|
||||
ObjectMeta: kapi.ObjectMeta{
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
},
|
||||
}
|
||||
return osgraph.EnsureUnique(g,
|
||||
ImageStreamImageNodeName(isi),
|
||||
func(node osgraph.Node) graph.Node {
|
||||
return &ImageStreamImageNode{node, isi, true}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// FindOrCreateSyntheticImageStreamImageNode returns the existing ISINode or creates a synthetic node in its place
|
||||
func FindOrCreateSyntheticImageStreamImageNode(g osgraph.MutableUniqueGraph, isi *imageapi.ImageStreamImage) *ImageStreamImageNode {
|
||||
return osgraph.EnsureUnique(g,
|
||||
ImageStreamImageNodeName(isi),
|
||||
func(node osgraph.Node) graph.Node {
|
||||
return &ImageStreamImageNode{node, isi, false}
|
||||
},
|
||||
).(*ImageStreamImageNode)
|
||||
}
|
||||
|
||||
// EnsureImageStreamNode adds a graph node for the Image Stream if it does not already exist.
|
||||
func EnsureImageStreamNode(g osgraph.MutableUniqueGraph, is *imageapi.ImageStream) graph.Node {
|
||||
return osgraph.EnsureUnique(g,
|
||||
ImageStreamNodeName(is),
|
||||
func(node osgraph.Node) graph.Node {
|
||||
return &ImageStreamNode{node, is, true}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// FindOrCreateSyntheticImageStreamNode returns the existing ISNode or creates a synthetic node in its place
|
||||
func FindOrCreateSyntheticImageStreamNode(g osgraph.MutableUniqueGraph, is *imageapi.ImageStream) *ImageStreamNode {
|
||||
return osgraph.EnsureUnique(g,
|
||||
ImageStreamNodeName(is),
|
||||
func(node osgraph.Node) graph.Node {
|
||||
return &ImageStreamNode{node, is, false}
|
||||
},
|
||||
).(*ImageStreamNode)
|
||||
}
|
||||
|
||||
// EnsureImageLayerNode adds a graph node for the layer if it does not already exist.
|
||||
func EnsureImageLayerNode(g osgraph.MutableUniqueGraph, layer string) graph.Node {
|
||||
return osgraph.EnsureUnique(g,
|
||||
ImageLayerNodeName(layer),
|
||||
func(node osgraph.Node) graph.Node {
|
||||
return &ImageLayerNode{node, layer}
|
||||
},
|
||||
)
|
||||
}
|
||||
-207
@@ -1,207 +0,0 @@
|
||||
package nodes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
osgraph "github.com/openshift/origin/pkg/api/graph"
|
||||
imageapi "github.com/openshift/origin/pkg/image/api"
|
||||
)
|
||||
|
||||
var (
|
||||
ImageStreamNodeKind = reflect.TypeOf(imageapi.ImageStream{}).Name()
|
||||
ImageNodeKind = reflect.TypeOf(imageapi.Image{}).Name()
|
||||
ImageStreamTagNodeKind = reflect.TypeOf(imageapi.ImageStreamTag{}).Name()
|
||||
ImageStreamImageNodeKind = reflect.TypeOf(imageapi.ImageStreamImage{}).Name()
|
||||
|
||||
// non-api types
|
||||
DockerRepositoryNodeKind = reflect.TypeOf(imageapi.DockerImageReference{}).Name()
|
||||
ImageLayerNodeKind = "ImageLayer"
|
||||
)
|
||||
|
||||
func ImageStreamNodeName(o *imageapi.ImageStream) osgraph.UniqueName {
|
||||
return osgraph.GetUniqueRuntimeObjectNodeName(ImageStreamNodeKind, o)
|
||||
}
|
||||
|
||||
type ImageStreamNode struct {
|
||||
osgraph.Node
|
||||
*imageapi.ImageStream
|
||||
|
||||
IsFound bool
|
||||
}
|
||||
|
||||
func (n ImageStreamNode) Found() bool {
|
||||
return n.IsFound
|
||||
}
|
||||
|
||||
func (n ImageStreamNode) Object() interface{} {
|
||||
return n.ImageStream
|
||||
}
|
||||
|
||||
func (n ImageStreamNode) String() string {
|
||||
return string(ImageStreamNodeName(n.ImageStream))
|
||||
}
|
||||
|
||||
func (n ImageStreamNode) UniqueName() osgraph.UniqueName {
|
||||
return ImageStreamNodeName(n.ImageStream)
|
||||
}
|
||||
|
||||
func (*ImageStreamNode) Kind() string {
|
||||
return ImageStreamNodeKind
|
||||
}
|
||||
|
||||
func ImageStreamTagNodeName(o *imageapi.ImageStreamTag) osgraph.UniqueName {
|
||||
return osgraph.GetUniqueRuntimeObjectNodeName(ImageStreamTagNodeKind, o)
|
||||
}
|
||||
|
||||
type ImageStreamTagNode struct {
|
||||
osgraph.Node
|
||||
*imageapi.ImageStreamTag
|
||||
|
||||
IsFound bool
|
||||
}
|
||||
|
||||
func (n ImageStreamTagNode) Found() bool {
|
||||
return n.IsFound
|
||||
}
|
||||
|
||||
func (n ImageStreamTagNode) ImageSpec() string {
|
||||
name, tag, _ := imageapi.SplitImageStreamTag(n.ImageStreamTag.Name)
|
||||
return imageapi.DockerImageReference{Namespace: n.Namespace, Name: name, Tag: tag}.String()
|
||||
}
|
||||
|
||||
func (n ImageStreamTagNode) ImageTag() string {
|
||||
_, tag, _ := imageapi.SplitImageStreamTag(n.ImageStreamTag.Name)
|
||||
return tag
|
||||
}
|
||||
|
||||
func (n ImageStreamTagNode) Object() interface{} {
|
||||
return n.ImageStreamTag
|
||||
}
|
||||
|
||||
func (n ImageStreamTagNode) String() string {
|
||||
return string(ImageStreamTagNodeName(n.ImageStreamTag))
|
||||
}
|
||||
|
||||
func (n ImageStreamTagNode) UniqueName() osgraph.UniqueName {
|
||||
return ImageStreamTagNodeName(n.ImageStreamTag)
|
||||
}
|
||||
|
||||
func (*ImageStreamTagNode) Kind() string {
|
||||
return ImageStreamTagNodeKind
|
||||
}
|
||||
|
||||
func ImageStreamImageNodeName(o *imageapi.ImageStreamImage) osgraph.UniqueName {
|
||||
return osgraph.GetUniqueRuntimeObjectNodeName(ImageStreamImageNodeKind, o)
|
||||
}
|
||||
|
||||
type ImageStreamImageNode struct {
|
||||
osgraph.Node
|
||||
*imageapi.ImageStreamImage
|
||||
|
||||
IsFound bool
|
||||
}
|
||||
|
||||
func (n ImageStreamImageNode) ImageSpec() string {
|
||||
return n.ImageStreamImage.Namespace + "/" + n.ImageStreamImage.Name
|
||||
}
|
||||
|
||||
func (n ImageStreamImageNode) ImageTag() string {
|
||||
_, id, _ := imageapi.SplitImageStreamImage(n.ImageStreamImage.Name)
|
||||
return id
|
||||
}
|
||||
|
||||
func (n ImageStreamImageNode) Object() interface{} {
|
||||
return n.ImageStreamImage
|
||||
}
|
||||
|
||||
func (n ImageStreamImageNode) String() string {
|
||||
return string(ImageStreamImageNodeName(n.ImageStreamImage))
|
||||
}
|
||||
|
||||
func (n ImageStreamImageNode) ResourceString() string {
|
||||
return "isimage/" + n.Name
|
||||
}
|
||||
|
||||
func (n ImageStreamImageNode) UniqueName() osgraph.UniqueName {
|
||||
return ImageStreamImageNodeName(n.ImageStreamImage)
|
||||
}
|
||||
|
||||
func (*ImageStreamImageNode) Kind() string {
|
||||
return ImageStreamImageNodeKind
|
||||
}
|
||||
|
||||
func DockerImageRepositoryNodeName(o imageapi.DockerImageReference) osgraph.UniqueName {
|
||||
return osgraph.UniqueName(fmt.Sprintf("%s|%s", DockerRepositoryNodeKind, o.String()))
|
||||
}
|
||||
|
||||
type DockerImageRepositoryNode struct {
|
||||
osgraph.Node
|
||||
Ref imageapi.DockerImageReference
|
||||
}
|
||||
|
||||
func (n DockerImageRepositoryNode) ImageSpec() string {
|
||||
return n.Ref.String()
|
||||
}
|
||||
|
||||
func (n DockerImageRepositoryNode) ImageTag() string {
|
||||
return n.Ref.DockerClientDefaults().Tag
|
||||
}
|
||||
|
||||
func (n DockerImageRepositoryNode) String() string {
|
||||
return string(DockerImageRepositoryNodeName(n.Ref))
|
||||
}
|
||||
|
||||
func (*DockerImageRepositoryNode) Kind() string {
|
||||
return DockerRepositoryNodeKind
|
||||
}
|
||||
|
||||
func (n DockerImageRepositoryNode) UniqueName() osgraph.UniqueName {
|
||||
return DockerImageRepositoryNodeName(n.Ref)
|
||||
}
|
||||
|
||||
func ImageNodeName(o *imageapi.Image) osgraph.UniqueName {
|
||||
return osgraph.GetUniqueRuntimeObjectNodeName(ImageNodeKind, o)
|
||||
}
|
||||
|
||||
type ImageNode struct {
|
||||
osgraph.Node
|
||||
Image *imageapi.Image
|
||||
}
|
||||
|
||||
func (n ImageNode) Object() interface{} {
|
||||
return n.Image
|
||||
}
|
||||
|
||||
func (n ImageNode) String() string {
|
||||
return string(ImageNodeName(n.Image))
|
||||
}
|
||||
|
||||
func (n ImageNode) UniqueName() osgraph.UniqueName {
|
||||
return ImageNodeName(n.Image)
|
||||
}
|
||||
|
||||
func (*ImageNode) Kind() string {
|
||||
return ImageNodeKind
|
||||
}
|
||||
|
||||
func ImageLayerNodeName(layer string) osgraph.UniqueName {
|
||||
return osgraph.UniqueName(fmt.Sprintf("%s|%s", ImageLayerNodeKind, layer))
|
||||
}
|
||||
|
||||
type ImageLayerNode struct {
|
||||
osgraph.Node
|
||||
Layer string
|
||||
}
|
||||
|
||||
func (n ImageLayerNode) Object() interface{} {
|
||||
return n.Layer
|
||||
}
|
||||
|
||||
func (n ImageLayerNode) String() string {
|
||||
return string(ImageLayerNodeName(n.Layer))
|
||||
}
|
||||
|
||||
func (*ImageLayerNode) Kind() string {
|
||||
return ImageLayerNodeKind
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package reference
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
)
|
||||
|
||||
// NamedDockerImageReference points to a Docker image.
|
||||
type NamedDockerImageReference struct {
|
||||
Registry string
|
||||
Namespace string
|
||||
Name string
|
||||
Tag string
|
||||
ID string
|
||||
}
|
||||
|
||||
// ParseNamedDockerImageReference parses a Docker pull spec string into a
|
||||
// NamedDockerImageReference.
|
||||
func ParseNamedDockerImageReference(spec string) (NamedDockerImageReference, error) {
|
||||
var ref NamedDockerImageReference
|
||||
|
||||
namedRef, err := reference.ParseNamed(spec)
|
||||
if err != nil {
|
||||
return ref, err
|
||||
}
|
||||
|
||||
name := namedRef.Name()
|
||||
i := strings.IndexRune(name, '/')
|
||||
if i == -1 || (!strings.ContainsAny(name[:i], ":.") && name[:i] != "localhost") {
|
||||
ref.Name = name
|
||||
} else {
|
||||
ref.Registry, ref.Name = name[:i], name[i+1:]
|
||||
}
|
||||
|
||||
if named, ok := namedRef.(reference.NamedTagged); ok {
|
||||
ref.Tag = named.Tag()
|
||||
}
|
||||
|
||||
if named, ok := namedRef.(reference.Canonical); ok {
|
||||
ref.ID = named.Digest().String()
|
||||
}
|
||||
|
||||
// It's not enough just to use the reference.ParseNamed(). We have to fill
|
||||
// ref.Namespace from ref.Name
|
||||
if i := strings.IndexRune(ref.Name, '/'); i != -1 {
|
||||
ref.Namespace, ref.Name = ref.Name[:i], ref.Name[i+1:]
|
||||
}
|
||||
|
||||
return ref, nil
|
||||
}
|
||||
+2
@@ -37,6 +37,7 @@ func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
&OAuthClientList{},
|
||||
&OAuthClientAuthorization{},
|
||||
&OAuthClientAuthorizationList{},
|
||||
&OAuthRedirectReference{},
|
||||
)
|
||||
return nil
|
||||
}
|
||||
@@ -49,3 +50,4 @@ func (obj *OAuthAuthorizeTokenList) GetObjectKind() unversioned.ObjectKind
|
||||
func (obj *OAuthAuthorizeToken) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
func (obj *OAuthAccessTokenList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
func (obj *OAuthAccessToken) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
func (obj *OAuthRedirectReference) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
|
||||
+18
@@ -59,6 +59,12 @@ type OAuthAuthorizeToken struct {
|
||||
// UserUID is the unique UID associated with this token. UserUID and UserName must both match
|
||||
// for this token to be valid.
|
||||
UserUID string
|
||||
|
||||
// CodeChallenge is the optional code_challenge associated with this authorization code, as described in rfc7636
|
||||
CodeChallenge string
|
||||
|
||||
// CodeChallengeMethod is the optional code_challenge_method associated with this authorization code, as described in rfc7636
|
||||
CodeChallengeMethod string
|
||||
}
|
||||
|
||||
// +genclient=true
|
||||
@@ -161,3 +167,15 @@ type OAuthClientAuthorizationList struct {
|
||||
unversioned.ListMeta
|
||||
Items []OAuthClientAuthorization
|
||||
}
|
||||
|
||||
type OAuthRedirectReference struct {
|
||||
unversioned.TypeMeta
|
||||
kapi.ObjectMeta
|
||||
Reference RedirectReference
|
||||
}
|
||||
|
||||
type RedirectReference struct {
|
||||
Group string
|
||||
Kind string
|
||||
Name string
|
||||
}
|
||||
|
||||
+28
@@ -28,6 +28,8 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error {
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_OAuthClientAuthorization, InType: reflect.TypeOf(&OAuthClientAuthorization{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_OAuthClientAuthorizationList, InType: reflect.TypeOf(&OAuthClientAuthorizationList{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_OAuthClientList, InType: reflect.TypeOf(&OAuthClientList{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_OAuthRedirectReference, InType: reflect.TypeOf(&OAuthRedirectReference{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_RedirectReference, InType: reflect.TypeOf(&RedirectReference{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_ScopeRestriction, InType: reflect.TypeOf(&ScopeRestriction{})},
|
||||
)
|
||||
}
|
||||
@@ -123,6 +125,8 @@ func DeepCopy_api_OAuthAuthorizeToken(in interface{}, out interface{}, c *conver
|
||||
out.State = in.State
|
||||
out.UserName = in.UserName
|
||||
out.UserUID = in.UserUID
|
||||
out.CodeChallenge = in.CodeChallenge
|
||||
out.CodeChallengeMethod = in.CodeChallengeMethod
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -252,6 +256,30 @@ func DeepCopy_api_OAuthClientList(in interface{}, out interface{}, c *conversion
|
||||
}
|
||||
}
|
||||
|
||||
func DeepCopy_api_OAuthRedirectReference(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*OAuthRedirectReference)
|
||||
out := out.(*OAuthRedirectReference)
|
||||
out.TypeMeta = in.TypeMeta
|
||||
if err := pkg_api.DeepCopy_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, c); err != nil {
|
||||
return err
|
||||
}
|
||||
out.Reference = in.Reference
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func DeepCopy_api_RedirectReference(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*RedirectReference)
|
||||
out := out.(*RedirectReference)
|
||||
out.Group = in.Group
|
||||
out.Kind = in.Kind
|
||||
out.Name = in.Name
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func DeepCopy_api_ScopeRestriction(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*ScopeRestriction)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user