Enable lint check for comments (#2023)

Commit 1: Enable lint check for comments

Part of #217. Follow up from #1982 and #2018.

A subsequent commit will fix the ci failure.

Commit 2: Address all comment-related linter errors.

This change addresses all comment-related linter errors by doing the
following:
- Add comments to exported symbols
- Make some exported symbols private
- Recommend via TODOs that some exported symbols should should move or
  be removed

This PR does not:
- Modify, move, or remove any code
- Modify existing comments

Signed-off-by: Andrew Seigner <siggy@buoyant.io>
This commit is contained in:
Andrew Seigner 2019-01-02 14:03:59 -08:00 committed by GitHub
parent ef02cd6828
commit 1c302182ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 214 additions and 35 deletions

View File

@ -12,8 +12,8 @@ cd "$rootdir"
# install golint from vendor # install golint from vendor
go install ./vendor/golang.org/x/lint/golint go install ./vendor/golang.org/x/lint/golint
# use `go list` to exclude packages in vendor, ignore uncommented warnings # use `go list` to exclude packages in vendor
out=$(go list ./... | xargs golint | grep -v 'should have comment') || true out=$(go list ./... | xargs golint) || true
if [ -n "$out" ]; then if [ -n "$out" ]; then
echo "$out" echo "$out"

View File

@ -5,6 +5,8 @@ import (
"google.golang.org/grpc" "google.golang.org/grpc"
) )
// NewClient creates a new gRPC client to the Destination service.
// TODO: consider moving this into destination-client, or removing altogether.
func NewClient(addr string) (pb.DestinationClient, *grpc.ClientConn, error) { func NewClient(addr string) (pb.DestinationClient, *grpc.ClientConn, error) {
conn, err := grpc.Dial(addr, grpc.WithInsecure()) conn, err := grpc.Dial(addr, grpc.WithInsecure())
if err != nil { if err != nil {

View File

@ -190,6 +190,8 @@ func newClient(apiURL *url.URL, httpClientToUse *http.Client, controlPlaneNamesp
}, nil }, nil
} }
// NewInternalClient creates a new Public API client intended to run inside a
// Kubernetes cluster.
func NewInternalClient(controlPlaneNamespace string, kubeAPIHost string) (pb.ApiClient, error) { func NewInternalClient(controlPlaneNamespace string, kubeAPIHost string) (pb.ApiClient, error) {
apiURL, err := url.Parse(fmt.Sprintf("http://%s/", kubeAPIHost)) apiURL, err := url.Parse(fmt.Sprintf("http://%s/", kubeAPIHost))
if err != nil { if err != nil {
@ -199,6 +201,8 @@ func NewInternalClient(controlPlaneNamespace string, kubeAPIHost string) (pb.Api
return newClient(apiURL, http.DefaultClient, controlPlaneNamespace) return newClient(apiURL, http.DefaultClient, controlPlaneNamespace)
} }
// NewExternalClient creates a new Public API client intended to run from
// outside a Kubernetes cluster.
func NewExternalClient(controlPlaneNamespace string, kubeAPI *k8s.KubernetesAPI) (pb.ApiClient, error) { func NewExternalClient(controlPlaneNamespace string, kubeAPI *k8s.KubernetesAPI) (pb.ApiClient, error) {
apiURL, err := kubeAPI.URLFor(controlPlaneNamespace, "/services/linkerd-controller-api:http/proxy/") apiURL, err := kubeAPI.URLFor(controlPlaneNamespace, "/services/linkerd-controller-api:http/proxy/")
if err != nil { if err != nil {

View File

@ -39,10 +39,10 @@ type podReport struct {
const ( const (
podQuery = "max(process_start_time_seconds{%s}) by (pod, namespace)" podQuery = "max(process_start_time_seconds{%s}) by (pod, namespace)"
K8sClientSubsystemName = "kubernetes" k8sClientSubsystemName = "kubernetes"
K8sClientCheckDescription = "control plane can talk to Kubernetes" k8sClientCheckDescription = "control plane can talk to Kubernetes"
PromClientSubsystemName = "prometheus" promClientSubsystemName = "prometheus"
PromClientCheckDescription = "control plane can talk to Prometheus" promClientCheckDescription = "control plane can talk to Prometheus"
) )
func newGrpcServer( func newGrpcServer(
@ -190,8 +190,8 @@ func (s *grpcServer) ListPods(ctx context.Context, req *pb.ListPodsRequest) (*pb
func (s *grpcServer) SelfCheck(ctx context.Context, in *healthcheckPb.SelfCheckRequest) (*healthcheckPb.SelfCheckResponse, error) { func (s *grpcServer) SelfCheck(ctx context.Context, in *healthcheckPb.SelfCheckRequest) (*healthcheckPb.SelfCheckResponse, error) {
k8sClientCheck := &healthcheckPb.CheckResult{ k8sClientCheck := &healthcheckPb.CheckResult{
SubsystemName: K8sClientSubsystemName, SubsystemName: k8sClientSubsystemName,
CheckDescription: K8sClientCheckDescription, CheckDescription: k8sClientCheckDescription,
Status: healthcheckPb.CheckStatus_OK, Status: healthcheckPb.CheckStatus_OK,
} }
_, err := s.k8sAPI.Pod().Lister().List(labels.Everything()) _, err := s.k8sAPI.Pod().Lister().List(labels.Everything())
@ -201,8 +201,8 @@ func (s *grpcServer) SelfCheck(ctx context.Context, in *healthcheckPb.SelfCheckR
} }
promClientCheck := &healthcheckPb.CheckResult{ promClientCheck := &healthcheckPb.CheckResult{
SubsystemName: PromClientSubsystemName, SubsystemName: promClientSubsystemName,
CheckDescription: PromClientCheckDescription, CheckDescription: promClientCheckDescription,
Status: healthcheckPb.CheckStatus_OK, Status: healthcheckPb.CheckStatus_OK,
} }
_, err = s.queryProm(ctx, fmt.Sprintf(podQuery, "")) _, err = s.queryProm(ctx, fmt.Sprintf(podQuery, ""))

View File

@ -167,7 +167,7 @@ spec:
} }
fakeGrpcServer := newGrpcServer( fakeGrpcServer := newGrpcServer(
&MockProm{Res: exp.promRes}, &mockProm{Res: exp.promRes},
tap.NewTapClient(nil), tap.NewTapClient(nil),
k8sAPI, k8sAPI,
"linkerd", "linkerd",
@ -248,7 +248,7 @@ metadata:
} }
fakeGrpcServer := newGrpcServer( fakeGrpcServer := newGrpcServer(
&MockProm{}, &mockProm{},
tap.NewTapClient(nil), tap.NewTapClient(nil),
k8sAPI, k8sAPI,
"linkerd", "linkerd",

View File

@ -239,6 +239,7 @@ func fullURLPathFor(method string) string {
return apiRoot + apiPrefix + method return apiRoot + apiPrefix + method
} }
// NewServer creates a Public API HTTP server.
func NewServer( func NewServer(
addr string, addr string,
prometheusClient promApi.Client, prometheusClient promApi.Client,

View File

@ -771,7 +771,7 @@ status:
for _, exp := range expectations { for _, exp := range expectations {
fakeGrpcServer := newGrpcServer( fakeGrpcServer := newGrpcServer(
&MockProm{Res: exp.mockPromResponse}, &mockProm{Res: exp.mockPromResponse},
tap.NewTapClient(nil), tap.NewTapClient(nil),
k8sAPI, k8sAPI,
"linkerd", "linkerd",
@ -795,7 +795,7 @@ status:
t.Fatalf("NewFakeAPI returned an error: %s", err) t.Fatalf("NewFakeAPI returned an error: %s", err)
} }
fakeGrpcServer := newGrpcServer( fakeGrpcServer := newGrpcServer(
&MockProm{Res: model.Vector{}}, &mockProm{Res: model.Vector{}},
tap.NewTapClient(nil), tap.NewTapClient(nil),
k8sAPI, k8sAPI,
"linkerd", "linkerd",

View File

@ -18,6 +18,7 @@ import (
"google.golang.org/grpc" "google.golang.org/grpc"
) )
// MockAPIClient satisfies the Public API's gRPC interface.
type MockAPIClient struct { type MockAPIClient struct {
ErrorToReturn error ErrorToReturn error
VersionInfoToReturn *pb.VersionInfo VersionInfoToReturn *pb.VersionInfo
@ -30,45 +31,53 @@ type MockAPIClient struct {
APITapByResourceClientToReturn pb.Api_TapByResourceClient APITapByResourceClientToReturn pb.Api_TapByResourceClient
} }
// StatSummary provides a mock of a Public API method.
func (c *MockAPIClient) StatSummary(ctx context.Context, in *pb.StatSummaryRequest, opts ...grpc.CallOption) (*pb.StatSummaryResponse, error) { func (c *MockAPIClient) StatSummary(ctx context.Context, in *pb.StatSummaryRequest, opts ...grpc.CallOption) (*pb.StatSummaryResponse, error) {
return c.StatSummaryResponseToReturn, c.ErrorToReturn return c.StatSummaryResponseToReturn, c.ErrorToReturn
} }
// TopRoutes provides a mock of a Public API method.
func (c *MockAPIClient) TopRoutes(ctx context.Context, in *pb.TopRoutesRequest, opts ...grpc.CallOption) (*pb.TopRoutesResponse, error) { func (c *MockAPIClient) TopRoutes(ctx context.Context, in *pb.TopRoutesRequest, opts ...grpc.CallOption) (*pb.TopRoutesResponse, error) {
return c.TopRoutesResponseToReturn, c.ErrorToReturn return c.TopRoutesResponseToReturn, c.ErrorToReturn
} }
// Version provides a mock of a Public API method.
func (c *MockAPIClient) Version(ctx context.Context, in *pb.Empty, opts ...grpc.CallOption) (*pb.VersionInfo, error) { func (c *MockAPIClient) Version(ctx context.Context, in *pb.Empty, opts ...grpc.CallOption) (*pb.VersionInfo, error) {
return c.VersionInfoToReturn, c.ErrorToReturn return c.VersionInfoToReturn, c.ErrorToReturn
} }
// ListPods provides a mock of a Public API method.
func (c *MockAPIClient) ListPods(ctx context.Context, in *pb.ListPodsRequest, opts ...grpc.CallOption) (*pb.ListPodsResponse, error) { func (c *MockAPIClient) ListPods(ctx context.Context, in *pb.ListPodsRequest, opts ...grpc.CallOption) (*pb.ListPodsResponse, error) {
return c.ListPodsResponseToReturn, c.ErrorToReturn return c.ListPodsResponseToReturn, c.ErrorToReturn
} }
// ListServices provides a mock of a Public API method.
func (c *MockAPIClient) ListServices(ctx context.Context, in *pb.ListServicesRequest, opts ...grpc.CallOption) (*pb.ListServicesResponse, error) { func (c *MockAPIClient) ListServices(ctx context.Context, in *pb.ListServicesRequest, opts ...grpc.CallOption) (*pb.ListServicesResponse, error) {
return c.ListServicesResponseToReturn, c.ErrorToReturn return c.ListServicesResponseToReturn, c.ErrorToReturn
} }
// Tap provides a mock of a Public API method.
func (c *MockAPIClient) Tap(ctx context.Context, in *pb.TapRequest, opts ...grpc.CallOption) (pb.Api_TapClient, error) { func (c *MockAPIClient) Tap(ctx context.Context, in *pb.TapRequest, opts ...grpc.CallOption) (pb.Api_TapClient, error) {
return c.APITapClientToReturn, c.ErrorToReturn return c.APITapClientToReturn, c.ErrorToReturn
} }
// TapByResource provides a mock of a Public API method.
func (c *MockAPIClient) TapByResource(ctx context.Context, in *pb.TapByResourceRequest, opts ...grpc.CallOption) (pb.Api_TapByResourceClient, error) { func (c *MockAPIClient) TapByResource(ctx context.Context, in *pb.TapByResourceRequest, opts ...grpc.CallOption) (pb.Api_TapByResourceClient, error) {
return c.APITapByResourceClientToReturn, c.ErrorToReturn return c.APITapByResourceClientToReturn, c.ErrorToReturn
} }
// SelfCheck provides a mock of a Public API method.
func (c *MockAPIClient) SelfCheck(ctx context.Context, in *healthcheckPb.SelfCheckRequest, _ ...grpc.CallOption) (*healthcheckPb.SelfCheckResponse, error) { func (c *MockAPIClient) SelfCheck(ctx context.Context, in *healthcheckPb.SelfCheckRequest, _ ...grpc.CallOption) (*healthcheckPb.SelfCheckResponse, error) {
return c.SelfCheckResponseToReturn, c.ErrorToReturn return c.SelfCheckResponseToReturn, c.ErrorToReturn
} }
type MockAPITapClient struct { type mockAPITapClient struct {
TapEventsToReturn []pb.TapEvent TapEventsToReturn []pb.TapEvent
ErrorsToReturn []error ErrorsToReturn []error
grpc.ClientStream grpc.ClientStream
} }
func (a *MockAPITapClient) Recv() (*pb.TapEvent, error) { func (a *mockAPITapClient) Recv() (*pb.TapEvent, error) {
var eventPopped pb.TapEvent var eventPopped pb.TapEvent
var errorPopped error var errorPopped error
if len(a.TapEventsToReturn) == 0 && len(a.ErrorsToReturn) == 0 { if len(a.TapEventsToReturn) == 0 && len(a.ErrorsToReturn) == 0 {
@ -84,12 +93,14 @@ func (a *MockAPITapClient) Recv() (*pb.TapEvent, error) {
return &eventPopped, errorPopped return &eventPopped, errorPopped
} }
// MockAPITapByResourceClient satisfies the TapByResourceClient gRPC interface.
type MockAPITapByResourceClient struct { type MockAPITapByResourceClient struct {
TapEventsToReturn []pb.TapEvent TapEventsToReturn []pb.TapEvent
ErrorsToReturn []error ErrorsToReturn []error
grpc.ClientStream grpc.ClientStream
} }
// Recv satisfies the TapByResourceClient.Recv() gRPC method.
func (a *MockAPITapByResourceClient) Recv() (*pb.TapEvent, error) { func (a *MockAPITapByResourceClient) Recv() (*pb.TapEvent, error) {
var eventPopped pb.TapEvent var eventPopped pb.TapEvent
var errorPopped error var errorPopped error
@ -110,37 +121,41 @@ func (a *MockAPITapByResourceClient) Recv() (*pb.TapEvent, error) {
// Prometheus client // Prometheus client
// //
type MockProm struct { type mockProm struct {
Res model.Value Res model.Value
QueriesExecuted []string // expose the queries our Mock Prometheus receives, to test query generation QueriesExecuted []string // expose the queries our Mock Prometheus receives, to test query generation
rwLock sync.Mutex rwLock sync.Mutex
} }
// PodCounts is a test helper struct that is used for representing data in a
// StatTable.PodGroup.Row.
type PodCounts struct { type PodCounts struct {
MeshedPods uint64 MeshedPods uint64
RunningPods uint64 RunningPods uint64
FailedPods uint64 FailedPods uint64
} }
func (m *MockProm) Query(ctx context.Context, query string, ts time.Time) (model.Value, error) { func (m *mockProm) Query(ctx context.Context, query string, ts time.Time) (model.Value, error) {
m.rwLock.Lock() m.rwLock.Lock()
defer m.rwLock.Unlock() defer m.rwLock.Unlock()
m.QueriesExecuted = append(m.QueriesExecuted, query) m.QueriesExecuted = append(m.QueriesExecuted, query)
return m.Res, nil return m.Res, nil
} }
func (m *MockProm) QueryRange(ctx context.Context, query string, r v1.Range) (model.Value, error) { func (m *mockProm) QueryRange(ctx context.Context, query string, r v1.Range) (model.Value, error) {
m.rwLock.Lock() m.rwLock.Lock()
defer m.rwLock.Unlock() defer m.rwLock.Unlock()
m.QueriesExecuted = append(m.QueriesExecuted, query) m.QueriesExecuted = append(m.QueriesExecuted, query)
return m.Res, nil return m.Res, nil
} }
func (m *MockProm) LabelValues(ctx context.Context, label string) (model.LabelValues, error) { func (m *mockProm) LabelValues(ctx context.Context, label string) (model.LabelValues, error) {
return nil, nil return nil, nil
} }
func (m *MockProm) Series(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) ([]model.LabelSet, error) { func (m *mockProm) Series(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) ([]model.LabelSet, error) {
return nil, nil return nil, nil
} }
// GenStatSummaryResponse generates a mock Public API StatSummaryResponse
// object.
func GenStatSummaryResponse(resName, resType string, resNs []string, counts *PodCounts, basicStats bool) pb.StatSummaryResponse { func GenStatSummaryResponse(resName, resType string, resNs []string, counts *PodCounts, basicStats bool) pb.StatSummaryResponse {
rows := []*pb.StatTable_PodGroup_Row{} rows := []*pb.StatTable_PodGroup_Row{}
for _, ns := range resNs { for _, ns := range resNs {
@ -192,6 +207,7 @@ func GenStatSummaryResponse(resName, resType string, resNs []string, counts *Pod
return resp return resp
} }
// GenTopRoutesResponse generates a mock Public API TopRoutesResponse object.
func GenTopRoutesResponse(routes []string, counts []uint64) pb.TopRoutesResponse { func GenTopRoutesResponse(routes []string, counts []uint64) pb.TopRoutesResponse {
rows := []*pb.RouteTable_Row{} rows := []*pb.RouteTable_Row{}
for i, route := range routes { for i, route := range routes {
@ -228,13 +244,13 @@ type expectedStatRPC struct {
expectedPrometheusQueries []string // queries we expect public-api to issue to prometheus expectedPrometheusQueries []string // queries we expect public-api to issue to prometheus
} }
func newMockGrpcServer(exp expectedStatRPC) (*MockProm, *grpcServer, error) { func newMockGrpcServer(exp expectedStatRPC) (*mockProm, *grpcServer, error) {
k8sAPI, err := k8s.NewFakeAPI("", exp.k8sConfigs...) k8sAPI, err := k8s.NewFakeAPI("", exp.k8sConfigs...)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
mockProm := &MockProm{Res: exp.mockPromResponse} mockProm := &mockProm{Res: exp.mockPromResponse}
fakeGrpcServer := newGrpcServer( fakeGrpcServer := newGrpcServer(
mockProm, mockProm,
tap.NewTapClient(nil), tap.NewTapClient(nil),
@ -248,7 +264,7 @@ func newMockGrpcServer(exp expectedStatRPC) (*MockProm, *grpcServer, error) {
return mockProm, fakeGrpcServer, nil return mockProm, fakeGrpcServer, nil
} }
func (exp expectedStatRPC) verifyPromQueries(mockProm *MockProm) error { func (exp expectedStatRPC) verifyPromQueries(mockProm *mockProm) error {
// if exp.expectedPrometheusQueries is an empty slice we still wanna check no queries were executed. // if exp.expectedPrometheusQueries is an empty slice we still wanna check no queries were executed.
if exp.expectedPrometheusQueries != nil { if exp.expectedPrometheusQueries != nil {
sort.Strings(exp.expectedPrometheusQueries) sort.Strings(exp.expectedPrometheusQueries)

View File

@ -49,7 +49,7 @@ var (
) )
// StatsBaseRequestParams contains parameters that are used to build requests // StatsBaseRequestParams contains parameters that are used to build requests
// for metrics data. This includes requests to StatSummary and TopRoutes // for metrics data. This includes requests to StatSummary and TopRoutes.
type StatsBaseRequestParams struct { type StatsBaseRequestParams struct {
TimeWindow string TimeWindow string
Namespace string Namespace string
@ -58,6 +58,8 @@ type StatsBaseRequestParams struct {
AllNamespaces bool AllNamespaces bool
} }
// StatsSummaryRequestParams contains parameters that are used to build
// StatSummary requests.
type StatsSummaryRequestParams struct { type StatsSummaryRequestParams struct {
StatsBaseRequestParams StatsBaseRequestParams
ToNamespace string ToNamespace string
@ -69,12 +71,16 @@ type StatsSummaryRequestParams struct {
SkipStats bool SkipStats bool
} }
// TopRoutesRequestParams contains parameters that are used to build TopRoutes
// requests.
type TopRoutesRequestParams struct { type TopRoutesRequestParams struct {
StatsBaseRequestParams StatsBaseRequestParams
To string To string
ToAll bool ToAll bool
} }
// TapRequestParams contains parameters that are used to build a
// TapByResourceRequest.
type TapRequestParams struct { type TapRequestParams struct {
Resource string Resource string
Namespace string Namespace string
@ -119,6 +125,8 @@ func GRPCError(err error) error {
return err return err
} }
// BuildStatSummaryRequest builds a Public API StatSummaryRequest from a
// StatsSummaryRequestParams.
func BuildStatSummaryRequest(p StatsSummaryRequestParams) (*pb.StatSummaryRequest, error) { func BuildStatSummaryRequest(p StatsSummaryRequestParams) (*pb.StatSummaryRequest, error) {
window := defaultMetricTimeWindow window := defaultMetricTimeWindow
if p.TimeWindow != "" { if p.TimeWindow != "" {
@ -206,6 +214,8 @@ func BuildStatSummaryRequest(p StatsSummaryRequestParams) (*pb.StatSummaryReques
return statRequest, nil return statRequest, nil
} }
// BuildTopRoutesRequest builds a Public API TopRoutesRequest from a
// TopRoutesRequestParams.
func BuildTopRoutesRequest(p TopRoutesRequestParams) (*pb.TopRoutesRequest, error) { func BuildTopRoutesRequest(p TopRoutesRequestParams) (*pb.TopRoutesRequest, error) {
window := defaultMetricTimeWindow window := defaultMetricTimeWindow
if p.TimeWindow != "" { if p.TimeWindow != "" {
@ -372,6 +382,8 @@ func buildResource(namespace string, resType string, name string) (pb.Resource,
}, nil }, nil
} }
// BuildTapByResourceRequest builds a Public API TapByResourceRequest from a
// TapRequestParams.
func BuildTapByResourceRequest(params TapRequestParams) (*pb.TapByResourceRequest, error) { func BuildTapByResourceRequest(params TapRequestParams) (*pb.TapByResourceRequest, error) {
target, err := BuildResource(params.Namespace, params.Resource) target, err := BuildResource(params.Namespace, params.Resource)
if err != nil { if err != nil {
@ -535,6 +547,8 @@ func routeLabels(event *pb.TapEvent) string {
return out return out
} }
// RenderTapEvent renders a Public API TapEvent to a string.
// TODO: consider moving this into cli/cmd/tap.go.
func RenderTapEvent(event *pb.TapEvent, resource string) string { func RenderTapEvent(event *pb.TapEvent, resource string) string {
dst := dst(event) dst := dst(event)
src := src(event) src := src(event)
@ -634,6 +648,8 @@ func RenderTapEvent(event *pb.TapEvent, resource string) string {
} }
} }
// GetRequestRate calculates request rate from Public API BasicStats.
// TODO: consider moving this into `/cli/cmd`.
func GetRequestRate(stats *pb.BasicStats, timeWindow string) float64 { func GetRequestRate(stats *pb.BasicStats, timeWindow string) float64 {
success := stats.SuccessCount success := stats.SuccessCount
failure := stats.FailureCount failure := stats.FailureCount
@ -645,6 +661,8 @@ func GetRequestRate(stats *pb.BasicStats, timeWindow string) float64 {
return float64(success+failure) / windowLength.Seconds() return float64(success+failure) / windowLength.Seconds()
} }
// GetSuccessRate calculates success rate from Public API BasicStats.
// TODO: consider moving this into `/cli/cmd`.
func GetSuccessRate(stats *pb.BasicStats) float64 { func GetSuccessRate(stats *pb.BasicStats) float64 {
success := stats.SuccessCount success := stats.SuccessCount
failure := stats.FailureCount failure := stats.FailureCount
@ -655,6 +673,9 @@ func GetSuccessRate(stats *pb.BasicStats) float64 {
return float64(success) / float64(success+failure) return float64(success) / float64(success+failure)
} }
// GetPercentTLS calculates the percent of traffic that is TLS, from Public API
// BasicStats.
// TODO: consider moving this into `/cli/cmd/stat.go`.
func GetPercentTLS(stats *pb.BasicStats) float64 { func GetPercentTLS(stats *pb.BasicStats) float64 {
reqTotal := stats.SuccessCount + stats.FailureCount reqTotal := stats.SuccessCount + stats.FailureCount
if reqTotal == 0 { if reqTotal == 0 {

View File

@ -48,6 +48,7 @@ type CA struct {
nextSerialNumber uint64 nextSerialNumber uint64
} }
// CertificateAndPrivateKey encapsulates a certificate / private key pair.
type CertificateAndPrivateKey struct { type CertificateAndPrivateKey struct {
// The ASN.1 DER-encoded (binary, not PEM) certificate. // The ASN.1 DER-encoded (binary, not PEM) certificate.
Certificate []byte Certificate []byte

View File

@ -18,6 +18,8 @@ import (
"k8s.io/client-go/util/workqueue" "k8s.io/client-go/util/workqueue"
) )
// CertificateController listens for added and updated meshed pods, and then
// provides certificates in the form of secrets.
type CertificateController struct { type CertificateController struct {
namespace string namespace string
k8sAPI *k8s.API k8sAPI *k8s.API
@ -32,6 +34,8 @@ type CertificateController struct {
queue workqueue.RateLimitingInterface queue workqueue.RateLimitingInterface
} }
// NewCertificateController initializes a CertificateController and its
// internal Certificate Authority.
func NewCertificateController(controllerNamespace string, k8sAPI *k8s.API, proxyAutoInject bool) (*CertificateController, error) { func NewCertificateController(controllerNamespace string, k8sAPI *k8s.API, proxyAutoInject bool) (*CertificateController, error) {
ca, err := NewCA() ca, err := NewCA()
if err != nil { if err != nil {
@ -67,6 +71,7 @@ func NewCertificateController(controllerNamespace string, k8sAPI *k8s.API, proxy
return c, nil return c, nil
} }
// Run kicks off CertificateController queue processing.
func (c *CertificateController) Run(stopCh <-chan struct{}) { func (c *CertificateController) Run(stopCh <-chan struct{}) {
defer runtime.HandleCrash() defer runtime.HandleCrash()
defer c.queue.ShutDown() defer c.queue.ShutDown()

View File

@ -1,3 +1,4 @@
package serviceprofile package serviceprofile
// GroupName identifies the API Group Name for a ServiceProfile.
const GroupName = "linkerd.io" const GroupName = "linkerd.io"

View File

@ -26,8 +26,15 @@ func Resource(resource string) schema.GroupResource {
} }
var ( var (
// SchemeBuilder collects functions that add things to a scheme. It's to allow
// code to compile without explicitly referencing generated types. You should
// declare one in each package that will have generated deep copy or conversion
// functions.
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
AddToScheme = SchemeBuilder.AddToScheme
// AddToScheme applies all the stored functions to the scheme. A non-nil error
// indicates that one function failed and the attempt was abandoned.
AddToScheme = SchemeBuilder.AddToScheme
) )
// Adds the list of known types to Scheme. // Adds the list of known types to Scheme.

View File

@ -25,16 +25,19 @@ type ServiceProfile struct {
Spec ServiceProfileSpec `json:"spec"` Spec ServiceProfileSpec `json:"spec"`
} }
// ServiceProfileSpec specifies a ServiceProfile resource.
type ServiceProfileSpec struct { type ServiceProfileSpec struct {
Routes []*RouteSpec `json:"routes"` Routes []*RouteSpec `json:"routes"`
} }
// RouteSpec specifies a Route resource.
type RouteSpec struct { type RouteSpec struct {
Name string `json:"name"` Name string `json:"name"`
Condition *RequestMatch `json:"condition"` Condition *RequestMatch `json:"condition"`
ResponseClasses []*ResponseClass `json:"responseClasses,omitempty"` ResponseClasses []*ResponseClass `json:"responseClasses,omitempty"`
} }
// RequestMatch describes the conditions under which to match a Route.
type RequestMatch struct { type RequestMatch struct {
All []*RequestMatch `json:"all,omitempty"` All []*RequestMatch `json:"all,omitempty"`
Not *RequestMatch `json:"not,omitempty"` Not *RequestMatch `json:"not,omitempty"`
@ -43,11 +46,14 @@ type RequestMatch struct {
Method string `json:"method,omitempty"` Method string `json:"method,omitempty"`
} }
// ResponseClass describes how to classify a response (e.g. success or
// failures).
type ResponseClass struct { type ResponseClass struct {
Condition *ResponseMatch `json:"condition"` Condition *ResponseMatch `json:"condition"`
IsFailure bool `json:"isFailure,omitempty"` IsFailure bool `json:"isFailure,omitempty"`
} }
// ResponseMatch describes the conditions under which to classify a response.
type ResponseMatch struct { type ResponseMatch struct {
All []*ResponseMatch `json:"all,omitempty"` All []*ResponseMatch `json:"all,omitempty"`
Not *ResponseMatch `json:"not,omitempty"` Not *ResponseMatch `json:"not,omitempty"`
@ -55,6 +61,7 @@ type ResponseMatch struct {
Status *Range `json:"status,omitempty"` Status *Range `json:"status,omitempty"`
} }
// Range describes a range of integers (e.g. status codes).
type Range struct { type Range struct {
Min uint32 `json:"min,omitempty"` Min uint32 `json:"min,omitempty"`
Max uint32 `json:"max,omitempty"` Max uint32 `json:"max,omitempty"`
@ -62,7 +69,7 @@ type Range struct {
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// ServiceProfileList is a list of ServiceProfile resources // ServiceProfileList is a list of ServiceProfile resources.
type ServiceProfileList struct { type ServiceProfileList struct {
metav1.TypeMeta `json:",inline"` metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"` metav1.ListMeta `json:"metadata"`

View File

@ -26,8 +26,11 @@ import (
"k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/cache"
) )
// APIResource is an enum for Kubernetes API resource types, for use when
// initializing a K8s API, to describe which resource types to interact with.
type APIResource int type APIResource int
// These constants enumerate Kubernetes resource types.
const ( const (
CM APIResource = iota CM APIResource = iota
Deploy Deploy
@ -140,6 +143,7 @@ func (api *API) Sync() {
log.Infof("caches synced") log.Infof("caches synced")
} }
// Deploy provides access to a shared informer and lister for Deployments.
func (api *API) Deploy() appinformers.DeploymentInformer { func (api *API) Deploy() appinformers.DeploymentInformer {
if api.deploy == nil { if api.deploy == nil {
panic("Deploy informer not configured") panic("Deploy informer not configured")
@ -147,6 +151,7 @@ func (api *API) Deploy() appinformers.DeploymentInformer {
return api.deploy return api.deploy
} }
// RS provides access to a shared informer and lister for ReplicaSets.
func (api *API) RS() appinformers.ReplicaSetInformer { func (api *API) RS() appinformers.ReplicaSetInformer {
if api.rs == nil { if api.rs == nil {
panic("RS informer not configured") panic("RS informer not configured")
@ -154,6 +159,7 @@ func (api *API) RS() appinformers.ReplicaSetInformer {
return api.rs return api.rs
} }
// Pod provides access to a shared informer and lister for Pods.
func (api *API) Pod() coreinformers.PodInformer { func (api *API) Pod() coreinformers.PodInformer {
if api.pod == nil { if api.pod == nil {
panic("Pod informer not configured") panic("Pod informer not configured")
@ -161,6 +167,8 @@ func (api *API) Pod() coreinformers.PodInformer {
return api.pod return api.pod
} }
// RC provides access to a shared informer and lister for
// ReplicationControllers.
func (api *API) RC() coreinformers.ReplicationControllerInformer { func (api *API) RC() coreinformers.ReplicationControllerInformer {
if api.rc == nil { if api.rc == nil {
panic("RC informer not configured") panic("RC informer not configured")
@ -168,6 +176,7 @@ func (api *API) RC() coreinformers.ReplicationControllerInformer {
return api.rc return api.rc
} }
// Svc provides access to a shared informer and lister for Services.
func (api *API) Svc() coreinformers.ServiceInformer { func (api *API) Svc() coreinformers.ServiceInformer {
if api.svc == nil { if api.svc == nil {
panic("Svc informer not configured") panic("Svc informer not configured")
@ -175,6 +184,7 @@ func (api *API) Svc() coreinformers.ServiceInformer {
return api.svc return api.svc
} }
// Endpoint provides access to a shared informer and lister for Endpoints.
func (api *API) Endpoint() coreinformers.EndpointsInformer { func (api *API) Endpoint() coreinformers.EndpointsInformer {
if api.endpoint == nil { if api.endpoint == nil {
panic("Endpoint informer not configured") panic("Endpoint informer not configured")
@ -182,6 +192,7 @@ func (api *API) Endpoint() coreinformers.EndpointsInformer {
return api.endpoint return api.endpoint
} }
// CM provides access to a shared informer and lister for ConfigMaps.
func (api *API) CM() coreinformers.ConfigMapInformer { func (api *API) CM() coreinformers.ConfigMapInformer {
if api.cm == nil { if api.cm == nil {
panic("CM informer not configured") panic("CM informer not configured")
@ -189,6 +200,7 @@ func (api *API) CM() coreinformers.ConfigMapInformer {
return api.cm return api.cm
} }
// SP provides access to a shared informer and lister for ServiceProfiles.
func (api *API) SP() spinformers.ServiceProfileInformer { func (api *API) SP() spinformers.ServiceProfileInformer {
if api.sp == nil { if api.sp == nil {
panic("SP informer not configured") panic("SP informer not configured")
@ -196,6 +208,7 @@ func (api *API) SP() spinformers.ServiceProfileInformer {
return api.sp return api.sp
} }
// MWC provides access to a shared informer and lister for MutatingWebhookConfigurations.
func (api *API) MWC() arinformers.MutatingWebhookConfigurationInformer { func (api *API) MWC() arinformers.MutatingWebhookConfigurationInformer {
if api.mwc == nil { if api.mwc == nil {
panic("MWC informer not configured") panic("MWC informer not configured")
@ -439,6 +452,8 @@ func (api *API) getServices(namespace, name string) ([]runtime.Object, error) {
return objects, nil return objects, nil
} }
// GetServices returns a list of Service resources, based on input namespace and
// name.
func (api *API) GetServices(namespace, name string) ([]*apiv1.Service, error) { func (api *API) GetServices(namespace, name string) ([]*apiv1.Service, error) {
var err error var err error
var services []*apiv1.Service var services []*apiv1.Service

View File

@ -4,10 +4,12 @@ import (
spclient "github.com/linkerd/linkerd2/controller/gen/client/clientset/versioned" spclient "github.com/linkerd/linkerd2/controller/gen/client/clientset/versioned"
"github.com/linkerd/linkerd2/pkg/k8s" "github.com/linkerd/linkerd2/pkg/k8s"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
// Load all the auth plugins for the cloud providers. // Load all the auth plugins for the cloud providers.
_ "k8s.io/client-go/plugin/pkg/client/auth" _ "k8s.io/client-go/plugin/pkg/client/auth"
) )
// NewClientSet returns a Kubernetes client for the given configuration.
func NewClientSet(kubeConfig string) (*kubernetes.Clientset, error) { func NewClientSet(kubeConfig string) (*kubernetes.Clientset, error) {
config, err := k8s.GetConfig(kubeConfig, "") config, err := k8s.GetConfig(kubeConfig, "")
if err != nil { if err != nil {
@ -17,6 +19,8 @@ func NewClientSet(kubeConfig string) (*kubernetes.Clientset, error) {
return kubernetes.NewForConfig(config) return kubernetes.NewForConfig(config)
} }
// NewSpClientSet returns a Kubernetes ServiceProfile client for the given
// configuration.
func NewSpClientSet(kubeConfig string) (*spclient.Clientset, error) { func NewSpClientSet(kubeConfig string) (*spclient.Clientset, error) {
config, err := k8s.GetConfig(kubeConfig, "") config, err := k8s.GetConfig(kubeConfig, "")
if err != nil { if err != nil {

View File

@ -18,6 +18,7 @@ func toRuntimeObject(config string) (runtime.Object, error) {
return obj, err return obj, err
} }
// NewFakeAPI provides a mock Kubernetes API for testing.
func NewFakeAPI(namespace string, configs ...string) (*API, error) { func NewFakeAPI(namespace string, configs ...string) (*API, error) {
objs := []runtime.Object{} objs := []runtime.Object{}
spObjs := []runtime.Object{} spObjs := []runtime.Object{}

View File

@ -11,6 +11,7 @@ import (
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
) )
// These constants provide default, fake strings for testing proxy-injector.
const ( const (
DefaultControllerNamespace = "linkerd" DefaultControllerNamespace = "linkerd"
DefaultNamespace = "default" DefaultNamespace = "default"

View File

@ -1,5 +1,7 @@
package tmpl package tmpl
// MutatingWebhookConfigurationSpec provides a template for a
// MutatingWebhookConfiguration.
var MutatingWebhookConfigurationSpec = ` var MutatingWebhookConfigurationSpec = `
apiVersion: admissionregistration.k8s.io/v1beta1 apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingWebhookConfiguration kind: MutatingWebhookConfiguration

View File

@ -5,6 +5,7 @@ import (
"google.golang.org/grpc" "google.golang.org/grpc"
) )
// NewClient creates a client for the control-plane's Tap service.
func NewClient(addr string) (pb.TapClient, *grpc.ClientConn, error) { func NewClient(addr string) (pb.TapClient, *grpc.ClientConn, error) {
conn, err := grpc.Dial(addr, grpc.WithInsecure()) conn, err := grpc.Dial(addr, grpc.WithInsecure())
if err != nil { if err != nil {

View File

@ -9,21 +9,25 @@ import (
"github.com/linkerd/linkerd2/controller/gen/public" "github.com/linkerd/linkerd2/controller/gen/public"
) )
// PublicAddressToString formats a Public API TCPAddress as a string.
func PublicAddressToString(addr *public.TcpAddress) string { func PublicAddressToString(addr *public.TcpAddress) string {
octects := decodeIPToOctets(addr.GetIp().GetIpv4()) octects := decodeIPToOctets(addr.GetIp().GetIpv4())
return fmt.Sprintf("%d.%d.%d.%d:%d", octects[0], octects[1], octects[2], octects[3], addr.GetPort()) return fmt.Sprintf("%d.%d.%d.%d:%d", octects[0], octects[1], octects[2], octects[3], addr.GetPort())
} }
// PublicIPToString formats a Public API IPAddress as a string.
func PublicIPToString(ip *public.IPAddress) string { func PublicIPToString(ip *public.IPAddress) string {
octets := decodeIPToOctets(ip.GetIpv4()) octets := decodeIPToOctets(ip.GetIpv4())
return fmt.Sprintf("%d.%d.%d.%d", octets[0], octets[1], octets[2], octets[3]) return fmt.Sprintf("%d.%d.%d.%d", octets[0], octets[1], octets[2], octets[3])
} }
// ProxyAddressToString formats a Proxy API TCPAddress as a string.
func ProxyAddressToString(addr *pb.TcpAddress) string { func ProxyAddressToString(addr *pb.TcpAddress) string {
octects := decodeIPToOctets(addr.GetIp().GetIpv4()) octects := decodeIPToOctets(addr.GetIp().GetIpv4())
return fmt.Sprintf("%d.%d.%d.%d:%d", octects[0], octects[1], octects[2], octects[3], addr.GetPort()) return fmt.Sprintf("%d.%d.%d.%d:%d", octects[0], octects[1], octects[2], octects[3], addr.GetPort())
} }
// ProxyAddressesToString formats a list of Proxy API TCPAddresses as a string.
func ProxyAddressesToString(addrs []pb.TcpAddress) string { func ProxyAddressesToString(addrs []pb.TcpAddress) string {
addrStrs := make([]string, len(addrs)) addrStrs := make([]string, len(addrs))
for i := range addrs { for i := range addrs {
@ -32,11 +36,13 @@ func ProxyAddressesToString(addrs []pb.TcpAddress) string {
return "[" + strings.Join(addrStrs, ",") + "]" return "[" + strings.Join(addrStrs, ",") + "]"
} }
// ProxyIPToString formats a Proxy API IPAddress as a string.
func ProxyIPToString(ip *pb.IPAddress) string { func ProxyIPToString(ip *pb.IPAddress) string {
octets := decodeIPToOctets(ip.GetIpv4()) octets := decodeIPToOctets(ip.GetIpv4())
return fmt.Sprintf("%d.%d.%d.%d", octets[0], octets[1], octets[2], octets[3]) return fmt.Sprintf("%d.%d.%d.%d", octets[0], octets[1], octets[2], octets[3])
} }
// ProxyIPV4 encodes 4 octets as a Proxy API IPAddress.
func ProxyIPV4(a1, a2, a3, a4 uint8) *pb.IPAddress { func ProxyIPV4(a1, a2, a3, a4 uint8) *pb.IPAddress {
ip := (uint32(a1) << 24) | (uint32(a2) << 16) | (uint32(a3) << 8) | uint32(a4) ip := (uint32(a1) << 24) | (uint32(a2) << 16) | (uint32(a3) << 8) | uint32(a4)
return &pb.IPAddress{ return &pb.IPAddress{
@ -46,6 +52,7 @@ func ProxyIPV4(a1, a2, a3, a4 uint8) *pb.IPAddress {
} }
} }
// ParseProxyIPV4 parses an IP Address string into a Proxy API IPAddress.
func ParseProxyIPV4(ip string) (*pb.IPAddress, error) { func ParseProxyIPV4(ip string) (*pb.IPAddress, error) {
segments := strings.Split(ip, ".") segments := strings.Split(ip, ".")
if len(segments) != 4 { if len(segments) != 4 {
@ -62,6 +69,7 @@ func ParseProxyIPV4(ip string) (*pb.IPAddress, error) {
return ProxyIPV4(octets[0], octets[1], octets[2], octets[3]), nil return ProxyIPV4(octets[0], octets[1], octets[2], octets[3]), nil
} }
// PublicIPV4 encodes 4 octets as a Public API IPAddress.
func PublicIPV4(a1, a2, a3, a4 uint8) *public.IPAddress { func PublicIPV4(a1, a2, a3, a4 uint8) *public.IPAddress {
ip := (uint32(a1) << 24) | (uint32(a2) << 16) | (uint32(a3) << 8) | uint32(a4) ip := (uint32(a1) << 24) | (uint32(a2) << 16) | (uint32(a3) << 8) | uint32(a4)
return &public.IPAddress{ return &public.IPAddress{
@ -71,6 +79,7 @@ func PublicIPV4(a1, a2, a3, a4 uint8) *public.IPAddress {
} }
} }
// ParsePublicIPV4 parses an IP Address string into a Public API IPAddress.
func ParsePublicIPV4(ip string) (*public.IPAddress, error) { func ParsePublicIPV4(ip string) (*public.IPAddress, error) {
segments := strings.Split(ip, ".") segments := strings.Split(ip, ".")
if len(segments) != 4 { if len(segments) != 4 {

View File

@ -12,6 +12,7 @@ type handler struct {
promHandler http.Handler promHandler http.Handler
} }
// StartServer starts an admin server listening on a given address.
func StartServer(addr string) { func StartServer(addr string) {
log.Infof("starting admin server on %s", addr) log.Infof("starting admin server on %s", addr)

View File

@ -12,6 +12,8 @@ import (
"os" "os"
) )
// FileSystem provides access to a collection of named files via
// http.FileSystem, given a directory.
func FileSystem(dir string) http.FileSystem { func FileSystem(dir string) http.FileSystem {
return fileSystem{http.Dir(dir)} return fileSystem{http.Dir(dir)}
} }

View File

@ -21,6 +21,7 @@ import (
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
) )
// Checks is an enum for the types of health checks.
type Checks int type Checks int
const ( const (
@ -56,11 +57,19 @@ const (
// and ShouldCheckDataPlaneVersion options are false. // and ShouldCheckDataPlaneVersion options are false.
LinkerdVersionChecks LinkerdVersionChecks
KubernetesAPICategory = "kubernetes-api" // KubernetesAPICategory is the string representation of KubernetesAPIChecks.
KubernetesAPICategory = "kubernetes-api"
// LinkerdPreInstallCategory is the string representation of
// LinkerdPreInstallChecks.
LinkerdPreInstallCategory = "kubernetes-setup" LinkerdPreInstallCategory = "kubernetes-setup"
LinkerdDataPlaneCategory = "linkerd-data-plane" // LinkerdDataPlaneCategory is the string representation of
LinkerdAPICategory = "linkerd-api" // LinkerdDataPlaneChecks.
LinkerdVersionCategory = "linkerd-version" LinkerdDataPlaneCategory = "linkerd-data-plane"
// LinkerdAPICategory is the string representation of LinkerdAPIChecks.
LinkerdAPICategory = "linkerd-api"
// LinkerdVersionCategory is the string representation of
// LinkerdVersionChecks.
LinkerdVersionCategory = "linkerd-version"
) )
var ( var (
@ -100,6 +109,7 @@ type checker struct {
checkRPC func() (*healthcheckPb.SelfCheckResponse, error) checkRPC func() (*healthcheckPb.SelfCheckResponse, error)
} }
// CheckResult encapsulates a check's identifying information and output
type CheckResult struct { type CheckResult struct {
Category string Category string
Description string Description string
@ -110,6 +120,7 @@ type CheckResult struct {
type checkObserver func(*CheckResult) type checkObserver func(*CheckResult)
// Options specifies configuration for a HealthChecker.
type Options struct { type Options struct {
ControlPlaneNamespace string ControlPlaneNamespace string
DataPlaneNamespace string DataPlaneNamespace string
@ -124,6 +135,8 @@ type Options struct {
SingleNamespace bool SingleNamespace bool
} }
// HealthChecker encapsulates all health check checkers, and clients required to
// perform those checks.
type HealthChecker struct { type HealthChecker struct {
checkers []*checker checkers []*checker
*Options *Options
@ -139,6 +152,7 @@ type HealthChecker struct {
latestVersion string latestVersion string
} }
// NewHealthChecker returns an initialized HealthChecker
func NewHealthChecker(checks []Checks, options *Options) *HealthChecker { func NewHealthChecker(checks []Checks, options *Options) *HealthChecker {
hc := &HealthChecker{ hc := &HealthChecker{
checkers: make([]*checker, 0), checkers: make([]*checker, 0),

View File

@ -19,10 +19,13 @@ import (
var minAPIVersion = [3]int{1, 8, 0} var minAPIVersion = [3]int{1, 8, 0}
// KubernetesAPI provides a client for accessing a Kubernetes cluster.
type KubernetesAPI struct { type KubernetesAPI struct {
*rest.Config *rest.Config
} }
// NewClient returns an http.Client configured with a Transport to connect to
// the Kubernetes cluster.
func (kubeAPI *KubernetesAPI) NewClient() (*http.Client, error) { func (kubeAPI *KubernetesAPI) NewClient() (*http.Client, error) {
secureTransport, err := rest.TransportFor(kubeAPI.Config) secureTransport, err := rest.TransportFor(kubeAPI.Config)
if err != nil { if err != nil {
@ -34,6 +37,7 @@ func (kubeAPI *KubernetesAPI) NewClient() (*http.Client, error) {
}, nil }, nil
} }
// GetVersionInfo returns version.Info for the Kubernetes cluster.
func (kubeAPI *KubernetesAPI) GetVersionInfo(client *http.Client) (*version.Info, error) { func (kubeAPI *KubernetesAPI) GetVersionInfo(client *http.Client) (*version.Info, error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel() defer cancel()
@ -58,6 +62,8 @@ func (kubeAPI *KubernetesAPI) GetVersionInfo(client *http.Client) (*version.Info
return &versionInfo, err return &versionInfo, err
} }
// CheckVersion validates whether the configured Kubernetes cluster's version is
// running a minimum Kubernetes API version.
func (kubeAPI *KubernetesAPI) CheckVersion(versionInfo *version.Info) error { func (kubeAPI *KubernetesAPI) CheckVersion(versionInfo *version.Info) error {
apiVersion, err := getK8sVersion(versionInfo.String()) apiVersion, err := getK8sVersion(versionInfo.String())
if err != nil { if err != nil {
@ -73,6 +79,7 @@ func (kubeAPI *KubernetesAPI) CheckVersion(versionInfo *version.Info) error {
return nil return nil
} }
// NamespaceExists validates whether a given namespace exists.
func (kubeAPI *KubernetesAPI) NamespaceExists(client *http.Client, namespace string) (bool, error) { func (kubeAPI *KubernetesAPI) NamespaceExists(client *http.Client, namespace string) (bool, error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel() defer cancel()

View File

@ -8,6 +8,7 @@ import (
"k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd"
) )
// These constants are string representations of Kubernetes resource types.
const ( const (
All = "all" All = "all"
Authority = "authority" Authority = "authority"
@ -159,6 +160,10 @@ func ShortNameFromCanonicalResourceName(canonicalName string) string {
} }
} }
// KindToL5DLabel converts a Kubernetes `kind` to a Linkerd label.
// For example:
// `pod` -> `pod`
// `job` -> `k8s_job`
func KindToL5DLabel(k8sKind string) string { func KindToL5DLabel(k8sKind string) string {
if k8sKind == Job { if k8sKind == Job {
return l5dJob return l5dJob

View File

@ -120,7 +120,12 @@ const (
// that contains the actual trust anchor bundle. // that contains the actual trust anchor bundle.
TLSTrustAnchorFileName = "trust-anchors.pem" TLSTrustAnchorFileName = "trust-anchors.pem"
TLSCertFileName = "certificate.crt" // TLSCertFileName is the name (key) within proxy-injector ConfigMap that
// contains the TLS certificate.
TLSCertFileName = "certificate.crt"
// TLSPrivateKeyFileName is the name (key) within proxy-injector ConfigMap
// that contains the TLS private key.
TLSPrivateKeyFileName = "private-key.p8" TLSPrivateKeyFileName = "private-key.p8"
/* /*
@ -185,6 +190,7 @@ func GetPodLabels(ownerKind, ownerName string, pod *coreV1.Pod) map[string]strin
return labels return labels
} }
// IsMeshed returns whether a given Pod is in a given controller's service mesh.
func IsMeshed(pod *coreV1.Pod, controllerNS string) bool { func IsMeshed(pod *coreV1.Pod, controllerNS string) bool {
return pod.Labels[ControllerNSLabel] == controllerNS return pod.Labels[ControllerNSLabel] == controllerNS
} }
@ -207,6 +213,7 @@ type TLSIdentity struct {
ControllerNamespace string ControllerNamespace string
} }
// ToDNSName formats a TLSIdentity as a DNS name.
func (i TLSIdentity) ToDNSName() string { func (i TLSIdentity) ToDNSName() string {
if i.Kind == Service { if i.Kind == Service {
return fmt.Sprintf("%s.%s.svc", i.Name, i.Namespace) return fmt.Sprintf("%s.%s.svc", i.Name, i.Namespace)
@ -215,10 +222,13 @@ func (i TLSIdentity) ToDNSName() string {
i.Kind, i.Namespace, i.ControllerNamespace) i.Kind, i.Namespace, i.ControllerNamespace)
} }
// ToSecretName formats a TLSIdentity as a secret name.
func (i TLSIdentity) ToSecretName() string { func (i TLSIdentity) ToSecretName() string {
return fmt.Sprintf("%s-%s-tls-linkerd-io", i.Name, i.Kind) return fmt.Sprintf("%s-%s-tls-linkerd-io", i.Name, i.Kind)
} }
// ToControllerIdentity returns the TLSIdentity of the Linkerd Controller, given
// an arbitrary TLSIdentity.
func (i TLSIdentity) ToControllerIdentity() TLSIdentity { func (i TLSIdentity) ToControllerIdentity() TLSIdentity {
return TLSIdentity{ return TLSIdentity{
Name: "controller", Name: "controller",

View File

@ -13,6 +13,7 @@ import (
_ "k8s.io/client-go/plugin/pkg/client/auth" _ "k8s.io/client-go/plugin/pkg/client/auth"
) )
// KubernetesProxy provides a proxied connection into a Kubernetes cluster.
type KubernetesProxy struct { type KubernetesProxy struct {
listener net.Listener listener net.Listener
server *proxy.Server server *proxy.Server

View File

@ -18,6 +18,7 @@ type profileTemplateConfig struct {
ClusterZone string ClusterZone string
} }
// ToRoute returns a Proxy API Route, given a ServiceProfile Route.
func ToRoute(route *sp.RouteSpec) (*pb.Route, error) { func ToRoute(route *sp.RouteSpec) (*pb.Route, error) {
cond, err := ToRequestMatch(route.Condition) cond, err := ToRequestMatch(route.Condition)
if err != nil { if err != nil {
@ -38,6 +39,8 @@ func ToRoute(route *sp.RouteSpec) (*pb.Route, error) {
}, nil }, nil
} }
// ToResponseClass returns a Proxy API ResponseClass, given a ServiceProfile
// ResponseClass.
func ToResponseClass(rc *sp.ResponseClass) (*pb.ResponseClass, error) { func ToResponseClass(rc *sp.ResponseClass) (*pb.ResponseClass, error) {
cond, err := ToResponseMatch(rc.Condition) cond, err := ToResponseMatch(rc.Condition)
if err != nil { if err != nil {
@ -49,6 +52,8 @@ func ToResponseClass(rc *sp.ResponseClass) (*pb.ResponseClass, error) {
}, nil }, nil
} }
// ToResponseMatch returns a Proxy API ResponseMatch, given a ServiceProfile
// ResponseMatch.
func ToResponseMatch(rspMatch *sp.ResponseMatch) (*pb.ResponseMatch, error) { func ToResponseMatch(rspMatch *sp.ResponseMatch) (*pb.ResponseMatch, error) {
if rspMatch == nil { if rspMatch == nil {
return nil, errors.New("missing response match") return nil, errors.New("missing response match")
@ -134,6 +139,8 @@ func ToResponseMatch(rspMatch *sp.ResponseMatch) (*pb.ResponseMatch, error) {
}, nil }, nil
} }
// ToRequestMatch returns a Proxy API RequestMatch, given a ServiceProfile
// RequestMatch.
func ToRequestMatch(reqMatch *sp.RequestMatch) (*pb.RequestMatch, error) { func ToRequestMatch(reqMatch *sp.RequestMatch) (*pb.RequestMatch, error) {
if reqMatch == nil { if reqMatch == nil {
return nil, errors.New("missing request match") return nil, errors.New("missing request match")
@ -226,6 +233,8 @@ func ToRequestMatch(reqMatch *sp.RequestMatch) (*pb.RequestMatch, error) {
}, nil }, nil
} }
// ValidateRequestMatch validates whether a ServiceProfile RequestMatch has at
// least one field set.
func ValidateRequestMatch(reqMatch *sp.RequestMatch) error { func ValidateRequestMatch(reqMatch *sp.RequestMatch) error {
matchKindSet := false matchKindSet := false
if reqMatch.All != nil { if reqMatch.All != nil {
@ -267,6 +276,8 @@ func ValidateRequestMatch(reqMatch *sp.RequestMatch) error {
return nil return nil
} }
// ValidateResponseMatch validates whether a ServiceProfile ResponseMatch has at
// least one field set, and sanity checks the Status Range.
func ValidateResponseMatch(rspMatch *sp.ResponseMatch) error { func ValidateResponseMatch(rspMatch *sp.ResponseMatch) error {
invalidRangeErr := errors.New("Range maximum cannot be smaller than minimum") invalidRangeErr := errors.New("Range maximum cannot be smaller than minimum")
matchKindSet := false matchKindSet := false
@ -318,6 +329,8 @@ func buildConfig(namespace, service, controlPlaneNamespace string) *profileTempl
} }
} }
// RenderProfileTemplate renders a ServiceProfile template to a buffer, given a
// namespace, service, and control plane namespace.
func RenderProfileTemplate(namespace, service, controlPlaneNamespace string, w io.Writer) error { func RenderProfileTemplate(namespace, service, controlPlaneNamespace string, w io.Writer) error {
config := buildConfig(namespace, service, controlPlaneNamespace) config := buildConfig(namespace, service, controlPlaneNamespace)
template, err := template.New("profile").Parse(Template) template, err := template.New("profile").Parse(Template)

View File

@ -9,6 +9,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
) )
// GenServiceProfile generates a mock ServiceProfile.
func GenServiceProfile(service, namespace, controlPlaneNamespace string) v1alpha1.ServiceProfile { func GenServiceProfile(service, namespace, controlPlaneNamespace string) v1alpha1.ServiceProfile {
return v1alpha1.ServiceProfile{ return v1alpha1.ServiceProfile{
TypeMeta: metav1.TypeMeta{ TypeMeta: metav1.TypeMeta{
@ -44,6 +45,7 @@ func GenServiceProfile(service, namespace, controlPlaneNamespace string) v1alpha
} }
} }
// ServiceProfileYamlEquals validates whether two ServiceProfiles are equal.
func ServiceProfileYamlEquals(actual, expected v1alpha1.ServiceProfile) error { func ServiceProfileYamlEquals(actual, expected v1alpha1.ServiceProfile) error {
if !reflect.DeepEqual(actual, expected) { if !reflect.DeepEqual(actual, expected) {
acutalYaml, err := yaml.Marshal(actual) acutalYaml, err := yaml.Marshal(actual)

View File

@ -6,6 +6,7 @@ import (
"fmt" "fmt"
) )
// Supported TLS key types.
const ( const (
KeyTypeRSA = "rsa" KeyTypeRSA = "rsa"
KeyTypeECDSA = "ecdsa" KeyTypeECDSA = "ecdsa"

View File

@ -37,6 +37,8 @@ func init() {
} }
} }
// CheckClientVersion validates whether the Linkerd Public API client's version
// matches an expected version.
func CheckClientVersion(expectedVersion string) error { func CheckClientVersion(expectedVersion string) error {
if Version != expectedVersion { if Version != expectedVersion {
return versionMismatchError(expectedVersion, Version) return versionMismatchError(expectedVersion, Version)
@ -45,6 +47,8 @@ func CheckClientVersion(expectedVersion string) error {
return nil return nil
} }
// CheckServerVersion validates whether the Linkerd Public API server's version
// matches an expected version.
func CheckServerVersion(apiClient pb.ApiClient, expectedVersion string) error { func CheckServerVersion(apiClient pb.ApiClient, expectedVersion string) error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel() defer cancel()
@ -61,6 +65,8 @@ func CheckServerVersion(apiClient pb.ApiClient, expectedVersion string) error {
return nil return nil
} }
// GetLatestVersion performs an online request to check for the latest Linkerd
// version.
func GetLatestVersion(uuid string, source string) (string, error) { func GetLatestVersion(uuid string, source string) (string, error) {
url := fmt.Sprintf(versionCheckURL, Version, uuid, source) url := fmt.Sprintf(versionCheckURL, Version, uuid, source)
req, err := http.NewRequest("GET", url, nil) req, err := http.NewRequest("GET", url, nil)

View File

@ -29,6 +29,8 @@ func newRootOptions() *rootOptions {
} }
} }
// NewRootCmd returns a configured cobra.Command for the `proxy-init` command.
// TODO: consider moving this to `/proxy-init/main.go`
func NewRootCmd() *cobra.Command { func NewRootCmd() *cobra.Command {
options := newRootOptions() options := newRootOptions()

View File

@ -10,16 +10,26 @@ import (
) )
const ( const (
RedirectAllMode = "redirect-all" // RedirectAllMode indicates redirecting all ports.
RedirectListedMode = "redirect-listed" RedirectAllMode = "redirect-all"
// RedirectListedMode indicates redirecting a given list of ports.
RedirectListedMode = "redirect-listed"
// IptablesPreroutingChainName specifies an iptables `PREROUTING` chain,
// responsible for packets that just arrived at the network interface.
IptablesPreroutingChainName = "PREROUTING" IptablesPreroutingChainName = "PREROUTING"
IptablesOutputChainName = "OUTPUT"
// IptablesOutputChainName specifies an iptables `OUTPUT` chain.
IptablesOutputChainName = "OUTPUT"
) )
var ( var (
// ExecutionTraceID provides a unique identifier for this script's execution.
ExecutionTraceID = strconv.Itoa(int(time.Now().Unix())) ExecutionTraceID = strconv.Itoa(int(time.Now().Unix()))
) )
// FirewallConfiguration specifies how to configure a pod's iptables.
type FirewallConfiguration struct { type FirewallConfiguration struct {
Mode string Mode string
PortsToRedirectInbound []int PortsToRedirectInbound []int

View File

@ -20,6 +20,7 @@ const (
) )
type ( type (
// Server encapsulates the Linkerd control plane's web dashboard server.
Server struct { Server struct {
templateDir string templateDir string
staticDir string staticDir string
@ -52,6 +53,9 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
s.router.ServeHTTP(w, req) s.router.ServeHTTP(w, req)
} }
// NewServer returns an initialized `http.Server`, configured to listen on an
// address, render templates, and serve static assets, for a given Linkerd
// control plane.
func NewServer(addr, templateDir, staticDir, uuid, controllerNamespace string, singleNamespace bool, webpackDevServer string, reload bool, apiClient pb.ApiClient) *http.Server { func NewServer(addr, templateDir, staticDir, uuid, controllerNamespace string, singleNamespace bool, webpackDevServer string, reload bool, apiClient pb.ApiClient) *http.Server {
server := &Server{ server := &Server{
templateDir: templateDir, templateDir: templateDir,
@ -118,6 +122,8 @@ func NewServer(addr, templateDir, staticDir, uuid, controllerNamespace string, s
return httpServer return httpServer
} }
// RenderTemplate writes a rendered template into a buffer, given an HTTP
// request and template information.
func (s *Server) RenderTemplate(w http.ResponseWriter, templateFile, templateName string, args interface{}) error { func (s *Server) RenderTemplate(w http.ResponseWriter, templateFile, templateName string, args interface{}) error {
log.Debugf("emitting template %s", templateFile) log.Debugf("emitting template %s", templateFile)
template, err := s.loadTemplate(templateFile) template, err := s.loadTemplate(templateFile)

View File

@ -1,5 +1,6 @@
package srv package srv
// FakeServer provides a mock of a Server in `/web/srv`.
func FakeServer() Server { func FakeServer() Server {
return Server{ return Server{
templateDir: "../templates", templateDir: "../templates",