<commit_before>// Package payload is a helper package which contains a payload
// builder to make constructing notification payloads easier.
package payload

import "encoding/json"

// Payload represents a notification which holds the content that will be
// marshalled as JSON.
type Payload struct {
	content map[string]interface{}
}

type aps struct {
	Alert            interface{} `json:"alert,omitempty"`
	Badge            interface{} `json:"badge,omitempty"`
	Category         string      `json:"category,omitempty"`
	ContentAvailable int         `json:"content-available,omitempty"`
	MutableContent   int         `json:"mutable-content,omitempty"`
	Sound            interface{} `json:"sound,omitempty"`
	ThreadID         string      `json:"thread-id,omitempty"`
	URLArgs          []string    `json:"url-args,omitempty"`
}

type alert struct {
	Action          string   `json:"action,omitempty"`
	ActionLocKey    string   `json:"action-loc-key,omitempty"`
	Body            string   `json:"body,omitempty"`
	LaunchImage     string   `json:"launch-image,omitempty"`
	LocArgs         []string `json:"loc-args,omitempty"`
	LocKey          string   `json:"loc-key,omitempty"`
	Title           string   `json:"title,omitempty"`
	Subtitle        string   `json:"subtitle,omitempty"`
	TitleLocArgs    []string `json:"title-loc-args,omitempty"`
	TitleLocKey     string   `json:"title-loc-key,omitempty"`
	SummaryArg      string   `json:"summary-arg,omitempty"`
	SummaryArgCount int      `json:"summary-arg-count,omitempty"`
}

type sound struct {
	Critical int     `json:"critical,omitempty"`
	Name     string  `json:"name,omitempty"`
	Volume   float32 `json:"volume,omitempty"`
}

// NewPayload returns a new Payload struct
func NewPayload() *Payload {
	return &Payload{
		map[string]interface{}{
			"aps": &aps{},
		},
	}
}

// Alert sets the aps alert on the payload.
// This will display a notification alert message to the user.
//
//	{"aps":{"alert":alert}}`
func (p *Payload) Alert(alert interface{}) *Payload {
	p.aps().Alert = alert
	return p
}

// Badge sets the aps badge on the payload.
// This will display a numeric badge on the app icon.
//
//	{"aps":{"badge":b}}
func (p *Payload) Badge(b int) *Payload {
	p.aps().Badge = b
	return p
}

// ZeroBadge sets the aps badge on the payload to 0.
// This will clear the badge on the app icon.
//
//	{"aps":{"badge":0}}
func (p *Payload) ZeroBadge() *Payload {
	p.aps().Badge = 0
	return p
}

// UnsetBadge removes the badge attribute from the payload.
// This will leave the badge on the app icon unchanged.
// If you wish to clear the app icon badge, use ZeroBadge() instead.
//
//	{"aps":{}}
func (p *Payload) UnsetBadge() *Payload {
	p.aps().Badge = nil
	return p
}

// Sound sets the aps sound on the payload.
// This will play a sound from the app bundle, or the default sound otherwise.
//
//	{"aps":{"sound":sound}}
func (p *Payload) Sound(sound interface{}) *Payload {
	p.aps().Sound = sound
	return p
}

// ContentAvailable sets the aps content-available on the payload to 1.
// This will indicate to the app that there is new content available to download
// and launch the app in the background.
//
//	{"aps":{"content-available":1}}
func (p *Payload) ContentAvailable() *Payload {
	p.aps().ContentAvailable = 1
	return p
}

// MutableContent sets the aps mutable-content on the payload to 1.
// This will indicate to the to the system to call your Notification Service
// extension to mutate or replace the notification's content.
//
//	{"aps":{"mutable-content":1}}
func (p *Payload) MutableContent() *Payload {
	p.aps().MutableContent = 1
	return p
}

// Custom payload

// Custom sets a custom key and value on the payload.
// This will add custom key/value data to the notification payload at root level.
//
//	{"aps":{}, key:value}
func (p *Payload) Custom(key string, val interface{}) *Payload {
	p.content[key] = val
	return p
}

// Alert dictionary

// AlertTitle sets the aps alert title on the payload.
// This will display a short string describing the purpose of the notification.
// Apple Watch & Safari display this string as part of the notification interface.
//
//	{"aps":{"alert":{"title":title}}}
func (p *Payload) AlertTitle(title string) *Payload {
	p.aps().alert().Title = title
	return p
}

// AlertTitleLocKey sets the aps alert title localization key on the payload.
// This is the key to a title string in the Localizable.strings file for the
// current localization. See Localized Formatted Strings in Apple documentation
// for more information.
//
//	{"aps":{"alert":{"title-loc-key":key}}}
func (p *Payload) AlertTitleLocKey(key string) *Payload {
	p.aps().alert().TitleLocKey = key
	return p
}

// AlertTitleLocArgs sets the aps alert title localization args on the payload.
// These are the variable string values to appear in place of the format
// specifiers in title-loc-key. See Localized Formatted Strings in Apple
// documentation for more information.
//
//	{"aps":{"alert":{"title-loc-args":args}}}
func (p *Payload) AlertTitleLocArgs(args []string) *Payload {
	p.aps().alert().TitleLocArgs = args
	return p
}

// AlertSubtitle sets the aps alert subtitle on the payload.
// This will display a short string describing the purpose of the notification.
// Apple Watch & Safari display this string as part of the notification interface.
//
//	{"aps":{"subtitle":"subtitle"}}
func (p *Payload) AlertSubtitle(subtitle string) *Payload {
	p.aps().alert().Subtitle = subtitle
	return p
}

// AlertBody sets the aps alert body on the payload.
// This is the text of the alert message.
//
//	{"aps":{"alert":{"body":body}}}
func (p *Payload) AlertBody(body string) *Payload {
	p.aps().alert().Body = body
	return p
}

// AlertLaunchImage sets the aps launch image on the payload.
// This is the filename of an image file in the app bundle. The image is used
// as the launch image when users tap the action button or move the action
// slider.
//
//	{"aps":{"alert":{"launch-image":image}}}
func (p *Payload) AlertLaunchImage(image string) *Payload {
	p.aps().alert().LaunchImage = image
	return p
}

// AlertLocArgs sets the aps alert localization args on the payload.
// These are the variable string values to appear in place of the format
// specifiers in loc-key. See Localized Formatted Strings in Apple
// documentation for more information.
//
//  {"aps":{"alert":{"loc-args":args}}}
func (p *Payload) AlertLocArgs(args []string) *Payload {
	p.aps().alert().LocArgs = args
	return p
}

// AlertLocKey sets the aps alert localization key on the payload.
// This is the key to an alert-message string in the Localizable.strings file
// for the current localization. See Localized Formatted Strings in Apple
// documentation for more information.
//
//	{"aps":{"alert":{"loc-key":key}}}
func (p *Payload) AlertLocKey(key string) *Payload {
	p.aps().alert().LocKey = key
	return p
}

// AlertAction sets the aps alert action on the payload.
// This is the label of the action button, if the user sets the notifications
// to appear as alerts. This label should be succinct, such as “Details” or
// “Read more”. If omitted, the default value is “Show”.
//
//	{"aps":{"alert":{"action":action}}}
func (p *Payload) AlertAction(action string) *Payload {
	p.aps().alert().Action = action
	return p
}

// AlertActionLocKey sets the aps alert action localization key on the payload.
// This is the the string used as a key to get a localized string in the current
// localization to use for the notfication right button’s title instead of
// “View”. See Localized Formatted Strings in Apple documentation for more
// information.
//
//	{"aps":{"alert":{"action-loc-key":key}}}
func (p *Payload) AlertActionLocKey(key string) *Payload {
	p.aps().alert().ActionLocKey = key
	return p
}

// AlertSummaryArg sets the aps alert summary arg key on the payload.
// This is the string that is used as a key to fill in an argument
// at the bottom of a notification to provide more context, such as
// a name associated with the sender of the notification.
//
//	{"aps":{"alert":{"summary-arg":key}}}
func (p *Payload) AlertSummaryArg(key string) *Payload {
	p.aps().alert().SummaryArg = key
	return p
}

// AlertSummaryArgCount sets the aps alert summary arg count key on the payload.
// This integer sets a custom "weight" on the notification, effectively
// allowing a notification to be viewed internally as two. For example if
// a notification encompasses 3 messages, you can set it to 3.
//
//	{"aps":{"alert":{"summary-arg-count":key}}}
func (p *Payload) AlertSummaryArgCount(key int) *Payload {
	p.aps().alert().SummaryArgCount = key
	return p
}

// General

// Category sets the aps category on the payload.
// This is a string value that represents the identifier property of the
// UIMutableUserNotificationCategory object you created to define custom actions.
//
//	{"aps":{"category":category}}
func (p *Payload) Category(category string) *Payload {
	p.aps().Category = category
	return p
}

// Mdm sets the mdm on the payload.
// This is for Apple Mobile Device Management (mdm) payloads.
//
//	{"aps":{}:"mdm":mdm}
func (p *Payload) Mdm(mdm string) *Payload {
	p.content["mdm"] = mdm
	return p
}

// ThreadID sets the aps thread id on the payload.
// This is for the purpose of updating the contents of a View Controller in a
// Notification Content app extension when a new notification arrives. If a
// new notification arrives whose thread-id value matches the thread-id of the
// notification already being displayed, the didReceiveNotification method
// is called.
//
//	{"aps":{"thread-id":id}}
func (p *Payload) ThreadID(threadID string) *Payload {
	p.aps().ThreadID = threadID
	return p
}

// URLArgs sets the aps category on the payload.
// This specifies an array of values that are paired with the placeholders
// inside the urlFormatString value of your website.json file.
// See Apple Notification Programming Guide for Websites.
//
//	{"aps":{"url-args":urlArgs}}
func (p *Payload) URLArgs(urlArgs []string) *Payload {
	p.aps().URLArgs = urlArgs
	return p
}

// SoundName sets the name value on the aps sound dictionary.
// This function makes the notification a critical alert, which should be pre-approved by Apple.
// See: https://developer.apple.com/contact/request/notifications-critical-alerts-entitlement/
//
// {"aps":{"sound":{"critical":1,"name":name,"volume":1.0}}}
func (p *Payload) SoundName(name string) *Payload {
	p.aps().sound().Name = name
	return p
}

// SoundVolume sets the volume value on the aps sound dictionary.
// This function makes the notification a critical alert, which should be pre-approved by Apple.
// See: https://developer.apple.com/contact/request/notifications-critical-alerts-entitlement/
//
// {"aps":{"sound":{"critical":1,"name":"default","volume":volume}}}
func (p *Payload) SoundVolume(volume float32) *Payload {
	p.aps().sound().Volume = volume
	return p
}

// MarshalJSON returns the JSON encoded version of the Payload
func (p *Payload) MarshalJSON() ([]byte, error) {
	return json.Marshal(p.content)
}

func (p *Payload) aps() *aps {
	return p.content["aps"].(*aps)
}

func (a *aps) alert() *alert {
	if _, ok := a.Alert.(*alert); !ok {
		a.Alert = &alert{}
	}
	return a.Alert.(*alert)
}

func (a *aps) sound() *sound {
	if _, ok := a.Sound.(*sound); !ok {
		a.Sound = &sound{Critical: 1, Name: "default", Volume: 1.0}
	}
	return a.Sound.(*sound)
}
<commit_msg>Docs fix (#143)<commit_after>// Package payload is a helper package which contains a payload
// builder to make constructing notification payloads easier.
package payload

import "encoding/json"

// Payload represents a notification which holds the content that will be
// marshalled as JSON.
type Payload struct {
	content map[string]interface{}
}

type aps struct {
	Alert            interface{} `json:"alert,omitempty"`
	Badge            interface{} `json:"badge,omitempty"`
	Category         string      `json:"category,omitempty"`
	ContentAvailable int         `json:"content-available,omitempty"`
	MutableContent   int         `json:"mutable-content,omitempty"`
	Sound            interface{} `json:"sound,omitempty"`
	ThreadID         string      `json:"thread-id,omitempty"`
	URLArgs          []string    `json:"url-args,omitempty"`
}

type alert struct {
	Action          string   `json:"action,omitempty"`
	ActionLocKey    string   `json:"action-loc-key,omitempty"`
	Body            string   `json:"body,omitempty"`
	LaunchImage     string   `json:"launch-image,omitempty"`
	LocArgs         []string `json:"loc-args,omitempty"`
	LocKey          string   `json:"loc-key,omitempty"`
	Title           string   `json:"title,omitempty"`
	Subtitle        string   `json:"subtitle,omitempty"`
	TitleLocArgs    []string `json:"title-loc-args,omitempty"`
	TitleLocKey     string   `json:"title-loc-key,omitempty"`
	SummaryArg      string   `json:"summary-arg,omitempty"`
	SummaryArgCount int      `json:"summary-arg-count,omitempty"`
}

type sound struct {
	Critical int     `json:"critical,omitempty"`
	Name     string  `json:"name,omitempty"`
	Volume   float32 `json:"volume,omitempty"`
}

// NewPayload returns a new Payload struct
func NewPayload() *Payload {
	return &Payload{
		map[string]interface{}{
			"aps": &aps{},
		},
	}
}

// Alert sets the aps alert on the payload.
// This will display a notification alert message to the user.
//
//	{"aps":{"alert":alert}}`
func (p *Payload) Alert(alert interface{}) *Payload {
	p.aps().Alert = alert
	return p
}

// Badge sets the aps badge on the payload.
// This will display a numeric badge on the app icon.
//
//	{"aps":{"badge":b}}
func (p *Payload) Badge(b int) *Payload {
	p.aps().Badge = b
	return p
}

// ZeroBadge sets the aps badge on the payload to 0.
// This will clear the badge on the app icon.
//
//	{"aps":{"badge":0}}
func (p *Payload) ZeroBadge() *Payload {
	p.aps().Badge = 0
	return p
}

// UnsetBadge removes the badge attribute from the payload.
// This will leave the badge on the app icon unchanged.
// If you wish to clear the app icon badge, use ZeroBadge() instead.
//
//	{"aps":{}}
func (p *Payload) UnsetBadge() *Payload {
	p.aps().Badge = nil
	return p
}

// Sound sets the aps sound on the payload.
// This will play a sound from the app bundle, or the default sound otherwise.
//
//	{"aps":{"sound":sound}}
func (p *Payload) Sound(sound interface{}) *Payload {
	p.aps().Sound = sound
	return p
}

// ContentAvailable sets the aps content-available on the payload to 1.
// This will indicate to the app that there is new content available to download
// and launch the app in the background.
//
//	{"aps":{"content-available":1}}
func (p *Payload) ContentAvailable() *Payload {
	p.aps().ContentAvailable = 1
	return p
}

// MutableContent sets the aps mutable-content on the payload to 1.
// This will indicate to the to the system to call your Notification Service
// extension to mutate or replace the notification's content.
//
//	{"aps":{"mutable-content":1}}
func (p *Payload) MutableContent() *Payload {
	p.aps().MutableContent = 1
	return p
}

// Custom payload

// Custom sets a custom key and value on the payload.
// This will add custom key/value data to the notification payload at root level.
//
//	{"aps":{}, key:value}
func (p *Payload) Custom(key string, val interface{}) *Payload {
	p.content[key] = val
	return p
}

// Alert dictionary

// AlertTitle sets the aps alert title on the payload.
// This will display a short string describing the purpose of the notification.
// Apple Watch & Safari display this string as part of the notification interface.
//
//	{"aps":{"alert":{"title":title}}}
func (p *Payload) AlertTitle(title string) *Payload {
	p.aps().alert().Title = title
	return p
}

// AlertTitleLocKey sets the aps alert title localization key on the payload.
// This is the key to a title string in the Localizable.strings file for the
// current localization. See Localized Formatted Strings in Apple documentation
// for more information.
//
//	{"aps":{"alert":{"title-loc-key":key}}}
func (p *Payload) AlertTitleLocKey(key string) *Payload {
	p.aps().alert().TitleLocKey = key
	return p
}

// AlertTitleLocArgs sets the aps alert title localization args on the payload.
// These are the variable string values to appear in place of the format
// specifiers in title-loc-key. See Localized Formatted Strings in Apple
// documentation for more information.
//
//	{"aps":{"alert":{"title-loc-args":args}}}
func (p *Payload) AlertTitleLocArgs(args []string) *Payload {
	p.aps().alert().TitleLocArgs = args
	return p
}

// AlertSubtitle sets the aps alert subtitle on the payload.
// This will display a short string describing the purpose of the notification.
// Apple Watch & Safari display this string as part of the notification interface.
//
//	{"aps":{"alert":{"subtitle":"subtitle"}}}
func (p *Payload) AlertSubtitle(subtitle string) *Payload {
	p.aps().alert().Subtitle = subtitle
	return p
}

// AlertBody sets the aps alert body on the payload.
// This is the text of the alert message.
//
//	{"aps":{"alert":{"body":body}}}
func (p *Payload) AlertBody(body string) *Payload {
	p.aps().alert().Body = body
	return p
}

// AlertLaunchImage sets the aps launch image on the payload.
// This is the filename of an image file in the app bundle. The image is used
// as the launch image when users tap the action button or move the action
// slider.
//
//	{"aps":{"alert":{"launch-image":image}}}
func (p *Payload) AlertLaunchImage(image string) *Payload {
	p.aps().alert().LaunchImage = image
	return p
}

// AlertLocArgs sets the aps alert localization args on the payload.
// These are the variable string values to appear in place of the format
// specifiers in loc-key. See Localized Formatted Strings in Apple
// documentation for more information.
//
//  {"aps":{"alert":{"loc-args":args}}}
func (p *Payload) AlertLocArgs(args []string) *Payload {
	p.aps().alert().LocArgs = args
	return p
}

// AlertLocKey sets the aps alert localization key on the payload.
// This is the key to an alert-message string in the Localizable.strings file
// for the current localization. See Localized Formatted Strings in Apple
// documentation for more information.
//
//	{"aps":{"alert":{"loc-key":key}}}
func (p *Payload) AlertLocKey(key string) *Payload {
	p.aps().alert().LocKey = key
	return p
}

// AlertAction sets the aps alert action on the payload.
// This is the label of the action button, if the user sets the notifications
// to appear as alerts. This label should be succinct, such as “Details” or
// “Read more”. If omitted, the default value is “Show”.
//
//	{"aps":{"alert":{"action":action}}}
func (p *Payload) AlertAction(action string) *Payload {
	p.aps().alert().Action = action
	return p
}

// AlertActionLocKey sets the aps alert action localization key on the payload.
// This is the the string used as a key to get a localized string in the current
// localization to use for the notfication right button’s title instead of
// “View”. See Localized Formatted Strings in Apple documentation for more
// information.
//
//	{"aps":{"alert":{"action-loc-key":key}}}
func (p *Payload) AlertActionLocKey(key string) *Payload {
	p.aps().alert().ActionLocKey = key
	return p
}

// AlertSummaryArg sets the aps alert summary arg key on the payload.
// This is the string that is used as a key to fill in an argument
// at the bottom of a notification to provide more context, such as
// a name associated with the sender of the notification.
//
//	{"aps":{"alert":{"summary-arg":key}}}
func (p *Payload) AlertSummaryArg(key string) *Payload {
	p.aps().alert().SummaryArg = key
	return p
}

// AlertSummaryArgCount sets the aps alert summary arg count key on the payload.
// This integer sets a custom "weight" on the notification, effectively
// allowing a notification to be viewed internally as two. For example if
// a notification encompasses 3 messages, you can set it to 3.
//
//	{"aps":{"alert":{"summary-arg-count":key}}}
func (p *Payload) AlertSummaryArgCount(key int) *Payload {
	p.aps().alert().SummaryArgCount = key
	return p
}

// General

// Category sets the aps category on the payload.
// This is a string value that represents the identifier property of the
// UIMutableUserNotificationCategory object you created to define custom actions.
//
//	{"aps":{"category":category}}
func (p *Payload) Category(category string) *Payload {
	p.aps().Category = category
	return p
}

// Mdm sets the mdm on the payload.
// This is for Apple Mobile Device Management (mdm) payloads.
//
//	{"aps":{}:"mdm":mdm}
func (p *Payload) Mdm(mdm string) *Payload {
	p.content["mdm"] = mdm
	return p
}

// ThreadID sets the aps thread id on the payload.
// This is for the purpose of updating the contents of a View Controller in a
// Notification Content app extension when a new notification arrives. If a
// new notification arrives whose thread-id value matches the thread-id of the
// notification already being displayed, the didReceiveNotification method
// is called.
//
//	{"aps":{"thread-id":id}}
func (p *Payload) ThreadID(threadID string) *Payload {
	p.aps().ThreadID = threadID
	return p
}

// URLArgs sets the aps category on the payload.
// This specifies an array of values that are paired with the placeholders
// inside the urlFormatString value of your website.json file.
// See Apple Notification Programming Guide for Websites.
//
//	{"aps":{"url-args":urlArgs}}
func (p *Payload) URLArgs(urlArgs []string) *Payload {
	p.aps().URLArgs = urlArgs
	return p
}

// SoundName sets the name value on the aps sound dictionary.
// This function makes the notification a critical alert, which should be pre-approved by Apple.
// See: https://developer.apple.com/contact/request/notifications-critical-alerts-entitlement/
//
// {"aps":{"sound":{"critical":1,"name":name,"volume":1.0}}}
func (p *Payload) SoundName(name string) *Payload {
	p.aps().sound().Name = name
	return p
}

// SoundVolume sets the volume value on the aps sound dictionary.
// This function makes the notification a critical alert, which should be pre-approved by Apple.
// See: https://developer.apple.com/contact/request/notifications-critical-alerts-entitlement/
//
// {"aps":{"sound":{"critical":1,"name":"default","volume":volume}}}
func (p *Payload) SoundVolume(volume float32) *Payload {
	p.aps().sound().Volume = volume
	return p
}

// MarshalJSON returns the JSON encoded version of the Payload
func (p *Payload) MarshalJSON() ([]byte, error) {
	return json.Marshal(p.content)
}

func (p *Payload) aps() *aps {
	return p.content["aps"].(*aps)
}

func (a *aps) alert() *alert {
	if _, ok := a.Alert.(*alert); !ok {
		a.Alert = &alert{}
	}
	return a.Alert.(*alert)
}

func (a *aps) sound() *sound {
	if _, ok := a.Sound.(*sound); !ok {
		a.Sound = &sound{Critical: 1, Name: "default", Volume: 1.0}
	}
	return a.Sound.(*sound)
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2016 Red Hat, Inc.
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 */

package probes

import (
	"os"
	"strings"

	"github.com/redhat-cip/skydive/api"
	"github.com/redhat-cip/skydive/logging"
	"github.com/redhat-cip/skydive/topology"
	"github.com/redhat-cip/skydive/topology/graph"
)

type OnDemandProbeListener struct {
	graph.DefaultGraphListener
	Graph          *graph.Graph
	Probes         *FlowProbeBundle
	CaptureHandler api.ApiHandler
	watcher        api.StoppableWatcher
	host           string
}

type FlowProbe interface {
	RegisterProbe(n *graph.Node, capture *api.Capture) error
	UnregisterProbe(n *graph.Node) error
}

func (o *OnDemandProbeListener) probeFromType(n *graph.Node) FlowProbe {
	var probeName string

	switch n.Metadata()["Type"] {
	case "ovsbridge":
		probeName = "ovssflow"
	default:
		probeName = "pcap"
	}

	probe := o.Probes.GetProbe(probeName)
	return probe.(FlowProbe)
}

func (o *OnDemandProbeListener) registerProbe(n *graph.Node, capture *api.Capture) {
	fprobe := o.probeFromType(n)
	if err := fprobe.RegisterProbe(n, capture); err != nil {
		logging.GetLogger().Debugf("Failed to register flow probe: %s", err.Error())
	}
}

func (o *OnDemandProbeListener) unregisterProbe(n *graph.Node) {
	fprobe := o.probeFromType(n)
	if err := fprobe.UnregisterProbe(n); err != nil {
		logging.GetLogger().Debugf("Failed to unregister flow probe: %s", err.Error())
	}
}

func (o *OnDemandProbeListener) OnNodeAdded(n *graph.Node) {
	nodes := o.Graph.LookupShortestPath(n, graph.Metadata{"Type": "host"}, topology.IsOwnershipEdge)
	if len(nodes) == 0 {
		return
	}

	path := topology.NodePath{nodes}.Marshal()

	var capture api.ApiResource
	var ok bool
	if capture, ok = o.CaptureHandler.Get(path); !ok {
		// try using the wildcard instead of the host
		wildcard := "*/" + topology.NodePath{nodes[:len(nodes)-1]}.Marshal()
		if capture, ok = o.CaptureHandler.Get(wildcard); !ok {
			return
		}
	}

	o.registerProbe(n, capture.(*api.Capture))
}

func (o *OnDemandProbeListener) OnNodeUpdated(n *graph.Node) {
	o.OnNodeAdded(n)
}

func (o *OnDemandProbeListener) OnEdgeAdded(e *graph.Edge) {
	parent, child := o.Graph.GetEdgeNodes(e)
	if parent == nil || child == nil {
		return
	}

	if parent.Metadata()["Type"] == "ovsbridge" {
		o.OnNodeAdded(parent)
		return
	}

	if child.Metadata()["Type"] == "ovsbridge" {
		o.OnNodeAdded(child)
		return
	}
}

func (o *OnDemandProbeListener) OnNodeDeleted(n *graph.Node) {
	o.unregisterProbe(n)
}

func (o *OnDemandProbeListener) onCaptureAdded(probePath string, capture *api.Capture) {
	o.Graph.Lock()
	defer o.Graph.Unlock()

	if node := topology.LookupNodeFromNodePathString(o.Graph, probePath); node != nil {
		o.registerProbe(node, capture)
	}
}

func (o *OnDemandProbeListener) onCaptureDeleted(probePath string) {
	o.Graph.Lock()
	defer o.Graph.Unlock()

	if node := topology.LookupNodeFromNodePathString(o.Graph, probePath); node != nil {
		o.unregisterProbe(node)
	}
}

func (o *OnDemandProbeListener) probePathFromID(id string) string {
	return strings.Replace(id, "*", o.host+"[Type=host]", 1)
}

func (o *OnDemandProbeListener) onApiWatcherEvent(action string, id string, resource api.ApiResource) {
	logging.GetLogger().Debugf("New watcher event %s for %s", action, id)
	capture := resource.(*api.Capture)
	switch action {
	case "init", "create", "set", "update":
		o.onCaptureAdded(o.probePathFromID(id), capture)
	case "expire", "delete":
		o.onCaptureDeleted(o.probePathFromID(id))
	}
}

func (o *OnDemandProbeListener) Start() error {
	o.watcher = o.CaptureHandler.AsyncWatch(o.onApiWatcherEvent)

	o.Graph.AddEventListener(o)

	return nil
}

func (o *OnDemandProbeListener) Stop() {
	o.watcher.Stop()
}

func NewOnDemandProbeListener(fb *FlowProbeBundle, g *graph.Graph, ch api.ApiHandler) (*OnDemandProbeListener, error) {
	h, err := os.Hostname()
	if err != nil {
		return nil, err
	}

	return &OnDemandProbeListener{
		Graph:          g,
		Probes:         fb,
		CaptureHandler: ch,
		host:           h,
	}, nil
}
<commit_msg>Fix stacktrace when register/unregister a probe not defined in conf<commit_after>/*
 * Copyright (C) 2016 Red Hat, Inc.
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 */

package probes

import (
	"os"
	"strings"

	"github.com/redhat-cip/skydive/api"
	"github.com/redhat-cip/skydive/logging"
	"github.com/redhat-cip/skydive/topology"
	"github.com/redhat-cip/skydive/topology/graph"
)

type OnDemandProbeListener struct {
	graph.DefaultGraphListener
	Graph          *graph.Graph
	Probes         *FlowProbeBundle
	CaptureHandler api.ApiHandler
	watcher        api.StoppableWatcher
	host           string
}

type FlowProbe interface {
	RegisterProbe(n *graph.Node, capture *api.Capture) error
	UnregisterProbe(n *graph.Node) error
}

func (o *OnDemandProbeListener) probeFromType(n *graph.Node) FlowProbe {
	var probeName string

	switch n.Metadata()["Type"] {
	case "ovsbridge":
		probeName = "ovssflow"
	default:
		probeName = "pcap"
	}

	probe := o.Probes.GetProbe(probeName)
	if probe == nil {
		return nil
	}

	return probe.(FlowProbe)
}

func (o *OnDemandProbeListener) registerProbe(n *graph.Node, capture *api.Capture) {
	fprobe := o.probeFromType(n)
	if fprobe == nil {
		logging.GetLogger().Errorf("Failed to register flow probe, unknown type")
		return
	}

	if err := fprobe.RegisterProbe(n, capture); err != nil {
		logging.GetLogger().Debugf("Failed to register flow probe: %s", err.Error())
	}
}

func (o *OnDemandProbeListener) unregisterProbe(n *graph.Node) {
	fprobe := o.probeFromType(n)
	if fprobe == nil {
		return
	}

	if err := fprobe.UnregisterProbe(n); err != nil {
		logging.GetLogger().Debugf("Failed to unregister flow probe: %s", err.Error())
	}
}

func (o *OnDemandProbeListener) OnNodeAdded(n *graph.Node) {
	nodes := o.Graph.LookupShortestPath(n, graph.Metadata{"Type": "host"}, topology.IsOwnershipEdge)
	if len(nodes) == 0 {
		return
	}

	path := topology.NodePath{nodes}.Marshal()

	var capture api.ApiResource
	var ok bool
	if capture, ok = o.CaptureHandler.Get(path); !ok {
		// try using the wildcard instead of the host
		wildcard := "*/" + topology.NodePath{nodes[:len(nodes)-1]}.Marshal()
		if capture, ok = o.CaptureHandler.Get(wildcard); !ok {
			return
		}
	}

	o.registerProbe(n, capture.(*api.Capture))
}

func (o *OnDemandProbeListener) OnNodeUpdated(n *graph.Node) {
	o.OnNodeAdded(n)
}

func (o *OnDemandProbeListener) OnEdgeAdded(e *graph.Edge) {
	parent, child := o.Graph.GetEdgeNodes(e)
	if parent == nil || child == nil {
		return
	}

	if parent.Metadata()["Type"] == "ovsbridge" {
		o.OnNodeAdded(parent)
		return
	}

	if child.Metadata()["Type"] == "ovsbridge" {
		o.OnNodeAdded(child)
		return
	}
}

func (o *OnDemandProbeListener) OnNodeDeleted(n *graph.Node) {
	o.unregisterProbe(n)
}

func (o *OnDemandProbeListener) onCaptureAdded(probePath string, capture *api.Capture) {
	o.Graph.Lock()
	defer o.Graph.Unlock()

	if node := topology.LookupNodeFromNodePathString(o.Graph, probePath); node != nil {
		o.registerProbe(node, capture)
	}
}

func (o *OnDemandProbeListener) onCaptureDeleted(probePath string) {
	o.Graph.Lock()
	defer o.Graph.Unlock()

	if node := topology.LookupNodeFromNodePathString(o.Graph, probePath); node != nil {
		o.unregisterProbe(node)
	}
}

func (o *OnDemandProbeListener) probePathFromID(id string) string {
	return strings.Replace(id, "*", o.host+"[Type=host]", 1)
}

func (o *OnDemandProbeListener) onApiWatcherEvent(action string, id string, resource api.ApiResource) {
	logging.GetLogger().Debugf("New watcher event %s for %s", action, id)
	capture := resource.(*api.Capture)
	switch action {
	case "init", "create", "set", "update":
		o.onCaptureAdded(o.probePathFromID(id), capture)
	case "expire", "delete":
		o.onCaptureDeleted(o.probePathFromID(id))
	}
}

func (o *OnDemandProbeListener) Start() error {
	o.watcher = o.CaptureHandler.AsyncWatch(o.onApiWatcherEvent)

	o.Graph.AddEventListener(o)

	return nil
}

func (o *OnDemandProbeListener) Stop() {
	o.watcher.Stop()
}

func NewOnDemandProbeListener(fb *FlowProbeBundle, g *graph.Graph, ch api.ApiHandler) (*OnDemandProbeListener, error) {
	h, err := os.Hostname()
	if err != nil {
		return nil, err
	}

	return &OnDemandProbeListener{
		Graph:          g,
		Probes:         fb,
		CaptureHandler: ch,
		host:           h,
	}, nil
}
<|endoftext|>


<commit_before>package g

import (
	"encoding/json"
	"log"
	"sync"

	"github.com/toolkits/file"
)

type HttpConfig struct {
	Enabled bool   `json:"enabled"`
	Listen  string `json:"listen"`
}

type RpcConfig struct {
	Enabled bool   `json:"enabled"`
	Listen  string `json:"listen"`
}

type RRDConfig struct {
	Storage string `json:"storage"`
}

type DBConfig struct {
	Dsn     string `json:"dsn"`
	MaxIdle int    `json:"maxIdle"`
}

type GlobalConfig struct {
	Pid     string      `json:"pid"`
	Debug   bool        `json:"debug"`
	Http    *HttpConfig `json:"http"`
	Rpc     *RpcConfig  `json:"rpc"`
	RRD     *RRDConfig  `json:"rrd"`
	DB      *DBConfig   `json:"db"`
	Migrate struct {
		Enabled  bool              `json:"enabled"`
		Replicas int               `json:"replicas"`
		Cluster  map[string]string `json:"cluster"`
	} `json:"migrate"`
}

var (
	ConfigFile string
	config     *GlobalConfig
	configLock = new(sync.RWMutex)
)

func Config() *GlobalConfig {
	configLock.RLock()
	defer configLock.RUnlock()
	return config
}

func ParseConfig(cfg string) {
	if cfg == "" {
		log.Fatalln("config file not specified: use -c $filename")
	}

	if !file.IsExist(cfg) {
		log.Fatalln("config file specified not found:", cfg)
	}

	ConfigFile = cfg

	configContent, err := file.ToTrimString(cfg)
	if err != nil {
		log.Fatalln("read config file", cfg, "error:", err.Error())
	}

	var c GlobalConfig
	err = json.Unmarshal([]byte(configContent), &c)
	if err != nil {
		log.Fatalln("parse config file", cfg, "error:", err.Error())
	}

	if c.Migrate.Enabled && len(c.Migrate.Cluster) == 0 {
		c.Migrate.Enabled = false
	}

	// set config
	configLock.Lock()
	defer configLock.Unlock()
	config = &c

	log.Println("g.ParseConfig ok, file", cfg)
}
<commit_msg>remove config.lock<commit_after>package g

import (
	"encoding/json"
	"log"
	"sync/atomic"
	"unsafe"

	"github.com/toolkits/file"
)

type HttpConfig struct {
	Enabled bool   `json:"enabled"`
	Listen  string `json:"listen"`
}

type RpcConfig struct {
	Enabled bool   `json:"enabled"`
	Listen  string `json:"listen"`
}

type RRDConfig struct {
	Storage string `json:"storage"`
}

type DBConfig struct {
	Dsn     string `json:"dsn"`
	MaxIdle int    `json:"maxIdle"`
}

type GlobalConfig struct {
	Pid     string      `json:"pid"`
	Debug   bool        `json:"debug"`
	Http    *HttpConfig `json:"http"`
	Rpc     *RpcConfig  `json:"rpc"`
	RRD     *RRDConfig  `json:"rrd"`
	DB      *DBConfig   `json:"db"`
	Migrate struct {
		Enabled  bool              `json:"enabled"`
		Replicas int               `json:"replicas"`
		Cluster  map[string]string `json:"cluster"`
	} `json:"migrate"`
}

var (
	ConfigFile string
	ptr        unsafe.Pointer
)

func Config() *GlobalConfig {
	return (*GlobalConfig)(atomic.LoadPointer(&ptr))
}

func ParseConfig(cfg string) {
	if cfg == "" {
		log.Fatalln("config file not specified: use -c $filename")
	}

	if !file.IsExist(cfg) {
		log.Fatalln("config file specified not found:", cfg)
	}

	ConfigFile = cfg

	configContent, err := file.ToTrimString(cfg)
	if err != nil {
		log.Fatalln("read config file", cfg, "error:", err.Error())
	}

	var c GlobalConfig
	err = json.Unmarshal([]byte(configContent), &c)
	if err != nil {
		log.Fatalln("parse config file", cfg, "error:", err.Error())
	}

	if c.Migrate.Enabled && len(c.Migrate.Cluster) == 0 {
		c.Migrate.Enabled = false
	}

	// set config
	atomic.StorePointer(&ptr, unsafe.Pointer(&c))

	log.Println("g.ParseConfig ok, file", cfg)
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package chartutil

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	"k8s.io/helm/pkg/proto/hapi/chart"
)

const (
	// ChartfileName is the default Chart file name.
	ChartfileName = "Chart.yaml"
	// ValuesfileName is the default values file name.
	ValuesfileName = "values.yaml"
	// TemplatesDir is the relative directory name for templates.
	TemplatesDir = "templates"
	// ChartsDir is the relative directory name for charts dependencies.
	ChartsDir = "charts"
	// IgnorefileName is the name of the Helm ignore file.
	IgnorefileName = ".helmignore"
	// DeploymentName is the name of the example deployment file.
	DeploymentName = "deployment.yaml"
	// ServiceName is the name of the example service file.
	ServiceName = "service.yaml"
	// NotesName is the name of the example NOTES.txt file.
	NotesName = "NOTES.txt"
	// HelpersName is the name of the example NOTES.txt file.
	HelpersName = "_helpers.tpl"
)

const defaultValues = `# Default values for %s.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
image:
  repository: nginx
  tag: stable
  pullPolicy: IfNotPresent
service:
  name: nginx
  type: ClusterIP
  externalPort: 80
  internalPort: 80
resources:
  limits:
    cpu: 100m
    memory: 128Mi
  requests:
    cpu: 100m
    memory: 128Mi

`

const defaultIgnore = `# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*~
# Various IDEs
.project
.idea/
*.tmproj
`

const defaultDeployment = `apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: {{ template "fullname" . }}
  labels:
    chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
spec:
  replicas: {{ .Values.replicaCount }}
  template:
    metadata:
      labels:
        app: {{ template "fullname" . }}
    spec:
      containers:
      - name: nginx
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
        imagePullPolicy: {{ .Values.image.pullPolicy }}
        ports:
        - containerPort: {{ .Values.service.internalPort }}
        livenessProbe:
          httpGet:
            path: /
            port: {{ .Values.service.internalPort }}
        readinessProbe:
          httpGet:
            path: /
            port: {{ .Values.service.internalPort }}
        resources:
{{ toYaml .Values.resources | indent 12 }}
`

const defaultService = `apiVersion: v1
kind: Service
metadata:
  name: {{ template "fullname" . }}
  labels:
    chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
spec:
  type: {{ .Values.service.type }}
  ports:
  - port: {{ .Values.service.externalPort }}
    targetPort: {{ .Values.service.internalPort }}
    protocol: TCP
    name: {{ .Values.service.name }}
  selector:
    app: {{ template "fullname" . }}
`

const defaultNotes = `1. Get the application URL by running these commands:
{{- if contains "NodePort" .Values.service.type }}
  export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "fullname" . }})
  export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
  echo http://$NODE_IP:$NODE_PORT/login
{{- else if contains "LoadBalancer" .Values.service.type }}
     NOTE: It may take a few minutes for the LoadBalancer IP to be available.
           You can watch the status of by running 'kubectl get svc -w {{ template "fullname" . }}'
  export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
  echo http://$SERVICE_IP:{{ .Values.Master.ServicePort }}
{{- else if contains "ClusterIP"  .Values.service.type }}
  export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "component={{ template "fullname" . }}-master" -o jsonpath="{.items[0].metadata.name}")
  echo http://127.0.0.1:{{ .Values.service.externalPort }}
  kubectl port-forward $POD_NAME {{ .Values.service.externalPort }}:{{ .Values.service.externalPort }}
{{- end }}
`

const defaultHelpers = `{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 24 -}}
{{- end -}}

{{/*
Create a default fully qualified app name.
We truncate at 24 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "fullname" -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- printf "%s-%s" .Release.Name $name | trunc 24 -}}
{{- end -}}
`

// Create creates a new chart in a directory.
//
// Inside of dir, this will create a directory based on the name of
// chartfile.Name. It will then write the Chart.yaml into this directory and
// create the (empty) appropriate directories.
//
// The returned string will point to the newly created directory. It will be
// an absolute path, even if the provided base directory was relative.
//
// If dir does not exist, this will return an error.
// If Chart.yaml or any directories cannot be created, this will return an
// error. In such a case, this will attempt to clean up by removing the
// new chart directory.
func Create(chartfile *chart.Metadata, dir string) (string, error) {
	path, err := filepath.Abs(dir)
	if err != nil {
		return path, err
	}

	if fi, err := os.Stat(path); err != nil {
		return path, err
	} else if !fi.IsDir() {
		return path, fmt.Errorf("no such directory %s", path)
	}

	n := chartfile.Name
	cdir := filepath.Join(path, n)
	if fi, err := os.Stat(cdir); err == nil && !fi.IsDir() {
		return cdir, fmt.Errorf("file %s already exists and is not a directory", cdir)
	}
	if err := os.MkdirAll(cdir, 0755); err != nil {
		return cdir, err
	}

	if err := SaveChartfile(filepath.Join(cdir, ChartfileName), chartfile); err != nil {
		return cdir, err
	}

	val := []byte(fmt.Sprintf(defaultValues, chartfile.Name))
	if err := ioutil.WriteFile(filepath.Join(cdir, ValuesfileName), val, 0644); err != nil {
		return cdir, err
	}

	val = []byte(defaultIgnore)
	if err := ioutil.WriteFile(filepath.Join(cdir, IgnorefileName), val, 0644); err != nil {
		return cdir, err
	}

	for _, d := range []string{TemplatesDir, ChartsDir} {
		if err := os.MkdirAll(filepath.Join(cdir, d), 0755); err != nil {
			return cdir, err
		}
	}

	// Write out deployment.yaml
	val = []byte(defaultDeployment)
	if err := ioutil.WriteFile(filepath.Join(cdir, TemplatesDir, DeploymentName), val, 0644); err != nil {
		return cdir, err
	}

	// Write out service.yaml
	val = []byte(defaultService)
	if err := ioutil.WriteFile(filepath.Join(cdir, TemplatesDir, ServiceName), val, 0644); err != nil {
		return cdir, err
	}

	// Write out NOTES.txt
	val = []byte(defaultNotes)
	if err := ioutil.WriteFile(filepath.Join(cdir, TemplatesDir, NotesName), val, 0644); err != nil {
		return cdir, err
	}

	// Write out _helpers.tpl
	val = []byte(defaultHelpers)
	return cdir, ioutil.WriteFile(filepath.Join(cdir, TemplatesDir, HelpersName), val, 0644)
}
<commit_msg>Use chart name for container name<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package chartutil

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	"k8s.io/helm/pkg/proto/hapi/chart"
)

const (
	// ChartfileName is the default Chart file name.
	ChartfileName = "Chart.yaml"
	// ValuesfileName is the default values file name.
	ValuesfileName = "values.yaml"
	// TemplatesDir is the relative directory name for templates.
	TemplatesDir = "templates"
	// ChartsDir is the relative directory name for charts dependencies.
	ChartsDir = "charts"
	// IgnorefileName is the name of the Helm ignore file.
	IgnorefileName = ".helmignore"
	// DeploymentName is the name of the example deployment file.
	DeploymentName = "deployment.yaml"
	// ServiceName is the name of the example service file.
	ServiceName = "service.yaml"
	// NotesName is the name of the example NOTES.txt file.
	NotesName = "NOTES.txt"
	// HelpersName is the name of the example NOTES.txt file.
	HelpersName = "_helpers.tpl"
)

const defaultValues = `# Default values for %s.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
image:
  repository: nginx
  tag: stable
  pullPolicy: IfNotPresent
service:
  name: nginx
  type: ClusterIP
  externalPort: 80
  internalPort: 80
resources:
  limits:
    cpu: 100m
    memory: 128Mi
  requests:
    cpu: 100m
    memory: 128Mi

`

const defaultIgnore = `# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*~
# Various IDEs
.project
.idea/
*.tmproj
`

const defaultDeployment = `apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: {{ template "fullname" . }}
  labels:
    chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
spec:
  replicas: {{ .Values.replicaCount }}
  template:
    metadata:
      labels:
        app: {{ template "fullname" . }}
    spec:
      containers:
      - name: {{ .Chart.Name }}
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
        imagePullPolicy: {{ .Values.image.pullPolicy }}
        ports:
        - containerPort: {{ .Values.service.internalPort }}
        livenessProbe:
          httpGet:
            path: /
            port: {{ .Values.service.internalPort }}
        readinessProbe:
          httpGet:
            path: /
            port: {{ .Values.service.internalPort }}
        resources:
{{ toYaml .Values.resources | indent 12 }}
`

const defaultService = `apiVersion: v1
kind: Service
metadata:
  name: {{ template "fullname" . }}
  labels:
    chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
spec:
  type: {{ .Values.service.type }}
  ports:
  - port: {{ .Values.service.externalPort }}
    targetPort: {{ .Values.service.internalPort }}
    protocol: TCP
    name: {{ .Values.service.name }}
  selector:
    app: {{ template "fullname" . }}
`

const defaultNotes = `1. Get the application URL by running these commands:
{{- if contains "NodePort" .Values.service.type }}
  export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "fullname" . }})
  export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
  echo http://$NODE_IP:$NODE_PORT/login
{{- else if contains "LoadBalancer" .Values.service.type }}
     NOTE: It may take a few minutes for the LoadBalancer IP to be available.
           You can watch the status of by running 'kubectl get svc -w {{ template "fullname" . }}'
  export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
  echo http://$SERVICE_IP:{{ .Values.Master.ServicePort }}
{{- else if contains "ClusterIP"  .Values.service.type }}
  export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "component={{ template "fullname" . }}-master" -o jsonpath="{.items[0].metadata.name}")
  echo http://127.0.0.1:{{ .Values.service.externalPort }}
  kubectl port-forward $POD_NAME {{ .Values.service.externalPort }}:{{ .Values.service.externalPort }}
{{- end }}
`

const defaultHelpers = `{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 24 -}}
{{- end -}}

{{/*
Create a default fully qualified app name.
We truncate at 24 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "fullname" -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- printf "%s-%s" .Release.Name $name | trunc 24 -}}
{{- end -}}
`

// Create creates a new chart in a directory.
//
// Inside of dir, this will create a directory based on the name of
// chartfile.Name. It will then write the Chart.yaml into this directory and
// create the (empty) appropriate directories.
//
// The returned string will point to the newly created directory. It will be
// an absolute path, even if the provided base directory was relative.
//
// If dir does not exist, this will return an error.
// If Chart.yaml or any directories cannot be created, this will return an
// error. In such a case, this will attempt to clean up by removing the
// new chart directory.
func Create(chartfile *chart.Metadata, dir string) (string, error) {
	path, err := filepath.Abs(dir)
	if err != nil {
		return path, err
	}

	if fi, err := os.Stat(path); err != nil {
		return path, err
	} else if !fi.IsDir() {
		return path, fmt.Errorf("no such directory %s", path)
	}

	n := chartfile.Name
	cdir := filepath.Join(path, n)
	if fi, err := os.Stat(cdir); err == nil && !fi.IsDir() {
		return cdir, fmt.Errorf("file %s already exists and is not a directory", cdir)
	}
	if err := os.MkdirAll(cdir, 0755); err != nil {
		return cdir, err
	}

	if err := SaveChartfile(filepath.Join(cdir, ChartfileName), chartfile); err != nil {
		return cdir, err
	}

	val := []byte(fmt.Sprintf(defaultValues, chartfile.Name))
	if err := ioutil.WriteFile(filepath.Join(cdir, ValuesfileName), val, 0644); err != nil {
		return cdir, err
	}

	val = []byte(defaultIgnore)
	if err := ioutil.WriteFile(filepath.Join(cdir, IgnorefileName), val, 0644); err != nil {
		return cdir, err
	}

	for _, d := range []string{TemplatesDir, ChartsDir} {
		if err := os.MkdirAll(filepath.Join(cdir, d), 0755); err != nil {
			return cdir, err
		}
	}

	// Write out deployment.yaml
	val = []byte(defaultDeployment)
	if err := ioutil.WriteFile(filepath.Join(cdir, TemplatesDir, DeploymentName), val, 0644); err != nil {
		return cdir, err
	}

	// Write out service.yaml
	val = []byte(defaultService)
	if err := ioutil.WriteFile(filepath.Join(cdir, TemplatesDir, ServiceName), val, 0644); err != nil {
		return cdir, err
	}

	// Write out NOTES.txt
	val = []byte(defaultNotes)
	if err := ioutil.WriteFile(filepath.Join(cdir, TemplatesDir, NotesName), val, 0644); err != nil {
		return cdir, err
	}

	// Write out _helpers.tpl
	val = []byte(defaultHelpers)
	return cdir, ioutil.WriteFile(filepath.Join(cdir, TemplatesDir, HelpersName), val, 0644)
}
<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"net"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"time"

	klatest "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
	kmaster "github.com/GoogleCloudPlatform/kubernetes/pkg/master"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
	etcdclient "github.com/coreos/go-etcd/etcd"
	"github.com/golang/glog"
	"github.com/spf13/cobra"

	"github.com/openshift/origin/pkg/cmd/flagtypes"
	"github.com/openshift/origin/pkg/cmd/server/etcd"
	"github.com/openshift/origin/pkg/cmd/server/kubernetes"
	"github.com/openshift/origin/pkg/cmd/server/origin"
	"github.com/openshift/origin/pkg/cmd/util"
	"github.com/openshift/origin/pkg/cmd/util/docker"
)

const longCommandDesc = `
Start an OpenShift server

This command helps you launch an OpenShift server. The default mode is all-in-one, which allows
you to run all of the components of an OpenShift system on a server with Docker. Running

    $ openshift start

will start OpenShift listening on all interfaces, launch an etcd server to store persistent
data, and launch the Kubernetes system components. The server will run in the foreground until
you terminate the process.

You may also pass an optional argument to the start command to start OpenShift in one of the
following roles:

    $ openshift start master --nodes host1,host2,host3,...

      Launches the server and control plane for OpenShift. You must pass a list of the node
      hostnames you want to watch (limitation).

    $ openshift start node --master masterIP

      Launches a new node and attempts to connect to the master on the provided IP.

You may also pass --etcd to connect to an external etcd server instead of running an integrated
instance.
`

// config is a struct that the command stores flag values into.
type config struct {
	Docker *docker.Helper

	MasterAddr flagtypes.Addr
	BindAddr   flagtypes.Addr
	EtcdAddr   flagtypes.Addr

	Hostname  string
	VolumeDir string

	EtcdDir string

	StorageVersion string

	NodeList flagtypes.StringList

	CORSAllowedOrigins flagtypes.StringList
}

func NewCommandStartServer(name string) *cobra.Command {
	cfg := &config{
		Docker:     docker.NewHelper(),
		MasterAddr: flagtypes.Addr{Value: "localhost:8080", DefaultScheme: "http", DefaultPort: 8080, AllowPrefix: true}.Default(),
		BindAddr:   flagtypes.Addr{Value: "0.0.0.0:8080", DefaultScheme: "http", DefaultPort: 8080, AllowPrefix: true}.Default(),
		EtcdAddr:   flagtypes.Addr{Value: "0.0.0.0:4001", DefaultScheme: "http", DefaultPort: 4001}.Default(),
		NodeList:   flagtypes.StringList{"127.0.0.1"},
	}

	cmd := &cobra.Command{
		Use:   fmt.Sprintf("%s [master|node]", name),
		Short: "Launch OpenShift",
		Long:  longCommandDesc,
		Run: func(c *cobra.Command, args []string) {
			if len(args) > 1 {
				glog.Fatalf("You may start an OpenShift all-in-one server with no arguments, or pass 'master' or 'node' to run in that role.")
			}

			var startEtcd, startNode, startMaster bool
			if len(args) == 1 {
				switch args[0] {
				case "master":
					startMaster = true
					startEtcd = !cfg.EtcdAddr.Provided
					defaultMasterAddress(cfg)
					glog.Infof("Starting an OpenShift master, reachable at %s (etcd: %s)", cfg.MasterAddr.String(), cfg.EtcdAddr.String())

				case "node":
					startNode = true
					defaultMasterAddress(cfg)
					glog.Infof("Starting an OpenShift node, connecting to %s (etcd: %s)", cfg.MasterAddr.String(), cfg.EtcdAddr.String())

				default:
					glog.Fatalf("You may start an OpenShift all-in-one server with no arguments, or pass 'master' or 'node' to run in that role.")
				}

			} else {
				startMaster = true
				startEtcd = !cfg.EtcdAddr.Provided
				startNode = true
				defaultMasterAddress(cfg)

				glog.Infof("Starting an OpenShift all-in-one, reachable at %s (etcd: %s)", cfg.MasterAddr.String(), cfg.EtcdAddr.String())
			}

			if startMaster {
				// update the node list to include the default node
				if len(cfg.Hostname) == 0 {
					cfg.Hostname = defaultHostname()
				}
				if len(cfg.NodeList) == 1 && cfg.NodeList[0] == "127.0.0.1" {
					cfg.NodeList[0] = cfg.Hostname
				}
				glog.Infof("Expecting %d nodes: %v", len(cfg.NodeList), cfg.NodeList)

				if startEtcd {
					etcdConfig := &etcd.Config{
						BindAddr:   cfg.EtcdAddr.URL.Host,
						MasterAddr: cfg.EtcdAddr.URL.Host,
						EtcdDir:    cfg.EtcdDir,
					}
					etcdConfig.Run()
				}

				// Connect and setup etcd interfaces
				etcdClient := getEtcdClient(cfg)
				etcdHelper, err := origin.NewEtcdHelper(cfg.StorageVersion, etcdClient)
				if err != nil {
					glog.Errorf("Error setting up server storage: %v", err)
				}
				ketcdHelper, err := kmaster.NewEtcdHelper(etcdClient.GetCluster(), klatest.Version)
				if err != nil {
					glog.Errorf("Error setting up Kubernetes server storage: %v", err)
				}

				assetAddr := net.JoinHostPort(cfg.MasterAddr.Host, "8091")

				osmaster := &origin.MasterConfig{
					BindAddr:   cfg.BindAddr.URL.Host,
					MasterAddr: cfg.MasterAddr.URL.String(),
					AssetAddr:  assetAddr,
					EtcdHelper: etcdHelper,
				}
				osmaster.EnsureKubernetesClient()
				osmaster.EnsureOpenShiftClient()
				osmaster.EnsureCORSAllowedOrigins(cfg.CORSAllowedOrigins)

				kmaster := &kubernetes.MasterConfig{
					NodeHosts:  cfg.NodeList,
					EtcdHelper: ketcdHelper,
					KubeClient: osmaster.KubeClient,
				}

				osmaster.RunAPI(kmaster)
				kmaster.RunScheduler()
				kmaster.RunReplicationController()
			}

			if startNode {
				etcdClient := getEtcdClient(cfg)

				hostname := cfg.Hostname
				if len(hostname) == 0 {
					hostname = defaultHostname()
				}

				nodeConfig := &kubernetes.NodeConfig{
					BindHost:   cfg.BindAddr.Host,
					NodeHost:   hostname,
					MasterHost: cfg.MasterAddr.URL.String(),

					VolumeDir: cfg.VolumeDir,

					NetworkContainerImage: env("KUBERNETES_NETWORK_CONTAINER_IMAGE", kubelet.NetworkContainerImage),

					EtcdClient: etcdClient,
				}

				nodeConfig.EnsureVolumeDir()
				nodeConfig.EnsureDocker(cfg.Docker)

				nodeConfig.RunProxy()
				nodeConfig.RunKubelet()
			}

			select {}
		},
	}

	flag := cmd.Flags()

	flag.Var(&cfg.BindAddr, "listen", "The address to listen for connections on (host port or URL).")
	flag.Var(&cfg.MasterAddr, "master", "The address the master can be reached on (host port or URL).")
	flag.Var(&cfg.EtcdAddr, "etcd", "The address of the etcd server (host port or URL).")

	flag.StringVar(&cfg.VolumeDir, "volume-dir", "openshift.local.volumes", "The volume storage directory.")
	flag.StringVar(&cfg.EtcdDir, "etcd-dir", "openshift.local.etcd", "The etcd data directory.")

	flag.Var(&cfg.NodeList, "nodes", "The hostnames of each node. This currently must be specified up front. Comma delimited list")
	flag.Var(&cfg.CORSAllowedOrigins, "cors-allowed-origins", "List of allowed origins for CORS, comma separated.  An allowed origin can be a regular expression to support subdomain matching.  If this list is empty CORS will not be enabled.")

	cfg.Docker.InstallFlags(flag)

	return cmd
}

// getEtcdClient creates an etcd client based on the provided config and waits until
// it is reachable.  If the server cannot be
func getEtcdClient(cfg *config) *etcdclient.Client {
	etcdServers := []string{cfg.EtcdAddr.URL.String()}
	etcdClient := etcdclient.NewClient(etcdServers)

	for i := 0; ; i += 1 {
		_, err := etcdClient.Get("/", false, false)
		if err == nil || tools.IsEtcdNotFound(err) {
			break
		}
		if i > 100 {
			glog.Fatal("Could not reach etcd: %v", err)
		}
		time.Sleep(50 * time.Millisecond)
	}

	return etcdClient
}

// defaultHostname returns the default hostname for this system.
func defaultHostname() string {
	// Note: We use exec here instead of os.Hostname() because we
	// want the FQDN, and this is the easiest way to get it.
	fqdn, err := exec.Command("hostname", "-f").Output()
	if err != nil {
		glog.Fatalf("Couldn't determine hostname: %v", err)
	}
	return strings.TrimSpace(string(fqdn))
}

// defaultMasterAddress checks for an unset master address and then attempts to use the first
// public IPv4 non-loopback address registered on this host. It will also update the
// EtcdAddr after if it was not provided.
// TODO: make me IPv6 safe
func defaultMasterAddress(cfg *config) {
	if !cfg.MasterAddr.Provided {
		// If the user specifies a bind address, and the master is not provided, use
		// the bind port by default
		port := cfg.MasterAddr.Port
		if cfg.BindAddr.Provided {
			port = cfg.BindAddr.Port
		}

		// use the default ip address for the system
		addr, err := util.DefaultLocalIP4()
		if err != nil {
			glog.Fatalf("Unable to find the public address of this master: %v", err)
		}

		masterAddr := net.JoinHostPort(addr.String(), strconv.Itoa(port))
		if err := cfg.MasterAddr.Set(masterAddr); err != nil {
			glog.Fatalf("Unable to set public address of this master: %v", err)
		}

		// Prefer to use the MasterAddr for etcd because some clients still connect to it.
		if !cfg.EtcdAddr.Provided {
			etcdAddr := net.JoinHostPort(addr.String(), strconv.Itoa(cfg.EtcdAddr.DefaultPort))
			if err := cfg.EtcdAddr.Set(etcdAddr); err != nil {
				glog.Fatalf("Unable to set public address of etcd: %v", err)
			}
		}
	} else if !cfg.EtcdAddr.Provided {
		// Etcd should be reachable on the same address that the master is (for simplicity)
		etcdAddr := net.JoinHostPort(cfg.MasterAddr.Host, strconv.Itoa(cfg.EtcdAddr.DefaultPort))
		if err := cfg.EtcdAddr.Set(etcdAddr); err != nil {
			glog.Fatalf("Unable to set public address of etcd: %v", err)
		}
	}
}

// env returns an environment variable or a default value if not specified.
func env(key string, defaultValue string) string {
	val := os.Getenv(key)
	if len(val) == 0 {
		return defaultValue
	} else {
		return val
	}
}
<commit_msg>Added missing controllers in server start<commit_after>package server

import (
	"fmt"
	"net"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"time"

	klatest "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
	kmaster "github.com/GoogleCloudPlatform/kubernetes/pkg/master"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
	etcdclient "github.com/coreos/go-etcd/etcd"
	"github.com/golang/glog"
	"github.com/spf13/cobra"

	"github.com/openshift/origin/pkg/cmd/flagtypes"
	"github.com/openshift/origin/pkg/cmd/server/etcd"
	"github.com/openshift/origin/pkg/cmd/server/kubernetes"
	"github.com/openshift/origin/pkg/cmd/server/origin"
	"github.com/openshift/origin/pkg/cmd/util"
	"github.com/openshift/origin/pkg/cmd/util/docker"
)

const longCommandDesc = `
Start an OpenShift server

This command helps you launch an OpenShift server. The default mode is all-in-one, which allows
you to run all of the components of an OpenShift system on a server with Docker. Running

    $ openshift start

will start OpenShift listening on all interfaces, launch an etcd server to store persistent
data, and launch the Kubernetes system components. The server will run in the foreground until
you terminate the process.

You may also pass an optional argument to the start command to start OpenShift in one of the
following roles:

    $ openshift start master --nodes host1,host2,host3,...

      Launches the server and control plane for OpenShift. You must pass a list of the node
      hostnames you want to watch (limitation).

    $ openshift start node --master masterIP

      Launches a new node and attempts to connect to the master on the provided IP.

You may also pass --etcd to connect to an external etcd server instead of running an integrated
instance.
`

// config is a struct that the command stores flag values into.
type config struct {
	Docker *docker.Helper

	MasterAddr flagtypes.Addr
	BindAddr   flagtypes.Addr
	EtcdAddr   flagtypes.Addr

	Hostname  string
	VolumeDir string

	EtcdDir string

	StorageVersion string

	NodeList flagtypes.StringList

	CORSAllowedOrigins flagtypes.StringList
}

func NewCommandStartServer(name string) *cobra.Command {
	cfg := &config{
		Docker:     docker.NewHelper(),
		MasterAddr: flagtypes.Addr{Value: "localhost:8080", DefaultScheme: "http", DefaultPort: 8080, AllowPrefix: true}.Default(),
		BindAddr:   flagtypes.Addr{Value: "0.0.0.0:8080", DefaultScheme: "http", DefaultPort: 8080, AllowPrefix: true}.Default(),
		EtcdAddr:   flagtypes.Addr{Value: "0.0.0.0:4001", DefaultScheme: "http", DefaultPort: 4001}.Default(),
		NodeList:   flagtypes.StringList{"127.0.0.1"},
	}

	cmd := &cobra.Command{
		Use:   fmt.Sprintf("%s [master|node]", name),
		Short: "Launch OpenShift",
		Long:  longCommandDesc,
		Run: func(c *cobra.Command, args []string) {
			if len(args) > 1 {
				glog.Fatalf("You may start an OpenShift all-in-one server with no arguments, or pass 'master' or 'node' to run in that role.")
			}

			var startEtcd, startNode, startMaster bool
			if len(args) == 1 {
				switch args[0] {
				case "master":
					startMaster = true
					startEtcd = !cfg.EtcdAddr.Provided
					defaultMasterAddress(cfg)
					glog.Infof("Starting an OpenShift master, reachable at %s (etcd: %s)", cfg.MasterAddr.String(), cfg.EtcdAddr.String())

				case "node":
					startNode = true
					defaultMasterAddress(cfg)
					glog.Infof("Starting an OpenShift node, connecting to %s (etcd: %s)", cfg.MasterAddr.String(), cfg.EtcdAddr.String())

				default:
					glog.Fatalf("You may start an OpenShift all-in-one server with no arguments, or pass 'master' or 'node' to run in that role.")
				}

			} else {
				startMaster = true
				startEtcd = !cfg.EtcdAddr.Provided
				startNode = true
				defaultMasterAddress(cfg)

				glog.Infof("Starting an OpenShift all-in-one, reachable at %s (etcd: %s)", cfg.MasterAddr.String(), cfg.EtcdAddr.String())
			}

			if startMaster {
				// update the node list to include the default node
				if len(cfg.Hostname) == 0 {
					cfg.Hostname = defaultHostname()
				}
				if len(cfg.NodeList) == 1 && cfg.NodeList[0] == "127.0.0.1" {
					cfg.NodeList[0] = cfg.Hostname
				}
				glog.Infof("Expecting %d nodes: %v", len(cfg.NodeList), cfg.NodeList)

				if startEtcd {
					etcdConfig := &etcd.Config{
						BindAddr:   cfg.EtcdAddr.URL.Host,
						MasterAddr: cfg.EtcdAddr.URL.Host,
						EtcdDir:    cfg.EtcdDir,
					}
					etcdConfig.Run()
				}

				// Connect and setup etcd interfaces
				etcdClient := getEtcdClient(cfg)
				etcdHelper, err := origin.NewEtcdHelper(cfg.StorageVersion, etcdClient)
				if err != nil {
					glog.Errorf("Error setting up server storage: %v", err)
				}
				ketcdHelper, err := kmaster.NewEtcdHelper(etcdClient.GetCluster(), klatest.Version)
				if err != nil {
					glog.Errorf("Error setting up Kubernetes server storage: %v", err)
				}

				assetAddr := net.JoinHostPort(cfg.MasterAddr.Host, "8091")

				osmaster := &origin.MasterConfig{
					BindAddr:   cfg.BindAddr.URL.Host,
					MasterAddr: cfg.MasterAddr.URL.String(),
					AssetAddr:  assetAddr,
					EtcdHelper: etcdHelper,
				}
				osmaster.EnsureKubernetesClient()
				osmaster.EnsureOpenShiftClient()
				osmaster.EnsureCORSAllowedOrigins(cfg.CORSAllowedOrigins)

				kmaster := &kubernetes.MasterConfig{
					NodeHosts:  cfg.NodeList,
					EtcdHelper: ketcdHelper,
					KubeClient: osmaster.KubeClient,
				}

				osmaster.RunAPI(kmaster)
				osmaster.RunAssetServer()
				osmaster.RunBuildController()
				osmaster.RunDeploymentController()

				kmaster.RunScheduler()
				kmaster.RunReplicationController()
			}

			if startNode {
				etcdClient := getEtcdClient(cfg)

				hostname := cfg.Hostname
				if len(hostname) == 0 {
					hostname = defaultHostname()
				}

				nodeConfig := &kubernetes.NodeConfig{
					BindHost:   cfg.BindAddr.Host,
					NodeHost:   hostname,
					MasterHost: cfg.MasterAddr.URL.String(),

					VolumeDir: cfg.VolumeDir,

					NetworkContainerImage: env("KUBERNETES_NETWORK_CONTAINER_IMAGE", kubelet.NetworkContainerImage),

					EtcdClient: etcdClient,
				}

				nodeConfig.EnsureVolumeDir()
				nodeConfig.EnsureDocker(cfg.Docker)

				nodeConfig.RunProxy()
				nodeConfig.RunKubelet()
			}

			select {}
		},
	}

	flag := cmd.Flags()

	flag.Var(&cfg.BindAddr, "listen", "The address to listen for connections on (host port or URL).")
	flag.Var(&cfg.MasterAddr, "master", "The address the master can be reached on (host port or URL).")
	flag.Var(&cfg.EtcdAddr, "etcd", "The address of the etcd server (host port or URL).")

	flag.StringVar(&cfg.VolumeDir, "volume-dir", "openshift.local.volumes", "The volume storage directory.")
	flag.StringVar(&cfg.EtcdDir, "etcd-dir", "openshift.local.etcd", "The etcd data directory.")

	flag.Var(&cfg.NodeList, "nodes", "The hostnames of each node. This currently must be specified up front. Comma delimited list")
	flag.Var(&cfg.CORSAllowedOrigins, "cors-allowed-origins", "List of allowed origins for CORS, comma separated.  An allowed origin can be a regular expression to support subdomain matching.  If this list is empty CORS will not be enabled.")

	cfg.Docker.InstallFlags(flag)

	return cmd
}

// getEtcdClient creates an etcd client based on the provided config and waits until
// it is reachable.  If the server cannot be
func getEtcdClient(cfg *config) *etcdclient.Client {
	etcdServers := []string{cfg.EtcdAddr.URL.String()}
	etcdClient := etcdclient.NewClient(etcdServers)

	for i := 0; ; i += 1 {
		_, err := etcdClient.Get("/", false, false)
		if err == nil || tools.IsEtcdNotFound(err) {
			break
		}
		if i > 100 {
			glog.Fatal("Could not reach etcd: %v", err)
		}
		time.Sleep(50 * time.Millisecond)
	}

	return etcdClient
}

// defaultHostname returns the default hostname for this system.
func defaultHostname() string {
	// Note: We use exec here instead of os.Hostname() because we
	// want the FQDN, and this is the easiest way to get it.
	fqdn, err := exec.Command("hostname", "-f").Output()
	if err != nil {
		glog.Fatalf("Couldn't determine hostname: %v", err)
	}
	return strings.TrimSpace(string(fqdn))
}

// defaultMasterAddress checks for an unset master address and then attempts to use the first
// public IPv4 non-loopback address registered on this host. It will also update the
// EtcdAddr after if it was not provided.
// TODO: make me IPv6 safe
func defaultMasterAddress(cfg *config) {
	if !cfg.MasterAddr.Provided {
		// If the user specifies a bind address, and the master is not provided, use
		// the bind port by default
		port := cfg.MasterAddr.Port
		if cfg.BindAddr.Provided {
			port = cfg.BindAddr.Port
		}

		// use the default ip address for the system
		addr, err := util.DefaultLocalIP4()
		if err != nil {
			glog.Fatalf("Unable to find the public address of this master: %v", err)
		}

		masterAddr := net.JoinHostPort(addr.String(), strconv.Itoa(port))
		if err := cfg.MasterAddr.Set(masterAddr); err != nil {
			glog.Fatalf("Unable to set public address of this master: %v", err)
		}

		// Prefer to use the MasterAddr for etcd because some clients still connect to it.
		if !cfg.EtcdAddr.Provided {
			etcdAddr := net.JoinHostPort(addr.String(), strconv.Itoa(cfg.EtcdAddr.DefaultPort))
			if err := cfg.EtcdAddr.Set(etcdAddr); err != nil {
				glog.Fatalf("Unable to set public address of etcd: %v", err)
			}
		}
	} else if !cfg.EtcdAddr.Provided {
		// Etcd should be reachable on the same address that the master is (for simplicity)
		etcdAddr := net.JoinHostPort(cfg.MasterAddr.Host, strconv.Itoa(cfg.EtcdAddr.DefaultPort))
		if err := cfg.EtcdAddr.Set(etcdAddr); err != nil {
			glog.Fatalf("Unable to set public address of etcd: %v", err)
		}
	}
}

// env returns an environment variable or a default value if not specified.
func env(key string, defaultValue string) string {
	val := os.Getenv(key)
	if len(val) == 0 {
		return defaultValue
	} else {
		return val
	}
}
<|endoftext|>


<commit_before>package tcp

import (
	"encoding/binary"
	"hash/fnv"
	"math/rand"
	"net"
	"time"

	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	"github.com/m-mizutani/lurker/pkg/domain/interfaces"
	"github.com/m-mizutani/lurker/pkg/domain/types"
	"github.com/m-mizutani/ttlcache"
)

type tcpHandler struct {
	networks     []*net.IPNet
	excludePorts map[int16]bool

	flows    *ttlcache.CacheTable[uint64, *tcpFlow]
	elapsed  time.Duration
	lastTick uint64
}

type tcpFlow struct {
	createdAt        time.Time
	srcHost, dstHost gopacket.Endpoint
	srcPort, dstPort uint16

	recvAck  bool
	baseSeq  uint32
	nextSeq  uint32
	recvData []byte
}

func New(optins ...Option) *tcpHandler {
	hdlr := &tcpHandler{
		excludePorts: make(map[int16]bool),

		flows: ttlcache.New[uint64, *tcpFlow](),
	}

	for _, opt := range optins {
		opt(hdlr)
	}

	return hdlr
}

type Option func(hdlr *tcpHandler)

func WithNetwork(allowed *net.IPNet) Option {
	return func(hdlr *tcpHandler) {
		hdlr.networks = append(hdlr.networks, allowed)
	}
}

func WithExcludePorts(ports []int) Option {
	return func(hdlr *tcpHandler) {
		for _, port := range ports {
			hdlr.excludePorts[int16(port)] = true
		}
	}
}

func (x *tcpHandler) Handle(ctx *types.Context, pkt gopacket.Packet, spouts *interfaces.Spout) error {
	l := extractPktLayers(pkt)
	if l == nil {
		return nil
	}

	nw := pkt.NetworkLayer()
	if nw == nil {
		return nil
	}
	src, dst := nw.NetworkFlow().Endpoints()

	tp := pkt.TransportLayer()
	hv := flowHash(nw, tp)

	if l.tcp.FIN || l.tcp.RST {
		return nil
	}

	if l.tcp.SYN && !l.tcp.ACK {
		if !x.isInnetworks(net.IP(dst.Raw())) {
			return nil
		}
		if x.excludePorts[int16(l.tcp.DstPort)] {
			return nil
		}

		flow := &tcpFlow{
			createdAt: time.Now(),
			srcHost:   src,
			dstHost:   dst,
			srcPort:   uint16(l.tcp.SrcPort),
			dstPort:   uint16(l.tcp.DstPort),

			baseSeq: l.tcp.Seq,
			nextSeq: l.tcp.Seq + 1,
		}
		x.flows.Set(hv, flow, 5)

		synAckPkt, err := createSynAckPacket(l)
		if err != nil {
			return err
		}

		spouts.WritePacket(synAckPkt)

	} else if flow := x.flows.Get(hv); flow != nil {
		if dst.String() == flow.dstHost.String() && l.tcp.DstPort == layers.TCPPort(flow.dstPort) {
			if flow.nextSeq == l.tcp.Seq {
				flow.recvAck = true
				flow.recvData = append(flow.recvData, l.tcp.Payload...)
				flow.nextSeq += uint32(len(l.tcp.Payload))
			}
		}
	}

	return nil
}

func (x *tcpHandler) isInnetworks(ip net.IP) bool {
	if x.networks == nil {
		return true
	}

	for _, nw := range x.networks {
		if nw.Contains(ip) {
			return true
		}
	}
	return false
}

func (x *tcpHandler) Tick(ctx *types.Context, spouts *interfaces.Spout) error {
	id := x.flows.SetHook(func(flow *tcpFlow) uint64 {
		outputConsole(spouts.Console, flow)
		outputSlack(ctx, spouts.Slack, flow)
		outputBigQuery(ctx, spouts.InsertTcpData, flow)
		return 0
	})
	defer x.flows.DelHook(id)

	x.flows.Elapse(1)

	return nil
}

type pktLayers struct {
	eth  *layers.Ethernet
	ipv4 *layers.IPv4
	tcp  *layers.TCP
}

func extractPktLayers(pkt gopacket.Packet) *pktLayers {
	l := &pktLayers{}
	var eth layers.Ethernet
	var ip4 layers.IPv4
	var tcp layers.TCP
	var payload gopacket.Payload

	parser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, &eth, &ip4, &tcp, &payload)

	var decoded []gopacket.LayerType
	if err := parser.DecodeLayers(pkt.Data(), &decoded); err != nil {
		return nil // ignore
	}

	for _, layerType := range decoded {
		switch layerType {
		case layers.LayerTypeEthernet:
			l.eth = &eth
		case layers.LayerTypeIPv4:
			l.ipv4 = &ip4
		case layers.LayerTypeTCP:
			l.tcp = &tcp
		}
	}

	if l.eth == nil || l.ipv4 == nil || l.tcp == nil {
		return nil
	}

	return l
}

func createSynAckPacket(l *pktLayers) ([]byte, error) {
	options := gopacket.SerializeOptions{FixLengths: true, ComputeChecksums: true}
	buffer := gopacket.NewSerializeBuffer()

	newEtherLayer := &layers.Ethernet{
		SrcMAC:       l.eth.DstMAC,
		DstMAC:       l.eth.SrcMAC,
		EthernetType: l.eth.EthernetType,
	}

	newIpv4Layer := &layers.IPv4{
		Version:  4,
		TTL:      64,
		Protocol: layers.IPProtocolTCP,
		SrcIP:    l.ipv4.DstIP,
		DstIP:    l.ipv4.SrcIP,
	}
	newTCPLayer := &layers.TCP{
		SrcPort: l.tcp.DstPort,
		DstPort: l.tcp.SrcPort,
		Ack:     l.tcp.Seq + 1,
		Seq:     rand.Uint32(),
		SYN:     true,
		ACK:     true,
		Window:  65535,
	}
	if err := newTCPLayer.SetNetworkLayerForChecksum(newIpv4Layer); err != nil {
		return nil, err
	}

	if err := gopacket.SerializeLayers(buffer, options,
		newEtherLayer,
		newIpv4Layer,
		newTCPLayer,
	); err != nil {
		return nil, err
	}

	return buffer.Bytes(), nil
}

func flowHash(nw gopacket.NetworkLayer, tp gopacket.TransportLayer) uint64 {
	nwHash := nw.NetworkFlow().FastHash()
	tpHash := tp.TransportFlow().FastHash()

	b1, b2 := make([]byte, 8), make([]byte, 8)
	binary.LittleEndian.PutUint64(b1, nwHash)
	binary.LittleEndian.PutUint64(b2, tpHash)

	hash := fnv.New64a()
	_, _ = hash.Write(b1)
	_, _ = hash.Write(b2)
	return hash.Sum64()
}
<commit_msg>Fix typo<commit_after>package tcp

import (
	"encoding/binary"
	"hash/fnv"
	"math/rand"
	"net"
	"time"

	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	"github.com/m-mizutani/lurker/pkg/domain/interfaces"
	"github.com/m-mizutani/lurker/pkg/domain/types"
	"github.com/m-mizutani/ttlcache"
)

type tcpHandler struct {
	networks     []*net.IPNet
	excludePorts map[int16]bool

	flows    *ttlcache.CacheTable[uint64, *tcpFlow]
	elapsed  time.Duration
	lastTick uint64
}

type tcpFlow struct {
	createdAt        time.Time
	srcHost, dstHost gopacket.Endpoint
	srcPort, dstPort uint16

	recvAck  bool
	baseSeq  uint32
	nextSeq  uint32
	recvData []byte
}

func New(optins ...Option) *tcpHandler {
	hdlr := &tcpHandler{
		excludePorts: make(map[int16]bool),

		flows: ttlcache.New[uint64, *tcpFlow](),
	}

	for _, opt := range optins {
		opt(hdlr)
	}

	return hdlr
}

type Option func(hdlr *tcpHandler)

func WithNetwork(allowed *net.IPNet) Option {
	return func(hdlr *tcpHandler) {
		hdlr.networks = append(hdlr.networks, allowed)
	}
}

func WithExcludePorts(ports []int) Option {
	return func(hdlr *tcpHandler) {
		for _, port := range ports {
			hdlr.excludePorts[int16(port)] = true
		}
	}
}

func (x *tcpHandler) Handle(ctx *types.Context, pkt gopacket.Packet, spouts *interfaces.Spout) error {
	l := extractPktLayers(pkt)
	if l == nil {
		return nil
	}

	nw := pkt.NetworkLayer()
	if nw == nil {
		return nil
	}
	src, dst := nw.NetworkFlow().Endpoints()

	tp := pkt.TransportLayer()
	hv := flowHash(nw, tp)

	if l.tcp.FIN || l.tcp.RST {
		return nil
	}

	if l.tcp.SYN && !l.tcp.ACK {
		if !x.isTarget(net.IP(dst.Raw())) {
			return nil
		}
		if x.excludePorts[int16(l.tcp.DstPort)] {
			return nil
		}

		flow := &tcpFlow{
			createdAt: time.Now(),
			srcHost:   src,
			dstHost:   dst,
			srcPort:   uint16(l.tcp.SrcPort),
			dstPort:   uint16(l.tcp.DstPort),

			baseSeq: l.tcp.Seq,
			nextSeq: l.tcp.Seq + 1,
		}
		x.flows.Set(hv, flow, 5)

		synAckPkt, err := createSynAckPacket(l)
		if err != nil {
			return err
		}

		spouts.WritePacket(synAckPkt)

	} else if flow := x.flows.Get(hv); flow != nil {
		if dst.String() == flow.dstHost.String() && l.tcp.DstPort == layers.TCPPort(flow.dstPort) {
			if flow.nextSeq == l.tcp.Seq {
				flow.recvAck = true
				flow.recvData = append(flow.recvData, l.tcp.Payload...)
				flow.nextSeq += uint32(len(l.tcp.Payload))
			}
		}
	}

	return nil
}

func (x *tcpHandler) isTarget(ip net.IP) bool {
	if x.networks == nil {
		return true
	}

	for _, nw := range x.networks {
		if nw.Contains(ip) {
			return true
		}
	}
	return false
}

func (x *tcpHandler) Tick(ctx *types.Context, spouts *interfaces.Spout) error {
	id := x.flows.SetHook(func(flow *tcpFlow) uint64 {
		outputConsole(spouts.Console, flow)
		outputSlack(ctx, spouts.Slack, flow)
		outputBigQuery(ctx, spouts.InsertTcpData, flow)
		return 0
	})
	defer x.flows.DelHook(id)

	x.flows.Elapse(1)

	return nil
}

type pktLayers struct {
	eth  *layers.Ethernet
	ipv4 *layers.IPv4
	tcp  *layers.TCP
}

func extractPktLayers(pkt gopacket.Packet) *pktLayers {
	l := &pktLayers{}
	var eth layers.Ethernet
	var ip4 layers.IPv4
	var tcp layers.TCP
	var payload gopacket.Payload

	parser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, &eth, &ip4, &tcp, &payload)

	var decoded []gopacket.LayerType
	if err := parser.DecodeLayers(pkt.Data(), &decoded); err != nil {
		return nil // ignore
	}

	for _, layerType := range decoded {
		switch layerType {
		case layers.LayerTypeEthernet:
			l.eth = &eth
		case layers.LayerTypeIPv4:
			l.ipv4 = &ip4
		case layers.LayerTypeTCP:
			l.tcp = &tcp
		}
	}

	if l.eth == nil || l.ipv4 == nil || l.tcp == nil {
		return nil
	}

	return l
}

func createSynAckPacket(l *pktLayers) ([]byte, error) {
	options := gopacket.SerializeOptions{FixLengths: true, ComputeChecksums: true}
	buffer := gopacket.NewSerializeBuffer()

	newEtherLayer := &layers.Ethernet{
		SrcMAC:       l.eth.DstMAC,
		DstMAC:       l.eth.SrcMAC,
		EthernetType: l.eth.EthernetType,
	}

	newIpv4Layer := &layers.IPv4{
		Version:  4,
		TTL:      64,
		Protocol: layers.IPProtocolTCP,
		SrcIP:    l.ipv4.DstIP,
		DstIP:    l.ipv4.SrcIP,
	}
	newTCPLayer := &layers.TCP{
		SrcPort: l.tcp.DstPort,
		DstPort: l.tcp.SrcPort,
		Ack:     l.tcp.Seq + 1,
		Seq:     rand.Uint32(),
		SYN:     true,
		ACK:     true,
		Window:  65535,
	}
	if err := newTCPLayer.SetNetworkLayerForChecksum(newIpv4Layer); err != nil {
		return nil, err
	}

	if err := gopacket.SerializeLayers(buffer, options,
		newEtherLayer,
		newIpv4Layer,
		newTCPLayer,
	); err != nil {
		return nil, err
	}

	return buffer.Bytes(), nil
}

func flowHash(nw gopacket.NetworkLayer, tp gopacket.TransportLayer) uint64 {
	nwHash := nw.NetworkFlow().FastHash()
	tpHash := tp.TransportFlow().FastHash()

	b1, b2 := make([]byte, 8), make([]byte, 8)
	binary.LittleEndian.PutUint64(b1, nwHash)
	binary.LittleEndian.PutUint64(b2, tpHash)

	hash := fnv.New64a()
	_, _ = hash.Write(b1)
	_, _ = hash.Write(b2)
	return hash.Sum64()
}
<|endoftext|>


<commit_before>// Copyright © 2018 The Knative Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package commands

import (
	"flag"
	"fmt"
	"io"
	"os"
	"path/filepath"

	serving "github.com/knative/serving/pkg/client/clientset/versioned/typed/serving/v1alpha1"
	homedir "github.com/mitchellh/go-homedir"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
	_ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
	"k8s.io/client-go/tools/clientcmd"
)

var cfgFile string
var kubeCfgFile string

// Parameters for creating commands. Useful for inserting mocks for testing.
type KnParams struct {
	Output         io.Writer
	ServingFactory func() (serving.ServingV1alpha1Interface, error)
}

func (c *KnParams) Initialize() {
	if c.ServingFactory == nil {
		c.ServingFactory = GetConfig
	}
}

// rootCmd represents the base command when called without any subcommands
func NewKnCommand(params ...KnParams) *cobra.Command {
	var p *KnParams
	if len(params) == 0 {
		p = &KnParams{}
	} else if len(params) == 1 {
		p = &params[0]
	} else {
		panic("Too many params objects to NewKnCommand")
	}
	p.Initialize()

	rootCmd := &cobra.Command{
		Use:   "kn",
		Short: "Knative client",
		Long: `Manage your Knative building blocks:

Serving: Manage your services and release new software to them.
Build: Create builds and keep track of their results.
Eventing: Manage event subscriptions and channels. Connect up event sources.`,

		// Affects children as well
		SilenceUsage: true,
	}
	if p.Output != nil {
		rootCmd.SetOutput(p.Output)
	}
	rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.kn.yaml)")
	rootCmd.PersistentFlags().StringVar(&kubeCfgFile, "kubeconfig", "", "kubectl config file (default is $HOME/.kube/config)")
	rootCmd.AddCommand(NewServiceCommand(p))
	rootCmd.AddCommand(NewRevisionCommand(p))
	rootCmd.AddCommand(NewCompletionCommand(p))
	rootCmd.AddCommand(NewVersionCommand(p))

	// For glog parse error.
	flag.CommandLine.Parse([]string{})
	return rootCmd
}

func InitializeConfig() {
	cobra.OnInitialize(initConfig)
	cobra.OnInitialize(initKubeConfig)

}

func initKubeConfig() {
	if kubeCfgFile != "" {
		return
	}
	if kubeEnvConf, ok := os.LookupEnv("KUBECONFIG"); ok {
		kubeCfgFile = kubeEnvConf
	} else {
		home, err := homedir.Dir()
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
		kubeCfgFile = filepath.Join(home, ".kube", "config")
	}
}

// initConfig reads in config file and ENV variables if set.
func initConfig() {
	if cfgFile != "" {
		// Use config file from the flag.
		viper.SetConfigFile(cfgFile)
	} else {
		// Find home directory.
		home, err := homedir.Dir()
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}

		// Search config in home directory with name ".kn" (without extension).
		viper.AddConfigPath(home)
		viper.SetConfigName(".kn")
	}

	viper.AutomaticEnv() // read in environment variables that match

	// If a config file is found, read it in.
	if err := viper.ReadInConfig(); err == nil {
		fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
	}
}

func GetConfig() (serving.ServingV1alpha1Interface, error) {
	config, err := clientcmd.BuildConfigFromFlags("", kubeCfgFile)
	if err != nil {
		return nil, err
	}
	client, err := serving.NewForConfig(config)
	if err != nil {
		return nil, err
	}
	return client, nil
}
<commit_msg>Remove fmt.Printf of error since creates double error print to user (#96)<commit_after>// Copyright © 2018 The Knative Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package commands

import (
	"flag"
	"fmt"
	"io"
	"os"
	"path/filepath"

	serving "github.com/knative/serving/pkg/client/clientset/versioned/typed/serving/v1alpha1"
	homedir "github.com/mitchellh/go-homedir"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
	_ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
	"k8s.io/client-go/tools/clientcmd"
)

var cfgFile string
var kubeCfgFile string

// Parameters for creating commands. Useful for inserting mocks for testing.
type KnParams struct {
	Output         io.Writer
	ServingFactory func() (serving.ServingV1alpha1Interface, error)
}

func (c *KnParams) Initialize() {
	if c.ServingFactory == nil {
		c.ServingFactory = GetConfig
	}
}

// rootCmd represents the base command when called without any subcommands
func NewKnCommand(params ...KnParams) *cobra.Command {
	var p *KnParams
	if len(params) == 0 {
		p = &KnParams{}
	} else if len(params) == 1 {
		p = &params[0]
	} else {
		panic("Too many params objects to NewKnCommand")
	}
	p.Initialize()

	rootCmd := &cobra.Command{
		Use:   "kn",
		Short: "Knative client",
		Long: `Manage your Knative building blocks:

Serving: Manage your services and release new software to them.
Build: Create builds and keep track of their results.
Eventing: Manage event subscriptions and channels. Connect up event sources.`,

		// Affects children as well
		SilenceUsage: true,

		// Prevents Cobra from dealing with errors as we deal with them in main.go
		SilenceErrors: true,
	}
	if p.Output != nil {
		rootCmd.SetOutput(p.Output)
	}
	rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.kn.yaml)")
	rootCmd.PersistentFlags().StringVar(&kubeCfgFile, "kubeconfig", "", "kubectl config file (default is $HOME/.kube/config)")
	rootCmd.AddCommand(NewServiceCommand(p))
	rootCmd.AddCommand(NewRevisionCommand(p))
	rootCmd.AddCommand(NewCompletionCommand(p))
	rootCmd.AddCommand(NewVersionCommand(p))

	// For glog parse error.
	flag.CommandLine.Parse([]string{})
	return rootCmd
}

func InitializeConfig() {
	cobra.OnInitialize(initConfig)
	cobra.OnInitialize(initKubeConfig)

}

func initKubeConfig() {
	if kubeCfgFile != "" {
		return
	}
	if kubeEnvConf, ok := os.LookupEnv("KUBECONFIG"); ok {
		kubeCfgFile = kubeEnvConf
	} else {
		home, err := homedir.Dir()
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
		kubeCfgFile = filepath.Join(home, ".kube", "config")
	}
}

// initConfig reads in config file and ENV variables if set.
func initConfig() {
	if cfgFile != "" {
		// Use config file from the flag.
		viper.SetConfigFile(cfgFile)
	} else {
		// Find home directory.
		home, err := homedir.Dir()
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}

		// Search config in home directory with name ".kn" (without extension).
		viper.AddConfigPath(home)
		viper.SetConfigName(".kn")
	}

	viper.AutomaticEnv() // read in environment variables that match

	// If a config file is found, read it in.
	if err := viper.ReadInConfig(); err == nil {
		fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
	}
}

func GetConfig() (serving.ServingV1alpha1Interface, error) {
	config, err := clientcmd.BuildConfigFromFlags("", kubeCfgFile)
	if err != nil {
		return nil, err
	}
	client, err := serving.NewForConfig(config)
	if err != nil {
		return nil, err
	}
	return client, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2014 Google Inc. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package kubelet

import (
	"errors"
	"fmt"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/types"
	cadvisor "github.com/google/cadvisor/info"
)

var (
	// ErrNoKubeletContainers returned when there are not containers managed by the kubelet (ie: either no containers on the node, or none that the kubelet cares about).
	ErrNoKubeletContainers = errors.New("no containers managed by kubelet")

	// ErrContainerNotFound returned when a container in the given pod with the given container name was not found, amongst those managed by the kubelet.
	ErrContainerNotFound = errors.New("no matching container")

	// ErrCadvisorApiFailure returned when cadvisor couldn't retrieve stats for the given container, either because it isn't running or it was confused by the request
	ErrCadvisorApiFailure = errors.New("failed to retrieve cadvisor stats")
)

// cadvisorInterface is an abstract interface for testability.  It abstracts the interface of "github.com/google/cadvisor/client".Client.
type cadvisorInterface interface {
	DockerContainer(name string, req *cadvisor.ContainerInfoRequest) (cadvisor.ContainerInfo, error)
	ContainerInfo(name string, req *cadvisor.ContainerInfoRequest) (*cadvisor.ContainerInfo, error)
	MachineInfo() (*cadvisor.MachineInfo, error)
}

// statsFromContainerPath takes a container's absolute path and returns the stats for the
// container. The container's absolute path refers to its hierarchy in the
// cgroup file system. e.g. The root container, which represents the whole
// machine, has path "/"; all docker containers have path "/docker/<docker id>"
func (kl *Kubelet) statsFromContainerPath(cc cadvisorInterface, containerPath string, req *cadvisor.ContainerInfoRequest) (*cadvisor.ContainerInfo, error) {
	cinfo, err := cc.ContainerInfo(containerPath, req)
	if err != nil {
		return nil, err
	}
	return cinfo, nil
}

// statsFromDockerContainer takes a Docker container's ID and returns the stats for the
// container.
func (kl *Kubelet) statsFromDockerContainer(cc cadvisorInterface, containerId string, req *cadvisor.ContainerInfoRequest) (*cadvisor.ContainerInfo, error) {
	cinfo, err := cc.DockerContainer(containerId, req)
	if err != nil {
		return nil, err
	}
	return &cinfo, nil
}

// GetContainerInfo returns stats (from Cadvisor) for a container.
func (kl *Kubelet) GetContainerInfo(podFullName string, uid types.UID, containerName string, req *cadvisor.ContainerInfoRequest) (*cadvisor.ContainerInfo, error) {
	cc := kl.GetCadvisorClient()
	if cc == nil {
		return nil, fmt.Errorf("no cadvisor connection")
	}
	dockerContainers, err := dockertools.GetKubeletDockerContainers(kl.dockerClient, false)
	if err != nil {
		return nil, err
	}
	if len(dockerContainers) == 0 {
		return nil, ErrNoKubeletContainers
	}
	dockerContainer, found, _ := dockerContainers.FindPodContainer(podFullName, uid, containerName)
	if !found {
		return nil, ErrContainerNotFound
	}

	ci, err := kl.statsFromDockerContainer(cc, dockerContainer.ID, req)
	if err != nil {
		return nil, ErrCadvisorApiFailure
	}
	return ci, nil
}

// GetRootInfo returns stats (from Cadvisor) of current machine (root container).
func (kl *Kubelet) GetRootInfo(req *cadvisor.ContainerInfoRequest) (*cadvisor.ContainerInfo, error) {
	cc := kl.GetCadvisorClient()
	if cc == nil {
		return nil, fmt.Errorf("no cadvisor connection")
	}
	return kl.statsFromContainerPath(cc, "/", req)
}

func (kl *Kubelet) GetMachineInfo() (*cadvisor.MachineInfo, error) {
	cc := kl.GetCadvisorClient()
	if cc == nil {
		return nil, fmt.Errorf("no cadvisor connection")
	}
	return cc.MachineInfo()
}
<commit_msg>pkg/kubelet: drop the unnecessary method receiver<commit_after>/*
Copyright 2014 Google Inc. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package kubelet

import (
	"errors"
	"fmt"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/types"
	cadvisor "github.com/google/cadvisor/info"
)

var (
	// ErrNoKubeletContainers returned when there are not containers managed by the kubelet (ie: either no containers on the node, or none that the kubelet cares about).
	ErrNoKubeletContainers = errors.New("no containers managed by kubelet")

	// ErrContainerNotFound returned when a container in the given pod with the given container name was not found, amongst those managed by the kubelet.
	ErrContainerNotFound = errors.New("no matching container")

	// ErrCadvisorApiFailure returned when cadvisor couldn't retrieve stats for the given container, either because it isn't running or it was confused by the request
	ErrCadvisorApiFailure = errors.New("failed to retrieve cadvisor stats")
)

// cadvisorInterface is an abstract interface for testability.  It abstracts the interface of "github.com/google/cadvisor/client".Client.
type cadvisorInterface interface {
	DockerContainer(name string, req *cadvisor.ContainerInfoRequest) (cadvisor.ContainerInfo, error)
	ContainerInfo(name string, req *cadvisor.ContainerInfoRequest) (*cadvisor.ContainerInfo, error)
	MachineInfo() (*cadvisor.MachineInfo, error)
}

// statsFromContainerPath takes a container's absolute path and returns the stats for the
// container. The container's absolute path refers to its hierarchy in the
// cgroup file system. e.g. The root container, which represents the whole
// machine, has path "/"; all docker containers have path "/docker/<docker id>"
func statsFromContainerPath(cc cadvisorInterface, containerPath string, req *cadvisor.ContainerInfoRequest) (*cadvisor.ContainerInfo, error) {
	cinfo, err := cc.ContainerInfo(containerPath, req)
	if err != nil {
		return nil, err
	}
	return cinfo, nil
}

// statsFromDockerContainer takes a Docker container's ID and returns the stats for the
// container.
func statsFromDockerContainer(cc cadvisorInterface, containerId string, req *cadvisor.ContainerInfoRequest) (*cadvisor.ContainerInfo, error) {
	cinfo, err := cc.DockerContainer(containerId, req)
	if err != nil {
		return nil, err
	}
	return &cinfo, nil
}

// GetContainerInfo returns stats (from Cadvisor) for a container.
func (kl *Kubelet) GetContainerInfo(podFullName string, uid types.UID, containerName string, req *cadvisor.ContainerInfoRequest) (*cadvisor.ContainerInfo, error) {
	cc := kl.GetCadvisorClient()
	if cc == nil {
		return nil, fmt.Errorf("no cadvisor connection")
	}
	dockerContainers, err := dockertools.GetKubeletDockerContainers(kl.dockerClient, false)
	if err != nil {
		return nil, err
	}
	if len(dockerContainers) == 0 {
		return nil, ErrNoKubeletContainers
	}
	dockerContainer, found, _ := dockerContainers.FindPodContainer(podFullName, uid, containerName)
	if !found {
		return nil, ErrContainerNotFound
	}

	ci, err := statsFromDockerContainer(cc, dockerContainer.ID, req)
	if err != nil {
		return nil, ErrCadvisorApiFailure
	}
	return ci, nil
}

// GetRootInfo returns stats (from Cadvisor) of current machine (root container).
func (kl *Kubelet) GetRootInfo(req *cadvisor.ContainerInfoRequest) (*cadvisor.ContainerInfo, error) {
	cc := kl.GetCadvisorClient()
	if cc == nil {
		return nil, fmt.Errorf("no cadvisor connection")
	}
	return statsFromContainerPath(cc, "/", req)
}

func (kl *Kubelet) GetMachineInfo() (*cadvisor.MachineInfo, error) {
	cc := kl.GetCadvisorClient()
	if cc == nil {
		return nil, fmt.Errorf("no cadvisor connection")
	}
	return cc.MachineInfo()
}
<|endoftext|>