-
Notifications
You must be signed in to change notification settings - Fork 324
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Ashwin Venkatesh <ashwin@hashicorp.com>
- Loading branch information
Ashwin Venkatesh
committed
Aug 13, 2020
1 parent
85c7713
commit ee44718
Showing
46 changed files
with
2,531 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
/* | ||
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 v1alpha1 contains API Schema definitions for the consul.hashicorp.com v1alpha1 API group | ||
// +kubebuilder:object:generate=true | ||
// +groupName=consul.hashicorp.com | ||
package v1alpha1 | ||
|
||
import ( | ||
"k8s.io/apimachinery/pkg/runtime/schema" | ||
"sigs.k8s.io/controller-runtime/pkg/scheme" | ||
) | ||
|
||
var ( | ||
// GroupVersion is group version used to register these objects | ||
GroupVersion = schema.GroupVersion{Group: "consul.hashicorp.com", Version: "v1alpha1"} | ||
|
||
// SchemeBuilder is used to add go types to the GroupVersionKind scheme | ||
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} | ||
|
||
// AddToScheme adds the types in this group-version to the given scheme. | ||
AddToScheme = SchemeBuilder.AddToScheme | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
/* | ||
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 v1alpha1 | ||
|
||
import ( | ||
consulapi "github.com/hashicorp/consul/api" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
) | ||
|
||
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! | ||
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. | ||
|
||
// ServiceDefaultsSpec defines the desired state of ServiceDefaults | ||
type ServiceDefaultsSpec struct { | ||
Protocol string `json:"protocol,omitempty"` | ||
MeshGateway MeshGatewayConfig `json:"meshGateway,omitempty"` | ||
Expose ExposeConfig `json:"expose,omitempty"` | ||
ExternalSNI string `json:"externalSNI,omitempty"` | ||
} | ||
|
||
// ServiceDefaultsStatus defines the observed state of ServiceDefaults | ||
type ServiceDefaultsStatus struct { | ||
Status `json:",inline"` | ||
} | ||
|
||
// +kubebuilder:object:root=true | ||
// +kubebuilder:subresource:status | ||
|
||
// ServiceDefaults is the Schema for the servicedefaults API | ||
// +kubebuilder:printcolumn:name="Synced",type="string",JSONPath=".status.conditions[?(@.type==\"Synced\")].status",description="The sync status of the resource with Consul" | ||
type ServiceDefaults struct { | ||
metav1.TypeMeta `json:",inline"` | ||
metav1.ObjectMeta `json:"metadata,omitempty"` | ||
|
||
Spec ServiceDefaultsSpec `json:"spec,omitempty"` | ||
Status ServiceDefaultsStatus `json:"status,omitempty"` | ||
} | ||
|
||
// +kubebuilder:object:root=true | ||
|
||
// ServiceDefaultsList contains a list of ServiceDefaults | ||
type ServiceDefaultsList struct { | ||
metav1.TypeMeta `json:",inline"` | ||
metav1.ListMeta `json:"metadata,omitempty"` | ||
Items []ServiceDefaults `json:"items"` | ||
} | ||
|
||
func init() { | ||
SchemeBuilder.Register(&ServiceDefaults{}, &ServiceDefaultsList{}) | ||
} | ||
|
||
func (s *ServiceDefaults) ToConsul() *consulapi.ServiceConfigEntry { | ||
return &consulapi.ServiceConfigEntry{ | ||
Kind: consulapi.ServiceDefaults, | ||
Name: s.Name, | ||
//Namespace: s.Namespace, // todo: don't set this unless enterprise | ||
Protocol: s.Spec.Protocol, | ||
MeshGateway: consulapi.MeshGatewayConfig{ | ||
Mode: consulapi.MeshGatewayModeDefault, //this will change. forcing it to default for now. | ||
}, | ||
Expose: consulapi.ExposeConfig{ | ||
Checks: s.Spec.Expose.Checks, | ||
Paths: []consulapi.ExposePath{}, //will create a helper on our expose paths to translate to consul expose paths | ||
}, | ||
ExternalSNI: s.Spec.ExternalSNI, | ||
} | ||
} | ||
|
||
// this will check if the consul struct shares the same spec as the spec of the resource | ||
func (s *ServiceDefaults) MatchesConsul(entry *consulapi.ServiceConfigEntry) bool { | ||
matches := s.Name == entry.GetName() && | ||
s.Spec.Protocol == entry.Protocol && | ||
s.Spec.MeshGateway.Mode == string(entry.MeshGateway.Mode) && | ||
s.Spec.Expose.Checks == entry.Expose.Checks && | ||
s.Spec.ExternalSNI == entry.ExternalSNI | ||
if !matches { | ||
return false | ||
} | ||
|
||
// Also check each exposed path config. | ||
if len(s.Spec.Expose.Paths) != len(entry.Expose.Paths) { | ||
return false | ||
} | ||
for _, path := range s.Spec.Expose.Paths { | ||
found := false | ||
for _, entryPath := range entry.Expose.Paths { | ||
if path.ParsedFromCheck == entryPath.ParsedFromCheck && | ||
path.Protocol == entryPath.Protocol && | ||
path.Path == entryPath.Path && | ||
path.ListenerPort == entryPath.ListenerPort && | ||
path.LocalPathPort == entryPath.LocalPathPort { | ||
found = true | ||
break | ||
} | ||
} | ||
|
||
if !found { | ||
return false | ||
} | ||
} | ||
return true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
/* | ||
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 v1alpha1 | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
"github.com/hashicorp/consul/api" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
ctrl "sigs.k8s.io/controller-runtime" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
logf "sigs.k8s.io/controller-runtime/pkg/log" | ||
"sigs.k8s.io/controller-runtime/pkg/webhook" | ||
) | ||
|
||
// log is for logging in this package. | ||
var servicedefaultslog = logf.Log.WithName("servicedefaults-resource") | ||
|
||
// todo: use our own validating webhook so we can inject this properly | ||
var ConsulClient *api.Client | ||
var KubeClient client.Client | ||
|
||
func (r *ServiceDefaults) SetupWebhookWithManager(mgr ctrl.Manager) error { | ||
return ctrl.NewWebhookManagedBy(mgr). | ||
For(r). | ||
Complete() | ||
} | ||
|
||
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! | ||
|
||
// +kubebuilder:webhook:path=/mutate-consul-hashicorp-com-v1alpha1-servicedefaults,mutating=true,failurePolicy=fail,groups=consul.hashicorp.com,resources=servicedefaults,verbs=create;update,versions=v1alpha1,name=mservicedefaults.kb.io | ||
|
||
var _ webhook.Defaulter = &ServiceDefaults{} | ||
|
||
// Default implements webhook.Defaulter so a webhook will be registered for the type | ||
func (r *ServiceDefaults) Default() { | ||
servicedefaultslog.Info("default", "name", r.Name) | ||
|
||
// TODO(user): fill in your defaulting logic. | ||
} | ||
|
||
// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. | ||
// +kubebuilder:webhook:verbs=create;update,path=/validate-consul-hashicorp-com-v1alpha1-servicedefaults,mutating=false,failurePolicy=fail,groups=consul.hashicorp.com,resources=servicedefaults,versions=v1alpha1,name=vservicedefaults.kb.io | ||
|
||
var _ webhook.Validator = &ServiceDefaults{} | ||
|
||
// ValidateCreate implements webhook.Validator so a webhook will be registered for the type | ||
func (r *ServiceDefaults) ValidateCreate() error { | ||
servicedefaultslog.Info("validate create", "name", r.Name) | ||
var svcDefaultsList ServiceDefaultsList | ||
if err := KubeClient.List(context.Background(), &svcDefaultsList); err != nil { | ||
return err | ||
} | ||
for _, item := range svcDefaultsList.Items { | ||
if item.Name == r.Name { | ||
return fmt.Errorf("ServiceDefaults resource with name %q is already defined in namespace %q – all ServiceDefaults resources must have unique names across namespaces", | ||
r.Name, item.Namespace) | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type | ||
func (r *ServiceDefaults) ValidateUpdate(old runtime.Object) error { | ||
servicedefaultslog.Info("validate update", "name", r.Name) | ||
|
||
// TODO(user): fill in your validation logic upon object update. | ||
return nil | ||
} | ||
|
||
// ValidateDelete implements webhook.Validator so a webhook will be registered for the type | ||
func (r *ServiceDefaults) ValidateDelete() error { | ||
servicedefaultslog.Info("validate delete", "name", r.Name) | ||
|
||
// TODO(user): fill in your validation logic upon object deletion. | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package v1alpha1 | ||
|
||
import ( | ||
corev1 "k8s.io/api/core/v1" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
) | ||
|
||
// Conditions is the schema for the conditions portion of the payload | ||
type Conditions []Condition | ||
|
||
// ConditionType is a camel-cased condition type. | ||
type ConditionType string | ||
|
||
const ( | ||
// ConditionSynced specifies that the resource has been synced with Consul. | ||
ConditionSynced ConditionType = "Synced" | ||
) | ||
|
||
// Conditions define a readiness condition for a Consul resource. | ||
// See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties | ||
// +k8s:deepcopy-gen=true | ||
// +k8s:openapi-gen=true | ||
type Condition struct { | ||
// Type of condition. | ||
// +required | ||
Type ConditionType `json:"type" description:"type of status condition"` | ||
|
||
// Status of the condition, one of True, False, Unknown. | ||
// +required | ||
Status corev1.ConditionStatus `json:"status" description:"status of the condition, one of True, False, Unknown"` | ||
|
||
// LastTransitionTime is the last time the condition transitioned from one status to another. | ||
// +optional | ||
LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty" description:"last time the condition transitioned from one status to another"` | ||
|
||
// The reason for the condition's last transition. | ||
// +optional | ||
Reason string `json:"reason,omitempty" description:"one-word CamelCase reason for the condition's last transition"` | ||
|
||
// A human readable message indicating details about the transition. | ||
// +optional | ||
Message string `json:"message,omitempty" description:"human-readable message indicating details about last transition"` | ||
} | ||
|
||
// IsTrue is true if the condition is True | ||
func (c *Condition) IsTrue() bool { | ||
if c == nil { | ||
return false | ||
} | ||
return c.Status == corev1.ConditionTrue | ||
} | ||
|
||
// IsFalse is true if the condition is False | ||
func (c *Condition) IsFalse() bool { | ||
if c == nil { | ||
return false | ||
} | ||
return c.Status == corev1.ConditionFalse | ||
} | ||
|
||
// IsUnknown is true if the condition is Unknown | ||
func (c *Condition) IsUnknown() bool { | ||
if c == nil { | ||
return true | ||
} | ||
return c.Status == corev1.ConditionUnknown | ||
} | ||
|
||
// Status shows how we expect folks to embed Conditions in | ||
// their Status field. | ||
// WARNING: Adding fields to this struct will add them to all Consul-k8s resources. | ||
// +k8s:deepcopy-gen=true | ||
// +k8s:openapi-gen=true | ||
type Status struct { | ||
// Conditions indicate the latest available observations of a resource's current state. | ||
// +optional | ||
// +patchMergeKey=type | ||
// +patchStrategy=merge | ||
Conditions Conditions `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` | ||
} | ||
|
||
func (s *Status) GetCondition(t ConditionType) *Condition { | ||
for _, cond := range s.Conditions { | ||
if cond.Type == t { | ||
return &cond | ||
} | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package v1alpha1 | ||
|
||
type MeshGatewayMode string | ||
|
||
const ( | ||
// MeshGatewayModeDefault represents no specific mode and should | ||
// be used to indicate that a different layer of the configuration | ||
// chain should take precedence | ||
MeshGatewayModeDefault MeshGatewayMode = "" | ||
|
||
// MeshGatewayModeNone represents that the Upstream Connect connections | ||
// should be direct and not flow through a mesh gateway. | ||
MeshGatewayModeNone MeshGatewayMode = "none" | ||
|
||
// MeshGatewayModeLocal represents that the Upstrea Connect connections | ||
// should be made to a mesh gateway in the local datacenter. This is | ||
MeshGatewayModeLocal MeshGatewayMode = "local" | ||
|
||
// MeshGatewayModeRemote represents that the Upstream Connect connections | ||
// should be made to a mesh gateway in a remote datacenter. | ||
MeshGatewayModeRemote MeshGatewayMode = "remote" | ||
) | ||
|
||
// MeshGatewayConfig controls how Mesh Gateways are used for upstream Connect | ||
// services | ||
type MeshGatewayConfig struct { | ||
// Mode is the mode that should be used for the upstream connection. | ||
Mode string `json:"mode,omitempty"` | ||
} | ||
|
||
// ExposeConfig describes HTTP paths to expose through Envoy outside of Connect. | ||
// Users can expose individual paths and/or all HTTP/GRPC paths for checks. | ||
type ExposeConfig struct { | ||
// Checks defines whether paths associated with Consul checks will be exposed. | ||
// This flag triggers exposing all HTTP and GRPC check paths registered for the service. | ||
Checks bool `json:"checks,omitempty"` | ||
|
||
// Paths is the list of paths exposed through the proxy. | ||
Paths []ExposePath `json:"paths,omitempty"` | ||
} | ||
|
||
type ExposePath struct { | ||
// ListenerPort defines the port of the proxy's listener for exposed paths. | ||
ListenerPort int `json:"listenerPort,omitempty"` | ||
|
||
// Path is the path to expose through the proxy, ie. "/metrics." | ||
Path string `json:"path,omitempty"` | ||
|
||
// LocalPathPort is the port that the service is listening on for the given path. | ||
LocalPathPort int `json:"localPathPort,omitempty"` | ||
|
||
// Protocol describes the upstream's service protocol. | ||
// Valid values are "http" and "http2", defaults to "http" | ||
Protocol string `json:"protocol,omitempty"` | ||
|
||
// ParsedFromCheck is set if this path was parsed from a registered check | ||
ParsedFromCheck bool `json:"parsedFromCheck,omitempty"` | ||
} |
Oops, something went wrong.