From 895866a59e5c69041e462869c003f06b21199beb Mon Sep 17 00:00:00 2001 From: awgreene Date: Mon, 9 Dec 2019 14:51:27 -0500 Subject: [PATCH 1/4] Update CRD go types This commit introduces a series of changes which will enable OLM to implement Strucutral Schemas for its CRDs. --- .../operators/clusterserviceversion_types.go | 44 ++- pkg/api/apis/operators/installplan_types.go | 12 +- pkg/api/apis/operators/operatorgroup_types.go | 2 +- .../apis/operators/v1/operatorgroup_types.go | 3 +- .../operators/v1/zz_generated.conversion.go | 4 +- .../operators/v1/zz_generated.deepcopy.go | 5 +- .../operators/v1alpha1/catalogsource_types.go | 3 +- .../v1alpha1/clusterserviceversion.go | 6 +- .../v1alpha1/clusterserviceversion_test.go | 13 +- .../v1alpha1/clusterserviceversion_types.go | 46 ++- .../operators/v1alpha1/installplan_types.go | 15 +- .../operators/v1alpha1/subscription_types.go | 5 +- .../v1alpha1/zz_generated.conversion.go | 139 +++++++-- .../v1alpha1/zz_generated.deepcopy.go | 124 +++++++- .../apis/operators/zz_generated.deepcopy.go | 129 ++++++++- pkg/controller/install/deployment.go | 43 +-- pkg/controller/install/deployment_test.go | 26 +- pkg/controller/install/resolver.go | 11 +- pkg/controller/operators/olm/apiservices.go | 17 +- pkg/controller/operators/olm/operator.go | 9 +- pkg/controller/operators/olm/operator_test.go | 265 +++--------------- pkg/controller/operators/olm/operatorgroup.go | 12 +- pkg/controller/operators/olm/requirements.go | 8 +- .../operators/olm/requirements_test.go | 33 +-- pkg/controller/registry/resolver/rbac.go | 2 +- .../registry/resolver/resolver_test.go | 3 +- pkg/controller/registry/resolver/util_test.go | 17 +- pkg/lib/scoped/syncer.go | 3 +- pkg/lib/version/version.go | 3 +- test/e2e/csv_e2e_test.go | 194 +++++++------ test/e2e/installplan_e2e_test.go | 46 ++- test/e2e/metrics_e2e_test.go | 5 +- test/e2e/operator_groups_e2e_test.go | 15 +- test/e2e/subscription_e2e_test.go | 20 +- test/e2e/user_defined_sa_test.go | 10 +- 35 files changed, 689 insertions(+), 603 deletions(-) diff --git a/pkg/api/apis/operators/clusterserviceversion_types.go b/pkg/api/apis/operators/clusterserviceversion_types.go index 94398e72e6..63298b395b 100644 --- a/pkg/api/apis/operators/clusterserviceversion_types.go +++ b/pkg/api/apis/operators/clusterserviceversion_types.go @@ -5,6 +5,8 @@ import ( "fmt" "sort" + appsv1 "k8s.io/api/apps/v1" + rbac "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/version" @@ -25,6 +27,8 @@ const ( InstallModeTypeMultiNamespace InstallModeType = "MultiNamespace" // InstallModeTypeAllNamespaces indicates that the operator can be a member of an `OperatorGroup` that selects all namespaces (target namespace set is the empty string ""). InstallModeTypeAllNamespaces InstallModeType = "AllNamespaces" + + InstallStrategyNameDeployment = "deployment" ) // InstallMode associates an InstallModeType with a flag representing if the CSV supports it @@ -39,8 +43,32 @@ type InstallModeSet map[InstallModeType]bool // NamedInstallStrategy represents the block of an ClusterServiceVersion resource // where the install strategy is specified. type NamedInstallStrategy struct { - StrategyName string - StrategySpecRaw json.RawMessage + StrategyName string + StrategySpec StrategyDetailsDeployment +} + +// StrategyDeploymentPermissions describe the rbac rules and service account needed by the install strategy +type StrategyDeploymentPermissions struct { + ServiceAccountName string + Rules []rbac.PolicyRule +} + +// StrategyDeploymentSpec contains the name and spec for the deployment ALM should create +type StrategyDeploymentSpec struct { + Name string + Spec appsv1.DeploymentSpec +} + +// StrategyDetailsDeployment represents the parsed details of a Deployment +// InstallStrategy. +type StrategyDetailsDeployment struct { + DeploymentSpecs []StrategyDeploymentSpec + Permissions []StrategyDeploymentPermissions + ClusterPermissions []StrategyDeploymentPermissions +} + +func (d *StrategyDetailsDeployment) GetStrategyName() string { + return InstallStrategyNameDeployment } // StatusDescriptor describes a field in a status block of a CRD so that OLM can consume it @@ -254,10 +282,10 @@ type ClusterServiceVersionCondition struct { Reason ConditionReason // Last time we updated the status // +optional - LastUpdateTime metav1.Time + LastUpdateTime *metav1.Time // Last time the status transitioned from one status to another. // +optional - LastTransitionTime metav1.Time + LastTransitionTime *metav1.Time } // OwnsCRD determines whether the current CSV owns a paritcular CRD. @@ -331,20 +359,20 @@ type ClusterServiceVersionStatus struct { Reason ConditionReason // Last time we updated the status // +optional - LastUpdateTime metav1.Time + LastUpdateTime *metav1.Time // Last time the status transitioned from one status to another. // +optional - LastTransitionTime metav1.Time + LastTransitionTime *metav1.Time // List of conditions, a history of state transitions Conditions []ClusterServiceVersionCondition // The status of each requirement for this CSV RequirementStatus []RequirementStatus // Last time the owned APIService certs were updated // +optional - CertsLastUpdated metav1.Time + CertsLastUpdated *metav1.Time // Time the owned APIService certs will rotate next // +optional - CertsRotateAt metav1.Time + CertsRotateAt *metav1.Time } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/api/apis/operators/installplan_types.go b/pkg/api/apis/operators/installplan_types.go index 447fd90a0f..33ba3deb18 100644 --- a/pkg/api/apis/operators/installplan_types.go +++ b/pkg/api/apis/operators/installplan_types.go @@ -90,8 +90,8 @@ type InstallPlanStatus struct { type InstallPlanCondition struct { Type InstallPlanConditionType Status corev1.ConditionStatus // True, False, or Unknown - LastUpdateTime metav1.Time - LastTransitionTime metav1.Time + LastUpdateTime *metav1.Time + LastTransitionTime *metav1.Time Reason InstallPlanConditionReason Message string } @@ -136,8 +136,8 @@ func ConditionFailed(cond InstallPlanConditionType, reason InstallPlanConditionR Status: corev1.ConditionFalse, Reason: reason, Message: message, - LastUpdateTime: *now, - LastTransitionTime: *now, + LastUpdateTime: now, + LastTransitionTime: now, } } @@ -145,8 +145,8 @@ func ConditionMet(cond InstallPlanConditionType, now *metav1.Time) InstallPlanCo return InstallPlanCondition{ Type: cond, Status: corev1.ConditionTrue, - LastUpdateTime: *now, - LastTransitionTime: *now, + LastUpdateTime: now, + LastTransitionTime: now, } } diff --git a/pkg/api/apis/operators/operatorgroup_types.go b/pkg/api/apis/operators/operatorgroup_types.go index 9f1b913ec4..e94386c778 100644 --- a/pkg/api/apis/operators/operatorgroup_types.go +++ b/pkg/api/apis/operators/operatorgroup_types.go @@ -47,7 +47,7 @@ type OperatorGroupStatus struct { ServiceAccountRef *corev1.ObjectReference // LastUpdated is a timestamp of the last time the OperatorGroup's status was Updated. - LastUpdated metav1.Time + LastUpdated *metav1.Time } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/api/apis/operators/v1/operatorgroup_types.go b/pkg/api/apis/operators/v1/operatorgroup_types.go index 36e6c7b7f0..f36b04804d 100644 --- a/pkg/api/apis/operators/v1/operatorgroup_types.go +++ b/pkg/api/apis/operators/v1/operatorgroup_types.go @@ -46,7 +46,7 @@ type OperatorGroupStatus struct { ServiceAccountRef *corev1.ObjectReference `json:"serviceAccountRef,omitempty"` // LastUpdated is a timestamp of the last time the OperatorGroup's status was Updated. - LastUpdated metav1.Time `json:"lastUpdated"` + LastUpdated *metav1.Time `json:"lastUpdated"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -58,6 +58,7 @@ type OperatorGroup struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata"` + // +optional Spec OperatorGroupSpec `json:"spec"` Status OperatorGroupStatus `json:"status,omitempty"` } diff --git a/pkg/api/apis/operators/v1/zz_generated.conversion.go b/pkg/api/apis/operators/v1/zz_generated.conversion.go index 02e4a3ed5a..14ae6dacc7 100644 --- a/pkg/api/apis/operators/v1/zz_generated.conversion.go +++ b/pkg/api/apis/operators/v1/zz_generated.conversion.go @@ -163,7 +163,7 @@ func Convert_operators_OperatorGroupSpec_To_v1_OperatorGroupSpec(in *operators.O func autoConvert_v1_OperatorGroupStatus_To_operators_OperatorGroupStatus(in *OperatorGroupStatus, out *operators.OperatorGroupStatus, s conversion.Scope) error { out.Namespaces = *(*[]string)(unsafe.Pointer(&in.Namespaces)) out.ServiceAccountRef = (*corev1.ObjectReference)(unsafe.Pointer(in.ServiceAccountRef)) - out.LastUpdated = in.LastUpdated + out.LastUpdated = (*metav1.Time)(unsafe.Pointer(in.LastUpdated)) return nil } @@ -175,7 +175,7 @@ func Convert_v1_OperatorGroupStatus_To_operators_OperatorGroupStatus(in *Operato func autoConvert_operators_OperatorGroupStatus_To_v1_OperatorGroupStatus(in *operators.OperatorGroupStatus, out *OperatorGroupStatus, s conversion.Scope) error { out.Namespaces = *(*[]string)(unsafe.Pointer(&in.Namespaces)) out.ServiceAccountRef = (*corev1.ObjectReference)(unsafe.Pointer(in.ServiceAccountRef)) - out.LastUpdated = in.LastUpdated + out.LastUpdated = (*metav1.Time)(unsafe.Pointer(in.LastUpdated)) return nil } diff --git a/pkg/api/apis/operators/v1/zz_generated.deepcopy.go b/pkg/api/apis/operators/v1/zz_generated.deepcopy.go index 22a114d623..b31dde0978 100644 --- a/pkg/api/apis/operators/v1/zz_generated.deepcopy.go +++ b/pkg/api/apis/operators/v1/zz_generated.deepcopy.go @@ -126,7 +126,10 @@ func (in *OperatorGroupStatus) DeepCopyInto(out *OperatorGroupStatus) { *out = new(corev1.ObjectReference) **out = **in } - in.LastUpdated.DeepCopyInto(&out.LastUpdated) + if in.LastUpdated != nil { + in, out := &in.LastUpdated, &out.LastUpdated + *out = (*in).DeepCopy() + } return } diff --git a/pkg/api/apis/operators/v1alpha1/catalogsource_types.go b/pkg/api/apis/operators/v1alpha1/catalogsource_types.go index 67551fdaf6..bb1b86afeb 100644 --- a/pkg/api/apis/operators/v1alpha1/catalogsource_types.go +++ b/pkg/api/apis/operators/v1alpha1/catalogsource_types.go @@ -117,7 +117,8 @@ type CatalogSource struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata"` - Spec CatalogSourceSpec `json:"spec"` + Spec CatalogSourceSpec `json:"spec"` + // +optional Status CatalogSourceStatus `json:"status"` } diff --git a/pkg/api/apis/operators/v1alpha1/clusterserviceversion.go b/pkg/api/apis/operators/v1alpha1/clusterserviceversion.go index 9dd6f56f8d..ffc357b12b 100644 --- a/pkg/api/apis/operators/v1alpha1/clusterserviceversion.go +++ b/pkg/api/apis/operators/v1alpha1/clusterserviceversion.go @@ -50,7 +50,7 @@ var safeToAnnotateOperatorGroupReasons = map[ConditionReason]struct{}{ } // SetPhaseWithEventIfChanged emits a Kubernetes event with details of a phase change and sets the current phase if phase, reason, or message would changed -func (c *ClusterServiceVersion) SetPhaseWithEventIfChanged(phase ClusterServiceVersionPhase, reason ConditionReason, message string, now metav1.Time, recorder record.EventRecorder) { +func (c *ClusterServiceVersion) SetPhaseWithEventIfChanged(phase ClusterServiceVersionPhase, reason ConditionReason, message string, now *metav1.Time, recorder record.EventRecorder) { if c.Status.Phase == phase && c.Status.Reason == reason && c.Status.Message == message { return } @@ -59,7 +59,7 @@ func (c *ClusterServiceVersion) SetPhaseWithEventIfChanged(phase ClusterServiceV } // SetPhaseWithEvent generates a Kubernetes event with details about the phase change and sets the current phase -func (c *ClusterServiceVersion) SetPhaseWithEvent(phase ClusterServiceVersionPhase, reason ConditionReason, message string, now metav1.Time, recorder record.EventRecorder) { +func (c *ClusterServiceVersion) SetPhaseWithEvent(phase ClusterServiceVersionPhase, reason ConditionReason, message string, now *metav1.Time, recorder record.EventRecorder) { var eventtype string if phase == CSVPhaseFailed { eventtype = v1.EventTypeWarning @@ -71,7 +71,7 @@ func (c *ClusterServiceVersion) SetPhaseWithEvent(phase ClusterServiceVersionPha } // SetPhase sets the current phase and adds a condition if necessary -func (c *ClusterServiceVersion) SetPhase(phase ClusterServiceVersionPhase, reason ConditionReason, message string, now metav1.Time) { +func (c *ClusterServiceVersion) SetPhase(phase ClusterServiceVersionPhase, reason ConditionReason, message string, now *metav1.Time) { newCondition := func() ClusterServiceVersionCondition { return ClusterServiceVersionCondition{ Phase: c.Status.Phase, diff --git a/pkg/api/apis/operators/v1alpha1/clusterserviceversion_test.go b/pkg/api/apis/operators/v1alpha1/clusterserviceversion_test.go index 3561a6d06c..1a2ea05139 100644 --- a/pkg/api/apis/operators/v1alpha1/clusterserviceversion_test.go +++ b/pkg/api/apis/operators/v1alpha1/clusterserviceversion_test.go @@ -56,7 +56,8 @@ func TestSetPhase(t *testing.T) { Conditions: tt.currentConditions, }, } - csv.SetPhase(tt.inPhase, "test", "test", metav1.Now()) + now := metav1.Now() + csv.SetPhase(tt.inPhase, "test", "test", &now) require.EqualValues(t, tt.outPhase, csv.Status.Phase) }) } @@ -337,13 +338,13 @@ func TestSetPhaseWithConditions(t *testing.T) { oldConditionsWant := csv.Status.Conditions[tt.startIndex:] lastAddedConditionWant := ClusterServiceVersionCondition{ Phase: ClusterServiceVersionPhase("Pending"), - LastTransitionTime: now, - LastUpdateTime: now, + LastTransitionTime: &now, + LastUpdateTime: &now, Message: "message", Reason: ConditionReason("reason"), } - csv.SetPhase("Pending", "reason", "message", now) + csv.SetPhase("Pending", "reason", "message", &now) conditionsGot := csv.Status.Conditions assert.Equal(t, tt.limit, len(conditionsGot)) @@ -364,8 +365,8 @@ func helperNewConditions(count int) []ClusterServiceVersionCondition { now := metav1.Now() condition := ClusterServiceVersionCondition{ Phase: ClusterServiceVersionPhase(fmt.Sprintf("phase-%d", i)), - LastTransitionTime: now, - LastUpdateTime: now, + LastTransitionTime: &now, + LastUpdateTime: &now, Message: fmt.Sprintf("message-%d", i), Reason: ConditionReason(fmt.Sprintf("reason-%d", i)), } diff --git a/pkg/api/apis/operators/v1alpha1/clusterserviceversion_types.go b/pkg/api/apis/operators/v1alpha1/clusterserviceversion_types.go index 03638d4060..43f28a899f 100644 --- a/pkg/api/apis/operators/v1alpha1/clusterserviceversion_types.go +++ b/pkg/api/apis/operators/v1alpha1/clusterserviceversion_types.go @@ -5,6 +5,8 @@ import ( "fmt" "sort" + appsv1 "k8s.io/api/apps/v1" + rbac "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/version" @@ -14,6 +16,7 @@ const ( ClusterServiceVersionAPIVersion = GroupName + "/" + GroupVersion ClusterServiceVersionKind = "ClusterServiceVersion" OperatorGroupNamespaceAnnotationKey = "olm.operatorNamespace" + InstallStrategyNameDeployment = "deployment" ) // InstallModeType is a supported type of install mode for CSV installation @@ -43,8 +46,32 @@ type InstallModeSet map[InstallModeType]bool // NamedInstallStrategy represents the block of an ClusterServiceVersion resource // where the install strategy is specified. type NamedInstallStrategy struct { - StrategyName string `json:"strategy"` - StrategySpecRaw json.RawMessage `json:"spec,omitempty"` + StrategyName string `json:"strategy"` + StrategySpec StrategyDetailsDeployment `json:"spec,omitempty"` +} + +// StrategyDeploymentPermissions describe the rbac rules and service account needed by the install strategy +type StrategyDeploymentPermissions struct { + ServiceAccountName string `json:"serviceAccountName"` + Rules []rbac.PolicyRule `json:"rules"` +} + +// StrategyDeploymentSpec contains the name and spec for the deployment ALM should create +type StrategyDeploymentSpec struct { + Name string `json:"name"` + Spec appsv1.DeploymentSpec `json:"spec"` +} + +// StrategyDetailsDeployment represents the parsed details of a Deployment +// InstallStrategy. +type StrategyDetailsDeployment struct { + DeploymentSpecs []StrategyDeploymentSpec `json:"deployments"` + Permissions []StrategyDeploymentPermissions `json:"permissions,omitempty"` + ClusterPermissions []StrategyDeploymentPermissions `json:"clusterPermissions,omitempty"` +} + +func (d *StrategyDetailsDeployment) GetStrategyName() string { + return InstallStrategyNameDeployment } // StatusDescriptor describes a field in a status block of a CRD so that OLM can consume it @@ -268,10 +295,10 @@ type ClusterServiceVersionCondition struct { Reason ConditionReason `json:"reason,omitempty"` // Last time we updated the status // +optional - LastUpdateTime metav1.Time `json:"lastUpdateTime,omitempty"` + LastUpdateTime *metav1.Time `json:"lastUpdateTime,omitempty"` // Last time the status transitioned from one status to another. // +optional - LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"` + LastTransitionTime *metav1.Time `json:"lastTransitionTime,omitempty"` } // OwnsCRD determines whether the current CSV owns a particular CRD. @@ -345,20 +372,20 @@ type ClusterServiceVersionStatus struct { Reason ConditionReason `json:"reason,omitempty"` // Last time we updated the status // +optional - LastUpdateTime metav1.Time `json:"lastUpdateTime,omitempty"` + LastUpdateTime *metav1.Time `json:"lastUpdateTime,omitempty"` // Last time the status transitioned from one status to another. // +optional - LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"` + LastTransitionTime *metav1.Time `json:"lastTransitionTime,omitempty"` // List of conditions, a history of state transitions Conditions []ClusterServiceVersionCondition `json:"conditions,omitempty"` // The status of each requirement for this CSV RequirementStatus []RequirementStatus `json:"requirementStatus,omitempty"` // Last time the owned APIService certs were updated // +optional - CertsLastUpdated metav1.Time `json:"certsLastUpdated,omitempty"` + CertsLastUpdated *metav1.Time `json:"certsLastUpdated,omitempty"` // Time the owned APIService certs will rotate next // +optional - CertsRotateAt metav1.Time `json:"certsRotateAt,omitempty"` + CertsRotateAt *metav1.Time `json:"certsRotateAt,omitempty"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -369,7 +396,8 @@ type ClusterServiceVersion struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata"` - Spec ClusterServiceVersionSpec `json:"spec"` + Spec ClusterServiceVersionSpec `json:"spec"` + // +optional Status ClusterServiceVersionStatus `json:"status"` } diff --git a/pkg/api/apis/operators/v1alpha1/installplan_types.go b/pkg/api/apis/operators/v1alpha1/installplan_types.go index c4800aa0bb..0950453a18 100644 --- a/pkg/api/apis/operators/v1alpha1/installplan_types.go +++ b/pkg/api/apis/operators/v1alpha1/installplan_types.go @@ -95,8 +95,8 @@ type InstallPlanStatus struct { type InstallPlanCondition struct { Type InstallPlanConditionType `json:"type,omitempty"` Status corev1.ConditionStatus `json:"status,omitempty"` // True, False, or Unknown - LastUpdateTime metav1.Time `json:"lastUpdateTime,omitempty"` - LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"` + LastUpdateTime *metav1.Time `json:"lastUpdateTime,omitempty"` + LastTransitionTime *metav1.Time `json:"lastTransitionTime,omitempty"` Reason InstallPlanConditionReason `json:"reason,omitempty"` Message string `json:"message,omitempty"` } @@ -141,8 +141,8 @@ func ConditionFailed(cond InstallPlanConditionType, reason InstallPlanConditionR Status: corev1.ConditionFalse, Reason: reason, Message: message, - LastUpdateTime: *now, - LastTransitionTime: *now, + LastUpdateTime: now, + LastTransitionTime: now, } } @@ -150,8 +150,8 @@ func ConditionMet(cond InstallPlanConditionType, now *metav1.Time) InstallPlanCo return InstallPlanCondition{ Type: cond, Status: corev1.ConditionTrue, - LastUpdateTime: *now, - LastTransitionTime: *now, + LastUpdateTime: now, + LastTransitionTime: now, } } @@ -227,7 +227,8 @@ type InstallPlan struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata"` - Spec InstallPlanSpec `json:"spec"` + Spec InstallPlanSpec `json:"spec"` + // +optional Status InstallPlanStatus `json:"status"` } diff --git a/pkg/api/apis/operators/v1alpha1/subscription_types.go b/pkg/api/apis/operators/v1alpha1/subscription_types.go index 8b4d95a2e3..9e997bb611 100644 --- a/pkg/api/apis/operators/v1alpha1/subscription_types.go +++ b/pkg/api/apis/operators/v1alpha1/subscription_types.go @@ -76,7 +76,7 @@ type SubscriptionConfig struct { // +patchStrategy=merge // +optional Env []corev1.EnvVar `json:"env,omitempty" patchMergeKey:"name" patchStrategy:"merge"` - + // List of Volumes to set in the podSpec. // +optional Volumes []corev1.Volume `json:"volumes,omitempty"` @@ -282,7 +282,8 @@ type Subscription struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata"` - Spec *SubscriptionSpec `json:"spec"` + Spec *SubscriptionSpec `json:"spec"` + // +optional Status SubscriptionStatus `json:"status"` } diff --git a/pkg/api/apis/operators/v1alpha1/zz_generated.conversion.go b/pkg/api/apis/operators/v1alpha1/zz_generated.conversion.go index 609276648d..2337025d0d 100644 --- a/pkg/api/apis/operators/v1alpha1/zz_generated.conversion.go +++ b/pkg/api/apis/operators/v1alpha1/zz_generated.conversion.go @@ -26,6 +26,7 @@ import ( operators "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators" corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" conversion "k8s.io/apimachinery/pkg/conversion" runtime "k8s.io/apimachinery/pkg/runtime" @@ -389,6 +390,36 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*StrategyDeploymentPermissions)(nil), (*operators.StrategyDeploymentPermissions)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_StrategyDeploymentPermissions_To_operators_StrategyDeploymentPermissions(a.(*StrategyDeploymentPermissions), b.(*operators.StrategyDeploymentPermissions), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*operators.StrategyDeploymentPermissions)(nil), (*StrategyDeploymentPermissions)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_operators_StrategyDeploymentPermissions_To_v1alpha1_StrategyDeploymentPermissions(a.(*operators.StrategyDeploymentPermissions), b.(*StrategyDeploymentPermissions), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*StrategyDeploymentSpec)(nil), (*operators.StrategyDeploymentSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_StrategyDeploymentSpec_To_operators_StrategyDeploymentSpec(a.(*StrategyDeploymentSpec), b.(*operators.StrategyDeploymentSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*operators.StrategyDeploymentSpec)(nil), (*StrategyDeploymentSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_operators_StrategyDeploymentSpec_To_v1alpha1_StrategyDeploymentSpec(a.(*operators.StrategyDeploymentSpec), b.(*StrategyDeploymentSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*StrategyDetailsDeployment)(nil), (*operators.StrategyDetailsDeployment)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_StrategyDetailsDeployment_To_operators_StrategyDetailsDeployment(a.(*StrategyDetailsDeployment), b.(*operators.StrategyDetailsDeployment), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*operators.StrategyDetailsDeployment)(nil), (*StrategyDetailsDeployment)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_operators_StrategyDetailsDeployment_To_v1alpha1_StrategyDetailsDeployment(a.(*operators.StrategyDetailsDeployment), b.(*StrategyDetailsDeployment), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*Subscription)(nil), (*operators.Subscription)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha1_Subscription_To_operators_Subscription(a.(*Subscription), b.(*operators.Subscription), scope) }); err != nil { @@ -794,8 +825,8 @@ func autoConvert_v1alpha1_ClusterServiceVersionCondition_To_operators_ClusterSer out.Phase = operators.ClusterServiceVersionPhase(in.Phase) out.Message = in.Message out.Reason = operators.ConditionReason(in.Reason) - out.LastUpdateTime = in.LastUpdateTime - out.LastTransitionTime = in.LastTransitionTime + out.LastUpdateTime = (*v1.Time)(unsafe.Pointer(in.LastUpdateTime)) + out.LastTransitionTime = (*v1.Time)(unsafe.Pointer(in.LastTransitionTime)) return nil } @@ -808,8 +839,8 @@ func autoConvert_operators_ClusterServiceVersionCondition_To_v1alpha1_ClusterSer out.Phase = ClusterServiceVersionPhase(in.Phase) out.Message = in.Message out.Reason = ConditionReason(in.Reason) - out.LastUpdateTime = in.LastUpdateTime - out.LastTransitionTime = in.LastTransitionTime + out.LastUpdateTime = (*v1.Time)(unsafe.Pointer(in.LastUpdateTime)) + out.LastTransitionTime = (*v1.Time)(unsafe.Pointer(in.LastTransitionTime)) return nil } @@ -916,12 +947,12 @@ func autoConvert_v1alpha1_ClusterServiceVersionStatus_To_operators_ClusterServic out.Phase = operators.ClusterServiceVersionPhase(in.Phase) out.Message = in.Message out.Reason = operators.ConditionReason(in.Reason) - out.LastUpdateTime = in.LastUpdateTime - out.LastTransitionTime = in.LastTransitionTime + out.LastUpdateTime = (*v1.Time)(unsafe.Pointer(in.LastUpdateTime)) + out.LastTransitionTime = (*v1.Time)(unsafe.Pointer(in.LastTransitionTime)) out.Conditions = *(*[]operators.ClusterServiceVersionCondition)(unsafe.Pointer(&in.Conditions)) out.RequirementStatus = *(*[]operators.RequirementStatus)(unsafe.Pointer(&in.RequirementStatus)) - out.CertsLastUpdated = in.CertsLastUpdated - out.CertsRotateAt = in.CertsRotateAt + out.CertsLastUpdated = (*v1.Time)(unsafe.Pointer(in.CertsLastUpdated)) + out.CertsRotateAt = (*v1.Time)(unsafe.Pointer(in.CertsRotateAt)) return nil } @@ -934,12 +965,12 @@ func autoConvert_operators_ClusterServiceVersionStatus_To_v1alpha1_ClusterServic out.Phase = ClusterServiceVersionPhase(in.Phase) out.Message = in.Message out.Reason = ConditionReason(in.Reason) - out.LastUpdateTime = in.LastUpdateTime - out.LastTransitionTime = in.LastTransitionTime + out.LastUpdateTime = (*v1.Time)(unsafe.Pointer(in.LastUpdateTime)) + out.LastTransitionTime = (*v1.Time)(unsafe.Pointer(in.LastTransitionTime)) out.Conditions = *(*[]ClusterServiceVersionCondition)(unsafe.Pointer(&in.Conditions)) out.RequirementStatus = *(*[]RequirementStatus)(unsafe.Pointer(&in.RequirementStatus)) - out.CertsLastUpdated = in.CertsLastUpdated - out.CertsRotateAt = in.CertsRotateAt + out.CertsLastUpdated = (*v1.Time)(unsafe.Pointer(in.CertsLastUpdated)) + out.CertsRotateAt = (*v1.Time)(unsafe.Pointer(in.CertsRotateAt)) return nil } @@ -1131,8 +1162,8 @@ func Convert_operators_InstallPlan_To_v1alpha1_InstallPlan(in *operators.Install func autoConvert_v1alpha1_InstallPlanCondition_To_operators_InstallPlanCondition(in *InstallPlanCondition, out *operators.InstallPlanCondition, s conversion.Scope) error { out.Type = operators.InstallPlanConditionType(in.Type) out.Status = corev1.ConditionStatus(in.Status) - out.LastUpdateTime = in.LastUpdateTime - out.LastTransitionTime = in.LastTransitionTime + out.LastUpdateTime = (*v1.Time)(unsafe.Pointer(in.LastUpdateTime)) + out.LastTransitionTime = (*v1.Time)(unsafe.Pointer(in.LastTransitionTime)) out.Reason = operators.InstallPlanConditionReason(in.Reason) out.Message = in.Message return nil @@ -1146,8 +1177,8 @@ func Convert_v1alpha1_InstallPlanCondition_To_operators_InstallPlanCondition(in func autoConvert_operators_InstallPlanCondition_To_v1alpha1_InstallPlanCondition(in *operators.InstallPlanCondition, out *InstallPlanCondition, s conversion.Scope) error { out.Type = InstallPlanConditionType(in.Type) out.Status = corev1.ConditionStatus(in.Status) - out.LastUpdateTime = in.LastUpdateTime - out.LastTransitionTime = in.LastTransitionTime + out.LastUpdateTime = (*v1.Time)(unsafe.Pointer(in.LastUpdateTime)) + out.LastTransitionTime = (*v1.Time)(unsafe.Pointer(in.LastTransitionTime)) out.Reason = InstallPlanConditionReason(in.Reason) out.Message = in.Message return nil @@ -1286,7 +1317,9 @@ func Convert_operators_Maintainer_To_v1alpha1_Maintainer(in *operators.Maintaine func autoConvert_v1alpha1_NamedInstallStrategy_To_operators_NamedInstallStrategy(in *NamedInstallStrategy, out *operators.NamedInstallStrategy, s conversion.Scope) error { out.StrategyName = in.StrategyName - out.StrategySpecRaw = *(*json.RawMessage)(unsafe.Pointer(&in.StrategySpecRaw)) + if err := Convert_v1alpha1_StrategyDetailsDeployment_To_operators_StrategyDetailsDeployment(&in.StrategySpec, &out.StrategySpec, s); err != nil { + return err + } return nil } @@ -1297,7 +1330,9 @@ func Convert_v1alpha1_NamedInstallStrategy_To_operators_NamedInstallStrategy(in func autoConvert_operators_NamedInstallStrategy_To_v1alpha1_NamedInstallStrategy(in *operators.NamedInstallStrategy, out *NamedInstallStrategy, s conversion.Scope) error { out.StrategyName = in.StrategyName - out.StrategySpecRaw = *(*json.RawMessage)(unsafe.Pointer(&in.StrategySpecRaw)) + if err := Convert_operators_StrategyDetailsDeployment_To_v1alpha1_StrategyDetailsDeployment(&in.StrategySpec, &out.StrategySpec, s); err != nil { + return err + } return nil } @@ -1484,6 +1519,74 @@ func Convert_operators_StepResource_To_v1alpha1_StepResource(in *operators.StepR return autoConvert_operators_StepResource_To_v1alpha1_StepResource(in, out, s) } +func autoConvert_v1alpha1_StrategyDeploymentPermissions_To_operators_StrategyDeploymentPermissions(in *StrategyDeploymentPermissions, out *operators.StrategyDeploymentPermissions, s conversion.Scope) error { + out.ServiceAccountName = in.ServiceAccountName + out.Rules = *(*[]rbacv1.PolicyRule)(unsafe.Pointer(&in.Rules)) + return nil +} + +// Convert_v1alpha1_StrategyDeploymentPermissions_To_operators_StrategyDeploymentPermissions is an autogenerated conversion function. +func Convert_v1alpha1_StrategyDeploymentPermissions_To_operators_StrategyDeploymentPermissions(in *StrategyDeploymentPermissions, out *operators.StrategyDeploymentPermissions, s conversion.Scope) error { + return autoConvert_v1alpha1_StrategyDeploymentPermissions_To_operators_StrategyDeploymentPermissions(in, out, s) +} + +func autoConvert_operators_StrategyDeploymentPermissions_To_v1alpha1_StrategyDeploymentPermissions(in *operators.StrategyDeploymentPermissions, out *StrategyDeploymentPermissions, s conversion.Scope) error { + out.ServiceAccountName = in.ServiceAccountName + out.Rules = *(*[]rbacv1.PolicyRule)(unsafe.Pointer(&in.Rules)) + return nil +} + +// Convert_operators_StrategyDeploymentPermissions_To_v1alpha1_StrategyDeploymentPermissions is an autogenerated conversion function. +func Convert_operators_StrategyDeploymentPermissions_To_v1alpha1_StrategyDeploymentPermissions(in *operators.StrategyDeploymentPermissions, out *StrategyDeploymentPermissions, s conversion.Scope) error { + return autoConvert_operators_StrategyDeploymentPermissions_To_v1alpha1_StrategyDeploymentPermissions(in, out, s) +} + +func autoConvert_v1alpha1_StrategyDeploymentSpec_To_operators_StrategyDeploymentSpec(in *StrategyDeploymentSpec, out *operators.StrategyDeploymentSpec, s conversion.Scope) error { + out.Name = in.Name + out.Spec = in.Spec + return nil +} + +// Convert_v1alpha1_StrategyDeploymentSpec_To_operators_StrategyDeploymentSpec is an autogenerated conversion function. +func Convert_v1alpha1_StrategyDeploymentSpec_To_operators_StrategyDeploymentSpec(in *StrategyDeploymentSpec, out *operators.StrategyDeploymentSpec, s conversion.Scope) error { + return autoConvert_v1alpha1_StrategyDeploymentSpec_To_operators_StrategyDeploymentSpec(in, out, s) +} + +func autoConvert_operators_StrategyDeploymentSpec_To_v1alpha1_StrategyDeploymentSpec(in *operators.StrategyDeploymentSpec, out *StrategyDeploymentSpec, s conversion.Scope) error { + out.Name = in.Name + out.Spec = in.Spec + return nil +} + +// Convert_operators_StrategyDeploymentSpec_To_v1alpha1_StrategyDeploymentSpec is an autogenerated conversion function. +func Convert_operators_StrategyDeploymentSpec_To_v1alpha1_StrategyDeploymentSpec(in *operators.StrategyDeploymentSpec, out *StrategyDeploymentSpec, s conversion.Scope) error { + return autoConvert_operators_StrategyDeploymentSpec_To_v1alpha1_StrategyDeploymentSpec(in, out, s) +} + +func autoConvert_v1alpha1_StrategyDetailsDeployment_To_operators_StrategyDetailsDeployment(in *StrategyDetailsDeployment, out *operators.StrategyDetailsDeployment, s conversion.Scope) error { + out.DeploymentSpecs = *(*[]operators.StrategyDeploymentSpec)(unsafe.Pointer(&in.DeploymentSpecs)) + out.Permissions = *(*[]operators.StrategyDeploymentPermissions)(unsafe.Pointer(&in.Permissions)) + out.ClusterPermissions = *(*[]operators.StrategyDeploymentPermissions)(unsafe.Pointer(&in.ClusterPermissions)) + return nil +} + +// Convert_v1alpha1_StrategyDetailsDeployment_To_operators_StrategyDetailsDeployment is an autogenerated conversion function. +func Convert_v1alpha1_StrategyDetailsDeployment_To_operators_StrategyDetailsDeployment(in *StrategyDetailsDeployment, out *operators.StrategyDetailsDeployment, s conversion.Scope) error { + return autoConvert_v1alpha1_StrategyDetailsDeployment_To_operators_StrategyDetailsDeployment(in, out, s) +} + +func autoConvert_operators_StrategyDetailsDeployment_To_v1alpha1_StrategyDetailsDeployment(in *operators.StrategyDetailsDeployment, out *StrategyDetailsDeployment, s conversion.Scope) error { + out.DeploymentSpecs = *(*[]StrategyDeploymentSpec)(unsafe.Pointer(&in.DeploymentSpecs)) + out.Permissions = *(*[]StrategyDeploymentPermissions)(unsafe.Pointer(&in.Permissions)) + out.ClusterPermissions = *(*[]StrategyDeploymentPermissions)(unsafe.Pointer(&in.ClusterPermissions)) + return nil +} + +// Convert_operators_StrategyDetailsDeployment_To_v1alpha1_StrategyDetailsDeployment is an autogenerated conversion function. +func Convert_operators_StrategyDetailsDeployment_To_v1alpha1_StrategyDetailsDeployment(in *operators.StrategyDetailsDeployment, out *StrategyDetailsDeployment, s conversion.Scope) error { + return autoConvert_operators_StrategyDetailsDeployment_To_v1alpha1_StrategyDetailsDeployment(in, out, s) +} + func autoConvert_v1alpha1_Subscription_To_operators_Subscription(in *Subscription, out *operators.Subscription, s conversion.Scope) error { out.ObjectMeta = in.ObjectMeta out.Spec = (*operators.SubscriptionSpec)(unsafe.Pointer(in.Spec)) diff --git a/pkg/api/apis/operators/v1alpha1/zz_generated.deepcopy.go b/pkg/api/apis/operators/v1alpha1/zz_generated.deepcopy.go index effada57c6..0a11b999e4 100644 --- a/pkg/api/apis/operators/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/api/apis/operators/v1alpha1/zz_generated.deepcopy.go @@ -24,6 +24,7 @@ import ( json "encoding/json" corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -349,8 +350,14 @@ func (in *ClusterServiceVersion) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterServiceVersionCondition) DeepCopyInto(out *ClusterServiceVersionCondition) { *out = *in - in.LastUpdateTime.DeepCopyInto(&out.LastUpdateTime) - in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) + if in.LastUpdateTime != nil { + in, out := &in.LastUpdateTime, &out.LastUpdateTime + *out = (*in).DeepCopy() + } + if in.LastTransitionTime != nil { + in, out := &in.LastTransitionTime, &out.LastTransitionTime + *out = (*in).DeepCopy() + } return } @@ -470,8 +477,14 @@ func (in *ClusterServiceVersionSpec) DeepCopy() *ClusterServiceVersionSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterServiceVersionStatus) DeepCopyInto(out *ClusterServiceVersionStatus) { *out = *in - in.LastUpdateTime.DeepCopyInto(&out.LastUpdateTime) - in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) + if in.LastUpdateTime != nil { + in, out := &in.LastUpdateTime, &out.LastUpdateTime + *out = (*in).DeepCopy() + } + if in.LastTransitionTime != nil { + in, out := &in.LastTransitionTime, &out.LastTransitionTime + *out = (*in).DeepCopy() + } if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions *out = make([]ClusterServiceVersionCondition, len(*in)) @@ -486,8 +499,14 @@ func (in *ClusterServiceVersionStatus) DeepCopyInto(out *ClusterServiceVersionSt (*in)[i].DeepCopyInto(&(*out)[i]) } } - in.CertsLastUpdated.DeepCopyInto(&out.CertsLastUpdated) - in.CertsRotateAt.DeepCopyInto(&out.CertsRotateAt) + if in.CertsLastUpdated != nil { + in, out := &in.CertsLastUpdated, &out.CertsLastUpdated + *out = (*in).DeepCopy() + } + if in.CertsRotateAt != nil { + in, out := &in.CertsRotateAt, &out.CertsRotateAt + *out = (*in).DeepCopy() + } return } @@ -666,8 +685,14 @@ func (in *InstallPlan) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *InstallPlanCondition) DeepCopyInto(out *InstallPlanCondition) { *out = *in - in.LastUpdateTime.DeepCopyInto(&out.LastUpdateTime) - in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) + if in.LastUpdateTime != nil { + in, out := &in.LastUpdateTime, &out.LastUpdateTime + *out = (*in).DeepCopy() + } + if in.LastTransitionTime != nil { + in, out := &in.LastTransitionTime, &out.LastTransitionTime + *out = (*in).DeepCopy() + } return } @@ -814,11 +839,7 @@ func (in *Maintainer) DeepCopy() *Maintainer { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *NamedInstallStrategy) DeepCopyInto(out *NamedInstallStrategy) { *out = *in - if in.StrategySpecRaw != nil { - in, out := &in.StrategySpecRaw, &out.StrategySpecRaw - *out = make(json.RawMessage, len(*in)) - copy(*out, *in) - } + in.StrategySpec.DeepCopyInto(&out.StrategySpec) return } @@ -963,6 +984,83 @@ func (in *StepResource) DeepCopy() *StepResource { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StrategyDeploymentPermissions) DeepCopyInto(out *StrategyDeploymentPermissions) { + *out = *in + if in.Rules != nil { + in, out := &in.Rules, &out.Rules + *out = make([]rbacv1.PolicyRule, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StrategyDeploymentPermissions. +func (in *StrategyDeploymentPermissions) DeepCopy() *StrategyDeploymentPermissions { + if in == nil { + return nil + } + out := new(StrategyDeploymentPermissions) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StrategyDeploymentSpec) DeepCopyInto(out *StrategyDeploymentSpec) { + *out = *in + in.Spec.DeepCopyInto(&out.Spec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StrategyDeploymentSpec. +func (in *StrategyDeploymentSpec) DeepCopy() *StrategyDeploymentSpec { + if in == nil { + return nil + } + out := new(StrategyDeploymentSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StrategyDetailsDeployment) DeepCopyInto(out *StrategyDetailsDeployment) { + *out = *in + if in.DeploymentSpecs != nil { + in, out := &in.DeploymentSpecs, &out.DeploymentSpecs + *out = make([]StrategyDeploymentSpec, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Permissions != nil { + in, out := &in.Permissions, &out.Permissions + *out = make([]StrategyDeploymentPermissions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ClusterPermissions != nil { + in, out := &in.ClusterPermissions, &out.ClusterPermissions + *out = make([]StrategyDeploymentPermissions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StrategyDetailsDeployment. +func (in *StrategyDetailsDeployment) DeepCopy() *StrategyDetailsDeployment { + if in == nil { + return nil + } + out := new(StrategyDetailsDeployment) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Subscription) DeepCopyInto(out *Subscription) { *out = *in diff --git a/pkg/api/apis/operators/zz_generated.deepcopy.go b/pkg/api/apis/operators/zz_generated.deepcopy.go index d126a14dc2..51023b90a8 100644 --- a/pkg/api/apis/operators/zz_generated.deepcopy.go +++ b/pkg/api/apis/operators/zz_generated.deepcopy.go @@ -24,6 +24,7 @@ import ( json "encoding/json" corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -349,8 +350,14 @@ func (in *ClusterServiceVersion) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterServiceVersionCondition) DeepCopyInto(out *ClusterServiceVersionCondition) { *out = *in - in.LastUpdateTime.DeepCopyInto(&out.LastUpdateTime) - in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) + if in.LastUpdateTime != nil { + in, out := &in.LastUpdateTime, &out.LastUpdateTime + *out = (*in).DeepCopy() + } + if in.LastTransitionTime != nil { + in, out := &in.LastTransitionTime, &out.LastTransitionTime + *out = (*in).DeepCopy() + } return } @@ -470,8 +477,14 @@ func (in *ClusterServiceVersionSpec) DeepCopy() *ClusterServiceVersionSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterServiceVersionStatus) DeepCopyInto(out *ClusterServiceVersionStatus) { *out = *in - in.LastUpdateTime.DeepCopyInto(&out.LastUpdateTime) - in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) + if in.LastUpdateTime != nil { + in, out := &in.LastUpdateTime, &out.LastUpdateTime + *out = (*in).DeepCopy() + } + if in.LastTransitionTime != nil { + in, out := &in.LastTransitionTime, &out.LastTransitionTime + *out = (*in).DeepCopy() + } if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions *out = make([]ClusterServiceVersionCondition, len(*in)) @@ -486,8 +499,14 @@ func (in *ClusterServiceVersionStatus) DeepCopyInto(out *ClusterServiceVersionSt (*in)[i].DeepCopyInto(&(*out)[i]) } } - in.CertsLastUpdated.DeepCopyInto(&out.CertsLastUpdated) - in.CertsRotateAt.DeepCopyInto(&out.CertsRotateAt) + if in.CertsLastUpdated != nil { + in, out := &in.CertsLastUpdated, &out.CertsLastUpdated + *out = (*in).DeepCopy() + } + if in.CertsRotateAt != nil { + in, out := &in.CertsRotateAt, &out.CertsRotateAt + *out = (*in).DeepCopy() + } return } @@ -666,8 +685,14 @@ func (in *InstallPlan) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *InstallPlanCondition) DeepCopyInto(out *InstallPlanCondition) { *out = *in - in.LastUpdateTime.DeepCopyInto(&out.LastUpdateTime) - in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) + if in.LastUpdateTime != nil { + in, out := &in.LastUpdateTime, &out.LastUpdateTime + *out = (*in).DeepCopy() + } + if in.LastTransitionTime != nil { + in, out := &in.LastTransitionTime, &out.LastTransitionTime + *out = (*in).DeepCopy() + } return } @@ -814,11 +839,7 @@ func (in *Maintainer) DeepCopy() *Maintainer { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *NamedInstallStrategy) DeepCopyInto(out *NamedInstallStrategy) { *out = *in - if in.StrategySpecRaw != nil { - in, out := &in.StrategySpecRaw, &out.StrategySpecRaw - *out = make(json.RawMessage, len(*in)) - copy(*out, *in) - } + in.StrategySpec.DeepCopyInto(&out.StrategySpec) return } @@ -932,7 +953,10 @@ func (in *OperatorGroupStatus) DeepCopyInto(out *OperatorGroupStatus) { *out = new(corev1.ObjectReference) **out = **in } - in.LastUpdated.DeepCopyInto(&out.LastUpdated) + if in.LastUpdated != nil { + in, out := &in.LastUpdated, &out.LastUpdated + *out = (*in).DeepCopy() + } return } @@ -1077,6 +1101,83 @@ func (in *StepResource) DeepCopy() *StepResource { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StrategyDeploymentPermissions) DeepCopyInto(out *StrategyDeploymentPermissions) { + *out = *in + if in.Rules != nil { + in, out := &in.Rules, &out.Rules + *out = make([]rbacv1.PolicyRule, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StrategyDeploymentPermissions. +func (in *StrategyDeploymentPermissions) DeepCopy() *StrategyDeploymentPermissions { + if in == nil { + return nil + } + out := new(StrategyDeploymentPermissions) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StrategyDeploymentSpec) DeepCopyInto(out *StrategyDeploymentSpec) { + *out = *in + in.Spec.DeepCopyInto(&out.Spec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StrategyDeploymentSpec. +func (in *StrategyDeploymentSpec) DeepCopy() *StrategyDeploymentSpec { + if in == nil { + return nil + } + out := new(StrategyDeploymentSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StrategyDetailsDeployment) DeepCopyInto(out *StrategyDetailsDeployment) { + *out = *in + if in.DeploymentSpecs != nil { + in, out := &in.DeploymentSpecs, &out.DeploymentSpecs + *out = make([]StrategyDeploymentSpec, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Permissions != nil { + in, out := &in.Permissions, &out.Permissions + *out = make([]StrategyDeploymentPermissions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ClusterPermissions != nil { + in, out := &in.ClusterPermissions, &out.ClusterPermissions + *out = make([]StrategyDeploymentPermissions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StrategyDetailsDeployment. +func (in *StrategyDetailsDeployment) DeepCopy() *StrategyDetailsDeployment { + if in == nil { + return nil + } + out := new(StrategyDetailsDeployment) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Subscription) DeepCopyInto(out *Subscription) { *out = *in diff --git a/pkg/controller/install/deployment.go b/pkg/controller/install/deployment.go index f067b94e27..4ed1a9800d 100644 --- a/pkg/controller/install/deployment.go +++ b/pkg/controller/install/deployment.go @@ -5,7 +5,6 @@ import ( log "github.com/sirupsen/logrus" appsv1 "k8s.io/api/apps/v1" - rbac "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/api/equality" k8serrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/util/diff" @@ -15,30 +14,6 @@ import ( "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil" ) -const ( - InstallStrategyNameDeployment = "deployment" -) - -// StrategyDeploymentPermissions describe the rbac rules and service account needed by the install strategy -type StrategyDeploymentPermissions struct { - ServiceAccountName string `json:"serviceAccountName"` - Rules []rbac.PolicyRule `json:"rules"` -} - -// StrategyDeploymentSpec contains the name and spec for the deployment ALM should create -type StrategyDeploymentSpec struct { - Name string `json:"name"` - Spec appsv1.DeploymentSpec `json:"spec"` -} - -// StrategyDetailsDeployment represents the parsed details of a Deployment -// InstallStrategy. -type StrategyDetailsDeployment struct { - DeploymentSpecs []StrategyDeploymentSpec `json:"deployments"` - Permissions []StrategyDeploymentPermissions `json:"permissions,omitempty"` - ClusterPermissions []StrategyDeploymentPermissions `json:"clusterPermissions,omitempty"` -} - type StrategyDeploymentInstaller struct { strategyClient wrappers.InstallStrategyDeploymentInterface owner ownerutil.Owner @@ -47,11 +22,7 @@ type StrategyDeploymentInstaller struct { initializers DeploymentInitializerFuncChain } -func (d *StrategyDetailsDeployment) GetStrategyName() string { - return InstallStrategyNameDeployment -} - -var _ Strategy = &StrategyDetailsDeployment{} +var _ Strategy = &v1alpha1.StrategyDetailsDeployment{} var _ StrategyInstaller = &StrategyDeploymentInstaller{} // DeploymentInitializerFunc takes a deployment object and appropriately @@ -95,7 +66,7 @@ func NewStrategyDeploymentInstaller(strategyClient wrappers.InstallStrategyDeplo } } -func (i *StrategyDeploymentInstaller) installDeployments(deps []StrategyDeploymentSpec) error { +func (i *StrategyDeploymentInstaller) installDeployments(deps []v1alpha1.StrategyDeploymentSpec) error { for _, d := range deps { deployment, err := i.deploymentForSpec(d.Name, d.Spec) if err != nil { @@ -137,7 +108,7 @@ func (i *StrategyDeploymentInstaller) deploymentForSpec(name string, spec appsv1 return } -func (i *StrategyDeploymentInstaller) cleanupPrevious(current *StrategyDetailsDeployment, previous *StrategyDetailsDeployment) error { +func (i *StrategyDeploymentInstaller) cleanupPrevious(current *v1alpha1.StrategyDetailsDeployment, previous *v1alpha1.StrategyDetailsDeployment) error { previousDeploymentsMap := map[string]struct{}{} for _, d := range previous.DeploymentSpecs { previousDeploymentsMap[d.Name] = struct{}{} @@ -155,7 +126,7 @@ func (i *StrategyDeploymentInstaller) cleanupPrevious(current *StrategyDetailsDe } func (i *StrategyDeploymentInstaller) Install(s Strategy) error { - strategy, ok := s.(*StrategyDetailsDeployment) + strategy, ok := s.(*v1alpha1.StrategyDetailsDeployment) if !ok { return fmt.Errorf("attempted to install %s strategy with deployment installer", strategy.GetStrategyName()) } @@ -174,7 +145,7 @@ func (i *StrategyDeploymentInstaller) Install(s Strategy) error { // CheckInstalled can return nil (installed), or errors // Errors can indicate: some component missing (keep installing), unable to query (check again later), or unrecoverable (failed in a way we know we can't recover from) func (i *StrategyDeploymentInstaller) CheckInstalled(s Strategy) (installed bool, err error) { - strategy, ok := s.(*StrategyDetailsDeployment) + strategy, ok := s.(*v1alpha1.StrategyDetailsDeployment) if !ok { return false, StrategyError{Reason: StrategyErrReasonInvalidStrategy, Message: fmt.Sprintf("attempted to check %s strategy with deployment installer", strategy.GetStrategyName())} } @@ -186,7 +157,7 @@ func (i *StrategyDeploymentInstaller) CheckInstalled(s Strategy) (installed bool return true, nil } -func (i *StrategyDeploymentInstaller) checkForDeployments(deploymentSpecs []StrategyDeploymentSpec) error { +func (i *StrategyDeploymentInstaller) checkForDeployments(deploymentSpecs []v1alpha1.StrategyDeploymentSpec) error { var depNames []string for _, dep := range deploymentSpecs { depNames = append(depNames, dep.Name) @@ -275,7 +246,7 @@ func (i *StrategyDeploymentInstaller) equalDeployments(calculated, onCluster *ap } // Clean up orphaned deployments after reinstalling deployments process -func (i *StrategyDeploymentInstaller) cleanupOrphanedDeployments(deploymentSpecs []StrategyDeploymentSpec) error { +func (i *StrategyDeploymentInstaller) cleanupOrphanedDeployments(deploymentSpecs []v1alpha1.StrategyDeploymentSpec) error { // Map of deployments depNames := map[string]string{} for _, dep := range deploymentSpecs { diff --git a/pkg/controller/install/deployment_test.go b/pkg/controller/install/deployment_test.go index e76fed2c3f..9de662f2db 100644 --- a/pkg/controller/install/deployment_test.go +++ b/pkg/controller/install/deployment_test.go @@ -55,15 +55,15 @@ func testServiceAccount(name string, mockOwner ownerutil.Owner) *corev1.ServiceA return serviceAccount } -func strategy(n int, namespace string, mockOwner ownerutil.Owner) *StrategyDetailsDeployment { - var deploymentSpecs = []StrategyDeploymentSpec{} - var permissions = []StrategyDeploymentPermissions{} +func strategy(n int, namespace string, mockOwner ownerutil.Owner) *v1alpha1.StrategyDetailsDeployment { + var deploymentSpecs = []v1alpha1.StrategyDeploymentSpec{} + var permissions = []v1alpha1.StrategyDeploymentPermissions{} for i := 1; i <= n; i++ { dep := testDeployment(fmt.Sprintf("olm-dep-%d", i), namespace, mockOwner) - spec := StrategyDeploymentSpec{Name: dep.GetName(), Spec: dep.Spec} + spec := v1alpha1.StrategyDeploymentSpec{Name: dep.GetName(), Spec: dep.Spec} deploymentSpecs = append(deploymentSpecs, spec) serviceAccount := testServiceAccount(fmt.Sprintf("olm-sa-%d", i), mockOwner) - permissions = append(permissions, StrategyDeploymentPermissions{ + permissions = append(permissions, v1alpha1.StrategyDeploymentPermissions{ ServiceAccountName: serviceAccount.Name, Rules: []rbacv1.PolicyRule{ { @@ -74,7 +74,7 @@ func strategy(n int, namespace string, mockOwner ownerutil.Owner) *StrategyDetai }, }) } - return &StrategyDetailsDeployment{ + return &v1alpha1.StrategyDetailsDeployment{ DeploymentSpecs: deploymentSpecs, Permissions: permissions, } @@ -103,7 +103,7 @@ func TestInstallStrategyDeploymentInstallDeployments(t *testing.T) { ) type inputs struct { - strategyDeploymentSpecs []StrategyDeploymentSpec + strategyDeploymentSpecs []v1alpha1.StrategyDeploymentSpec } type setup struct { existingDeployments []*appsv1.Deployment @@ -122,7 +122,7 @@ func TestInstallStrategyDeploymentInstallDeployments(t *testing.T) { { description: "updates/creates correctly", inputs: inputs{ - strategyDeploymentSpecs: []StrategyDeploymentSpec{ + strategyDeploymentSpecs: []v1alpha1.StrategyDeploymentSpec{ { Name: "test-deployment-1", Spec: appsv1.DeploymentSpec{}, @@ -358,7 +358,7 @@ func TestInstallStrategyDeploymentCleanupDeployments(t *testing.T) { ) type inputs struct { - strategyDeploymentSpecs []StrategyDeploymentSpec + strategyDeploymentSpecs []v1alpha1.StrategyDeploymentSpec } type setup struct { existingDeployments []*appsv1.Deployment @@ -378,7 +378,7 @@ func TestInstallStrategyDeploymentCleanupDeployments(t *testing.T) { { description: "cleanup successfully", inputs: inputs{ - strategyDeploymentSpecs: []StrategyDeploymentSpec{ + strategyDeploymentSpecs: []v1alpha1.StrategyDeploymentSpec{ { Name: "test-deployment-1", Spec: appsv1.DeploymentSpec{}, @@ -410,7 +410,7 @@ func TestInstallStrategyDeploymentCleanupDeployments(t *testing.T) { { description: "cleanup unsuccessfully as no orphaned deployments found", inputs: inputs{ - strategyDeploymentSpecs: []StrategyDeploymentSpec{ + strategyDeploymentSpecs: []v1alpha1.StrategyDeploymentSpec{ { Name: "test-deployment-1", Spec: appsv1.DeploymentSpec{}, @@ -430,7 +430,7 @@ func TestInstallStrategyDeploymentCleanupDeployments(t *testing.T) { { description: "cleanup unsuccessfully as unable to look up orphaned deployments", inputs: inputs{ - strategyDeploymentSpecs: []StrategyDeploymentSpec{ + strategyDeploymentSpecs: []v1alpha1.StrategyDeploymentSpec{ { Name: "test-deployment-1", Spec: appsv1.DeploymentSpec{}, @@ -450,7 +450,7 @@ func TestInstallStrategyDeploymentCleanupDeployments(t *testing.T) { { description: "cleanup unsuccessfully as unable to delete deployments", inputs: inputs{ - strategyDeploymentSpecs: []StrategyDeploymentSpec{ + strategyDeploymentSpecs: []v1alpha1.StrategyDeploymentSpec{ { Name: "test-deployment-1", Spec: appsv1.DeploymentSpec{}, diff --git a/pkg/controller/install/resolver.go b/pkg/controller/install/resolver.go index df83871334..3b9f39c9e9 100644 --- a/pkg/controller/install/resolver.go +++ b/pkg/controller/install/resolver.go @@ -4,7 +4,6 @@ package install import ( - "encoding/json" "fmt" "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1" @@ -34,12 +33,8 @@ type StrategyResolver struct { func (r *StrategyResolver) UnmarshalStrategy(s v1alpha1.NamedInstallStrategy) (strategy Strategy, err error) { switch s.StrategyName { - case InstallStrategyNameDeployment: - strategy = &StrategyDetailsDeployment{} - if err := json.Unmarshal(s.StrategySpecRaw, strategy); err != nil { - return nil, err - } - return + case v1alpha1.InstallStrategyNameDeployment: + return &s.StrategySpec, nil } err = fmt.Errorf("unrecognized install strategy") return @@ -47,7 +42,7 @@ func (r *StrategyResolver) UnmarshalStrategy(s v1alpha1.NamedInstallStrategy) (s func (r *StrategyResolver) InstallerForStrategy(strategyName string, opClient operatorclient.ClientInterface, opLister operatorlister.OperatorLister, owner ownerutil.Owner, annotations map[string]string, previousStrategy Strategy) StrategyInstaller { switch strategyName { - case InstallStrategyNameDeployment: + case v1alpha1.InstallStrategyNameDeployment: strategyClient := wrappers.NewInstallStrategyDeploymentClient(opClient, opLister, owner.GetNamespace()) initializers := []DeploymentInitializerFunc{} diff --git a/pkg/controller/operators/olm/apiservices.go b/pkg/controller/operators/olm/apiservices.go index bb8f4e9bd0..c0afb54d01 100644 --- a/pkg/controller/operators/olm/apiservices.go +++ b/pkg/controller/operators/olm/apiservices.go @@ -284,7 +284,7 @@ func (a *Operator) installOwnedAPIServiceRequirements(csv *v1alpha1.ClusterServi }) // Assume the strategy is for a deployment - strategyDetailsDeployment, ok := strategy.(*install.StrategyDetailsDeployment) + strategyDetailsDeployment, ok := strategy.(*v1alpha1.StrategyDetailsDeployment) if !ok { return nil, fmt.Errorf("unsupported InstallStrategy type") } @@ -331,8 +331,10 @@ func (a *Operator) installOwnedAPIServiceRequirements(csv *v1alpha1.ClusterServi } // Set CSV cert status - csv.Status.CertsLastUpdated = metav1.Now() - csv.Status.CertsRotateAt = metav1.NewTime(rotateAt) + now := metav1.Now() + rotateTime := metav1.NewTime(rotateAt) + csv.Status.CertsLastUpdated = &now + csv.Status.CertsRotateAt = &rotateTime return strategyDetailsDeployment, nil } @@ -710,7 +712,7 @@ func (a *Operator) installAPIServiceRequirements(desc v1alpha1.APIServiceDescrip logger.WithFields(log.Fields{"obj": "apiService", "labels": apiService.GetLabels()}).Errorf("adoption check failed - %v", err) } - if !adoptable{ + if !adoptable { return nil, fmt.Errorf("pre-existing APIService %s is not adoptable", apiServiceName) } } @@ -770,7 +772,7 @@ func (a *Operator) isAPIServiceAdoptable(target *v1alpha1.ClusterServiceVersion, if err := ownerutil.InferGroupVersionKind(target); err != nil { a.logger.Warn(err.Error()) - } + } targetKind := target.GetObjectKind().GroupVersionKind().Kind if ownerKind != targetKind { @@ -795,11 +797,10 @@ func (a *Operator) isAPIServiceAdoptable(target *v1alpha1.ClusterServiceVersion, if replacing != nil { owners = append(owners, replacing) } - if currentOwnerCSV != nil && ( - currentOwnerCSV.Status.Phase == v1alpha1.CSVPhaseReplacing || currentOwnerCSV.Status.Phase == v1alpha1.CSVPhaseDeleting) { + if currentOwnerCSV != nil && (currentOwnerCSV.Status.Phase == v1alpha1.CSVPhaseReplacing || currentOwnerCSV.Status.Phase == v1alpha1.CSVPhaseDeleting) { owners = append(owners, currentOwnerCSV) } adoptable = ownerutil.AdoptableLabels(apiService.GetLabels(), true, owners...) return -} \ No newline at end of file +} diff --git a/pkg/controller/operators/olm/operator.go b/pkg/controller/operators/olm/operator.go index 5ffbf01343..76dd66d26f 100644 --- a/pkg/controller/operators/olm/operator.go +++ b/pkg/controller/operators/olm/operator.go @@ -484,8 +484,9 @@ func newOperatorWithConfig(ctx context.Context, config *operatorConfig) (*Operat return op, nil } -func (a *Operator) now() metav1.Time { - return metav1.NewTime(a.clock.Now().UTC()) +func (a *Operator) now() *metav1.Time { + now := metav1.NewTime(a.clock.Now().UTC()) + return &now } func (a *Operator) syncSubscription(obj interface{}) error { @@ -1834,7 +1835,7 @@ func (a *Operator) cleanupCSVDeployments(logger *logrus.Entry, csv *v1alpha1.Clu } // Assume the strategy is for a deployment - strategyDetailsDeployment, ok := strategy.(*install.StrategyDetailsDeployment) + strategyDetailsDeployment, ok := strategy.(*v1alpha1.StrategyDetailsDeployment) if !ok { logger.Warnf("could not cast install strategy as type %T", strategyDetailsDeployment) return @@ -1866,7 +1867,7 @@ func (a *Operator) ensureDeploymentAnnotations(logger *logrus.Entry, csv *v1alph } // Assume the strategy is for a deployment - strategyDetailsDeployment, ok := strategy.(*install.StrategyDetailsDeployment) + strategyDetailsDeployment, ok := strategy.(*v1alpha1.StrategyDetailsDeployment) if !ok { logger.Warnf("could not cast install strategy as type %T", strategyDetailsDeployment) return nil diff --git a/pkg/controller/operators/olm/operator_test.go b/pkg/controller/operators/olm/operator_test.go index 7eac4f3bdb..03648018d7 100644 --- a/pkg/controller/operators/olm/operator_test.go +++ b/pkg/controller/operators/olm/operator_test.go @@ -7,7 +7,6 @@ import ( "crypto/rand" "crypto/x509" "crypto/x509/pkix" - "encoding/json" "fmt" "math" "math/big" @@ -541,10 +540,10 @@ func csvWithStatusReason(csv *v1alpha1.ClusterServiceVersion, reason v1alpha1.Co return csv } -func installStrategy(deploymentName string, permissions []install.StrategyDeploymentPermissions, clusterPermissions []install.StrategyDeploymentPermissions) v1alpha1.NamedInstallStrategy { +func installStrategy(deploymentName string, permissions []v1alpha1.StrategyDeploymentPermissions, clusterPermissions []v1alpha1.StrategyDeploymentPermissions) v1alpha1.NamedInstallStrategy { var singleInstance = int32(1) - strategy := install.StrategyDetailsDeployment{ - DeploymentSpecs: []install.StrategyDeploymentSpec{ + strategy := v1alpha1.StrategyDetailsDeployment{ + DeploymentSpecs: []v1alpha1.StrategyDeploymentSpec{ { Name: deploymentName, Spec: appsv1.DeploymentSpec{ @@ -581,14 +580,10 @@ func installStrategy(deploymentName string, permissions []install.StrategyDeploy Permissions: permissions, ClusterPermissions: clusterPermissions, } - strategyRaw, err := json.Marshal(strategy) - if err != nil { - panic(err) - } return v1alpha1.NamedInstallStrategy{ - StrategyName: install.InstallStrategyNameDeployment, - StrategySpecRaw: strategyRaw, + StrategyName: v1alpha1.InstallStrategyNameDeployment, + StrategySpec: strategy, } } @@ -656,13 +651,13 @@ func withConditionReason(csv *v1alpha1.ClusterServiceVersion, reason v1alpha1.Co } func withPhase(csv *v1alpha1.ClusterServiceVersion, phase v1alpha1.ClusterServiceVersionPhase, reason v1alpha1.ConditionReason, message string, now metav1.Time) *v1alpha1.ClusterServiceVersion { - csv.SetPhase(phase, reason, message, now) + csv.SetPhase(phase, reason, message, &now) return csv } func withCertInfo(csv *v1alpha1.ClusterServiceVersion, rotateAt metav1.Time, lastUpdated metav1.Time) *v1alpha1.ClusterServiceVersion { - csv.Status.CertsRotateAt = rotateAt - csv.Status.CertsLastUpdated = lastUpdated + csv.Status.CertsRotateAt = &rotateAt + csv.Status.CertsLastUpdated = &lastUpdated return csv } @@ -915,31 +910,6 @@ func TestTransitionCSV(t *testing.T) { }, }, }, - { - name: "SingleCSVPendingToFailed/BadStrategy", - initial: initial{ - csvs: []runtime.Object{ - csvWithAnnotations(csv("csv1", - namespace, - "0.0.0", - "", - v1alpha1.NamedInstallStrategy{"deployment", json.RawMessage{}}, - []*v1beta1.CustomResourceDefinition{crd("c1", "v1", "g1")}, - []*v1beta1.CustomResourceDefinition{}, - v1alpha1.CSVPhasePending, - ), defaultTemplateAnnotations), - }, - clientObjs: []runtime.Object{addAnnotation(defaultOperatorGroup, v1.OperatorGroupProvidedAPIsAnnotationKey, "c1.v1.g1")}, - crds: []runtime.Object{ - crd("c1", "v1", "g1"), - }, - }, - expected: expected{ - csvStates: map[string]csvState{ - "csv1": {exists: true, phase: v1alpha1.CSVPhaseFailed}, - }, - }, - }, { name: "SingleCSVPendingToFailed/BadStrategyPermissions", initial: initial{ @@ -950,7 +920,7 @@ func TestTransitionCSV(t *testing.T) { "", installStrategy("csv1-dep1", nil, - []install.StrategyDeploymentPermissions{ + []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: "sa", Rules: []rbacv1.PolicyRule{ @@ -1293,31 +1263,6 @@ func TestTransitionCSV(t *testing.T) { }, }, }, - { - name: "SingleCSVFailedToFailed/BadStrategy", - initial: initial{ - csvs: []runtime.Object{ - csvWithAnnotations(csv("csv1", - namespace, - "0.0.0", - "", - v1alpha1.NamedInstallStrategy{"deployment", json.RawMessage{}}, - []*v1beta1.CustomResourceDefinition{crd("c1", "v1", "g1")}, - []*v1beta1.CustomResourceDefinition{}, - v1alpha1.CSVPhaseFailed, - ), defaultTemplateAnnotations), - }, - clientObjs: []runtime.Object{addAnnotation(defaultOperatorGroup, v1.OperatorGroupProvidedAPIsAnnotationKey, "c1.v1.g1")}, - objs: []runtime.Object{ - deployment("a1", namespace, "sa", defaultTemplateAnnotations), - }, - }, - expected: expected{ - csvStates: map[string]csvState{ - "csv1": {exists: true, phase: v1alpha1.CSVPhaseFailed, reason: v1alpha1.CSVReasonInvalidStrategy}, - }, - }, - }, { name: "SingleCSVPendingToInstallReady/CRD", initial: initial{ @@ -2221,31 +2166,6 @@ func TestTransitionCSV(t *testing.T) { }, }, }, - { - name: "SingleCSVInstallReadyToFailed/BadStrategy", - initial: initial{ - csvs: []runtime.Object{ - csvWithAnnotations(csv("csv1", - namespace, - "0.0.0", - "", - v1alpha1.NamedInstallStrategy{"deployment", json.RawMessage{}}, - []*v1beta1.CustomResourceDefinition{crd("c1", "v1", "g1")}, - []*v1beta1.CustomResourceDefinition{}, - v1alpha1.CSVPhaseInstallReady, - ), defaultTemplateAnnotations), - }, - clientObjs: []runtime.Object{defaultOperatorGroup}, - crds: []runtime.Object{ - crd("c1", "v1", "g1"), - }, - }, - expected: expected{ - csvStates: map[string]csvState{ - "csv1": {exists: true, phase: v1alpha1.CSVPhaseFailed}, - }, - }, - }, { name: "SingleCSVInstallingToSucceeded/UnmanagedDeploymentNotAffected", initial: initial{ @@ -3345,7 +3265,7 @@ func TestSyncOperatorGroups(t *testing.T) { serviceAccount := serviceAccount("sa", operatorNamespace) - permissions := []install.StrategyDeploymentPermissions{ + permissions := []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: serviceAccount.GetName(), Rules: []rbacv1.PolicyRule{ @@ -3375,8 +3295,8 @@ func TestSyncOperatorGroups(t *testing.T) { operatorCSVFinal.Status.Phase = v1alpha1.CSVPhaseSucceeded operatorCSVFinal.Status.Message = "install strategy completed with no errors" operatorCSVFinal.Status.Reason = v1alpha1.CSVReasonInstallSuccessful - operatorCSVFinal.Status.LastUpdateTime = now - operatorCSVFinal.Status.LastTransitionTime = now + operatorCSVFinal.Status.LastUpdateTime = &now + operatorCSVFinal.Status.LastTransitionTime = &now operatorCSVFinal.Status.RequirementStatus = []v1alpha1.RequirementStatus{ { Group: "operators.coreos.com", @@ -3416,29 +3336,29 @@ func TestSyncOperatorGroups(t *testing.T) { Phase: v1alpha1.CSVPhasePending, Reason: v1alpha1.CSVReasonRequirementsUnknown, Message: "requirements not yet checked", - LastUpdateTime: now, - LastTransitionTime: now, + LastUpdateTime: &now, + LastTransitionTime: &now, }, { Phase: v1alpha1.CSVPhaseInstallReady, Reason: v1alpha1.CSVReasonRequirementsMet, Message: "all requirements found, attempting install", - LastUpdateTime: now, - LastTransitionTime: now, + LastUpdateTime: &now, + LastTransitionTime: &now, }, { Phase: v1alpha1.CSVPhaseInstalling, Reason: v1alpha1.CSVReasonInstallSuccessful, Message: "waiting for install components to report healthy", - LastUpdateTime: now, - LastTransitionTime: now, + LastUpdateTime: &now, + LastTransitionTime: &now, }, { Phase: v1alpha1.CSVPhaseSucceeded, Reason: v1alpha1.CSVReasonInstallSuccessful, Message: "install strategy completed with no errors", - LastUpdateTime: now, - LastTransitionTime: now, + LastUpdateTime: &now, + LastTransitionTime: &now, }, } @@ -3447,15 +3367,15 @@ func TestSyncOperatorGroups(t *testing.T) { operatorCSVFailedNoTargetNS.Status.Phase = v1alpha1.CSVPhaseFailed operatorCSVFailedNoTargetNS.Status.Message = "no targetNamespaces are matched operatorgroups namespace selection" operatorCSVFailedNoTargetNS.Status.Reason = v1alpha1.CSVReasonNoTargetNamespaces - operatorCSVFailedNoTargetNS.Status.LastUpdateTime = now - operatorCSVFailedNoTargetNS.Status.LastTransitionTime = now + operatorCSVFailedNoTargetNS.Status.LastUpdateTime = &now + operatorCSVFailedNoTargetNS.Status.LastTransitionTime = &now operatorCSVFailedNoTargetNS.Status.Conditions = []v1alpha1.ClusterServiceVersionCondition{ { Phase: v1alpha1.CSVPhaseFailed, Reason: v1alpha1.CSVReasonNoTargetNamespaces, Message: "no targetNamespaces are matched operatorgroups namespace selection", - LastUpdateTime: now, - LastTransitionTime: now, + LastUpdateTime: &now, + LastTransitionTime: &now, }, } @@ -3463,7 +3383,7 @@ func TestSyncOperatorGroups(t *testing.T) { targetCSV.SetNamespace(targetNamespace) targetCSV.Status.Reason = v1alpha1.CSVReasonCopied targetCSV.Status.Message = "The operator is running in operator-ns but is managing this namespace" - targetCSV.Status.LastUpdateTime = now + targetCSV.Status.LastUpdateTime = &now ownerutil.AddNonBlockingOwner(serviceAccount, operatorCSV) @@ -3647,7 +3567,7 @@ func TestSyncOperatorGroups(t *testing.T) { }, expectedStatus: v1.OperatorGroupStatus{ Namespaces: []string{targetNamespace}, - LastUpdated: now, + LastUpdated: &now, }, }, { @@ -3688,7 +3608,7 @@ func TestSyncOperatorGroups(t *testing.T) { }, expectedStatus: v1.OperatorGroupStatus{ Namespaces: []string{operatorNamespace, targetNamespace}, - LastUpdated: now, + LastUpdated: &now, }, final: final{objects: map[string][]runtime.Object{ operatorNamespace: { @@ -3790,7 +3710,7 @@ func TestSyncOperatorGroups(t *testing.T) { }, expectedStatus: v1.OperatorGroupStatus{ Namespaces: []string{operatorNamespace, targetNamespace}, - LastUpdated: now, + LastUpdated: &now, }, final: final{objects: map[string][]runtime.Object{ operatorNamespace: { @@ -3895,7 +3815,7 @@ func TestSyncOperatorGroups(t *testing.T) { }, expectedStatus: v1.OperatorGroupStatus{ Namespaces: []string{corev1.NamespaceAll}, - LastUpdated: now, + LastUpdated: &now, }, final: final{objects: map[string][]runtime.Object{ operatorNamespace: { @@ -3957,127 +3877,6 @@ func TestSyncOperatorGroups(t *testing.T) { }, }}, }, - { - name: "AllNamespaces/CSVPresent/Found/PruneMissingProvidedAPI", - expectedEqual: true, - initial: initial{ - operatorGroup: &v1.OperatorGroup{ - ObjectMeta: metav1.ObjectMeta{ - Name: "operator-group-1", - Namespace: operatorNamespace, - Labels: map[string]string{"app": "app-a"}, - Annotations: map[string]string{ - v1.OperatorGroupProvidedAPIsAnnotationKey: "c1.v1.fake.api.group,missing.v1.fake.api.group", - }, - }, - Spec: v1.OperatorGroupSpec{}, - }, - clientObjs: []runtime.Object{operatorCSV}, - k8sObjs: []runtime.Object{ - &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: operatorNamespace, - Labels: map[string]string{"app": "app-a"}, - Annotations: map[string]string{"test": "annotation"}, - }, - }, - &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: targetNamespace, - Labels: map[string]string{"app": "app-a"}, - Annotations: map[string]string{"test": "annotation"}, - }, - }, - ownedDeployment, - serviceAccount, - role, - roleBinding, - }, - crds: []runtime.Object{crd}, - }, - expectedStatus: v1.OperatorGroupStatus{ - Namespaces: []string{corev1.NamespaceAll}, - LastUpdated: now, - }, - final: final{objects: map[string][]runtime.Object{ - operatorNamespace: { - withAnnotations(operatorCSVFinal.DeepCopy(), map[string]string{v1.OperatorGroupTargetsAnnotationKey: "", v1.OperatorGroupAnnotationKey: "operator-group-1", v1.OperatorGroupNamespaceAnnotationKey: operatorNamespace}), - annotatedGlobalDeployment, - &v1.OperatorGroup{ - TypeMeta: metav1.TypeMeta{ - Kind: v1.OperatorGroupKind, - APIVersion: strings.Join([]string{v1.GroupName, v1.GroupVersion}, "/"), - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "operator-group-1", - Namespace: operatorNamespace, - Labels: map[string]string{"app": "app-a"}, - Annotations: map[string]string{ - v1.OperatorGroupProvidedAPIsAnnotationKey: "c1.v1.fake.api.group", - }, - }, - Spec: v1.OperatorGroupSpec{}, - Status: v1.OperatorGroupStatus{ - Namespaces: []string{corev1.NamespaceAll}, - LastUpdated: now, - }, - }, - }, - "": { - &rbacv1.ClusterRole{ - TypeMeta: metav1.TypeMeta{ - Kind: "ClusterRole", - APIVersion: rbacv1.GroupName, - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "csv-role", - Labels: map[string]string{ - "olm.owner": "csv1", - "olm.owner.namespace": "operator-ns", - "olm.owner.kind": "ClusterServiceVersion", - }, - }, - Rules: append(permissions[0].Rules, rbacv1.PolicyRule{ - Verbs: ViewVerbs, - APIGroups: []string{corev1.GroupName}, - Resources: []string{"namespaces"}, - }), - }, - &rbacv1.ClusterRoleBinding{ - TypeMeta: metav1.TypeMeta{ - Kind: "ClusterRoleBinding", - APIVersion: rbacv1.GroupName, - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "csv-rolebinding", - Labels: map[string]string{ - "olm.owner": "csv1", - "olm.owner.namespace": "operator-ns", - "olm.owner.kind": "ClusterServiceVersion", - }, - }, - Subjects: []rbacv1.Subject{ - { - Kind: rbacv1.ServiceAccountKind, - Name: serviceAccount.GetName(), - Namespace: operatorNamespace, - }, - }, - RoleRef: rbacv1.RoleRef{ - APIGroup: rbacv1.GroupName, - Kind: "ClusterRole", - Name: "csv-role", - }, - }, - }, - targetNamespace: { - withLabels( - withAnnotations(targetCSV.DeepCopy(), map[string]string{v1.OperatorGroupAnnotationKey: "operator-group-1", v1.OperatorGroupNamespaceAnnotationKey: operatorNamespace}), - labels.Merge(targetCSV.GetLabels(), map[string]string{v1alpha1.CopiedLabelKey: operatorNamespace}), - ), - }, - }}, - }, { name: "AllNamespaces/CSVPresent/Found/PruneMissingProvidedAPI/StaticProvidedAPIs", expectedEqual: true, @@ -4107,7 +3906,7 @@ func TestSyncOperatorGroups(t *testing.T) { }, expectedStatus: v1.OperatorGroupStatus{ Namespaces: []string{corev1.NamespaceAll}, - LastUpdated: now, + LastUpdated: &now, }, final: final{objects: map[string][]runtime.Object{ operatorNamespace: { @@ -4125,7 +3924,7 @@ func TestSyncOperatorGroups(t *testing.T) { }, Status: v1.OperatorGroupStatus{ Namespaces: []string{corev1.NamespaceAll}, - LastUpdated: now, + LastUpdated: &now, }, }, }, @@ -4172,7 +3971,7 @@ func TestSyncOperatorGroups(t *testing.T) { }, expectedStatus: v1.OperatorGroupStatus{ Namespaces: []string{corev1.NamespaceAll}, - LastUpdated: now, + LastUpdated: &now, }, final: final{objects: map[string][]runtime.Object{ operatorNamespace: { diff --git a/pkg/controller/operators/olm/operatorgroup.go b/pkg/controller/operators/olm/operatorgroup.go index 3ea53a2f7d..de73959a40 100644 --- a/pkg/controller/operators/olm/operatorgroup.go +++ b/pkg/controller/operators/olm/operatorgroup.go @@ -365,7 +365,7 @@ func (a *Operator) ensureRBACInTargetNamespace(csv *v1alpha1.ClusterServiceVersi if err != nil { return err } - strategyDetailsDeployment, ok := strategy.(*install.StrategyDetailsDeployment) + strategyDetailsDeployment, ok := strategy.(*v1alpha1.StrategyDetailsDeployment) if !ok { return fmt.Errorf("could not cast install strategy as type %T", strategyDetailsDeployment) } @@ -584,15 +584,7 @@ func (a *Operator) ensureCSVsInNamespaces(csv *v1alpha1.ClusterServiceVersion, o return err } - strategyResolver := install.StrategyResolver{} - strategy, err := strategyResolver.UnmarshalStrategy(csv.Spec.InstallStrategy) - if err != nil { - return err - } - strategyDetailsDeployment, ok := strategy.(*install.StrategyDetailsDeployment) - if !ok { - return fmt.Errorf("could not cast install strategy as type %T", strategyDetailsDeployment) - } + strategyDetailsDeployment := &csv.Spec.InstallStrategy.StrategySpec ruleChecker := install.NewCSVRuleChecker(a.lister.RbacV1().RoleLister(), a.lister.RbacV1().RoleBindingLister(), a.lister.RbacV1().ClusterRoleLister(), a.lister.RbacV1().ClusterRoleBindingLister(), csv) logger := a.logger.WithField("opgroup", operatorGroup.GetName()).WithField("csv", csv.GetName()) diff --git a/pkg/controller/operators/olm/requirements.go b/pkg/controller/operators/olm/requirements.go index ad758f48da..cc8ac091b8 100644 --- a/pkg/controller/operators/olm/requirements.go +++ b/pkg/controller/operators/olm/requirements.go @@ -70,7 +70,7 @@ func (a *Operator) minKubeVersionStatus(name string, minKubeVersion string) (met return } -func (a *Operator) requirementStatus(strategyDetailsDeployment *install.StrategyDetailsDeployment, crdDescs []v1alpha1.CRDDescription, +func (a *Operator) requirementStatus(strategyDetailsDeployment *v1alpha1.StrategyDetailsDeployment, crdDescs []v1alpha1.CRDDescription, ownedAPIServiceDescs []v1alpha1.APIServiceDescription, requiredAPIServiceDescs []v1alpha1.APIServiceDescription, requiredNativeAPIs []metav1.GroupVersionKind) (met bool, statuses []v1alpha1.RequirementStatus) { met = true @@ -238,10 +238,10 @@ func (a *Operator) requirementStatus(strategyDetailsDeployment *install.Strategy } // permissionStatus checks whether the given CSV's RBAC requirements are met in its namespace -func (a *Operator) permissionStatus(strategyDetailsDeployment *install.StrategyDetailsDeployment, ruleChecker install.RuleChecker, targetNamespace, serviceAccountNamespace string) (bool, []v1alpha1.RequirementStatus, error) { +func (a *Operator) permissionStatus(strategyDetailsDeployment *v1alpha1.StrategyDetailsDeployment, ruleChecker install.RuleChecker, targetNamespace, serviceAccountNamespace string) (bool, []v1alpha1.RequirementStatus, error) { statusesSet := map[string]v1alpha1.RequirementStatus{} - checkPermissions := func(permissions []install.StrategyDeploymentPermissions, namespace string) (bool, error) { + checkPermissions := func(permissions []v1alpha1.StrategyDeploymentPermissions, namespace string) (bool, error) { met := true for _, perm := range permissions { saName := perm.ServiceAccountName @@ -345,7 +345,7 @@ func (a *Operator) requirementAndPermissionStatus(csv *v1alpha1.ClusterServiceVe } // Assume the strategy is for a deployment - strategyDetailsDeployment, ok := strategy.(*install.StrategyDetailsDeployment) + strategyDetailsDeployment, ok := strategy.(*v1alpha1.StrategyDetailsDeployment) if !ok { return false, nil, fmt.Errorf("could not cast install strategy as type %T", strategyDetailsDeployment) } diff --git a/pkg/controller/operators/olm/requirements_test.go b/pkg/controller/operators/olm/requirements_test.go index ddb1b0b13b..b7df914b04 100644 --- a/pkg/controller/operators/olm/requirements_test.go +++ b/pkg/controller/operators/olm/requirements_test.go @@ -2,7 +2,6 @@ package olm import ( "context" - "encoding/json" "fmt" "testing" @@ -15,7 +14,6 @@ import ( "k8s.io/apimachinery/pkg/types" "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1" - "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install" ) func TestRequirementAndPermissionStatus(t *testing.T) { @@ -35,23 +33,6 @@ func TestRequirementAndPermissionStatus(t *testing.T) { expectedRequirementStatuses map[gvkn]v1alpha1.RequirementStatus expectedError error }{ - { - description: "BadInstallStrategy", - csv: csv("csv1", - namespace, - "0.0.0", - "", - v1alpha1.NamedInstallStrategy{"deployment", json.RawMessage{}}, - nil, - nil, - v1alpha1.CSVPhasePending, - ), - existingObjs: nil, - existingExtObjs: nil, - met: false, - expectedRequirementStatuses: nil, - expectedError: fmt.Errorf("unexpected end of JSON input"), - }, { description: "AllPermissionsMet", csv: csv("csv1", @@ -60,7 +41,7 @@ func TestRequirementAndPermissionStatus(t *testing.T) { "", installStrategy( "csv1-dep", - []install.StrategyDeploymentPermissions{ + []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: "sa", Rules: []rbacv1.PolicyRule{ @@ -72,7 +53,7 @@ func TestRequirementAndPermissionStatus(t *testing.T) { }, }, }, - []install.StrategyDeploymentPermissions{ + []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: "sa", Rules: []rbacv1.PolicyRule{ @@ -198,7 +179,7 @@ func TestRequirementAndPermissionStatus(t *testing.T) { "", installStrategy( "csv1-dep", - []install.StrategyDeploymentPermissions{ + []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: "sa", Rules: []rbacv1.PolicyRule{ @@ -210,7 +191,7 @@ func TestRequirementAndPermissionStatus(t *testing.T) { }, }, }, - []install.StrategyDeploymentPermissions{ + []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: "sa", Rules: []rbacv1.PolicyRule{ @@ -336,7 +317,7 @@ func TestRequirementAndPermissionStatus(t *testing.T) { "", installStrategy( "csv1-dep", - []install.StrategyDeploymentPermissions{ + []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: "sa", Rules: []rbacv1.PolicyRule{ @@ -612,7 +593,9 @@ func TestRequirementAndPermissionStatus(t *testing.T) { // Get the permission status met, statuses, err := op.requirementAndPermissionStatus(test.csv) + if test.expectedError != nil { + require.Error(t, err) require.EqualError(t, test.expectedError, err.Error()) } require.Equal(t, test.met, met) @@ -644,7 +627,7 @@ func TestMinKubeVersionStatus(t *testing.T) { namespace, "0.0.0", "", - v1alpha1.NamedInstallStrategy{"deployment", json.RawMessage{}}, + v1alpha1.NamedInstallStrategy{"deployment", v1alpha1.StrategyDetailsDeployment{}}, nil, nil, v1alpha1.CSVPhasePending, diff --git a/pkg/controller/registry/resolver/rbac.go b/pkg/controller/registry/resolver/rbac.go index d221b6eb9e..17ebf610b7 100644 --- a/pkg/controller/registry/resolver/rbac.go +++ b/pkg/controller/registry/resolver/rbac.go @@ -62,7 +62,7 @@ func RBACForClusterServiceVersion(csv *v1alpha1.ClusterServiceVersion) (map[stri } // Assume the strategy is for a deployment - strategyDetailsDeployment, ok := strategy.(*install.StrategyDetailsDeployment) + strategyDetailsDeployment, ok := strategy.(*v1alpha1.StrategyDetailsDeployment) if !ok { return nil, fmt.Errorf("could not assert strategy implementation as deployment for CSV %s", csv.GetName()) } diff --git a/pkg/controller/registry/resolver/resolver_test.go b/pkg/controller/registry/resolver/resolver_test.go index 284fbf2da4..5d792b7a94 100644 --- a/pkg/controller/registry/resolver/resolver_test.go +++ b/pkg/controller/registry/resolver/resolver_test.go @@ -18,7 +18,6 @@ import ( "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned" "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned/fake" "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/informers/externalversions" - "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorlister" ) @@ -400,7 +399,7 @@ func TestNamespaceResolverRBAC(t *testing.T) { namespace := "catsrc-namespace" catalog := CatalogKey{"catsrc", namespace} - simplePermissions := []install.StrategyDeploymentPermissions{ + simplePermissions := []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: "test-sa", Rules: []rbacv1.PolicyRule{ diff --git a/pkg/controller/registry/resolver/util_test.go b/pkg/controller/registry/resolver/util_test.go index aa6b3569d3..66b1c38382 100644 --- a/pkg/controller/registry/resolver/util_test.go +++ b/pkg/controller/registry/resolver/util_test.go @@ -20,7 +20,6 @@ import ( "k8s.io/apimachinery/pkg/runtime" "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1" - "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/fakes" ) @@ -89,12 +88,12 @@ func NewFakeOperatorSurface(name, pkg, channel, replaces, src, startingCSV strin } } -func csv(name, replaces string, ownedCRDs, requiredCRDs, ownedAPIServices, requiredAPIServices APISet, permissions, clusterPermissions []install.StrategyDeploymentPermissions) *v1alpha1.ClusterServiceVersion { +func csv(name, replaces string, ownedCRDs, requiredCRDs, ownedAPIServices, requiredAPIServices APISet, permissions, clusterPermissions []v1alpha1.StrategyDeploymentPermissions) *v1alpha1.ClusterServiceVersion { var singleInstance = int32(1) - strategy := install.StrategyDetailsDeployment{ + strategy := v1alpha1.StrategyDetailsDeployment{ Permissions: permissions, ClusterPermissions: clusterPermissions, - DeploymentSpecs: []install.StrategyDeploymentSpec{ + DeploymentSpecs: []v1alpha1.StrategyDeploymentSpec{ { Name: name, Spec: appsv1.DeploymentSpec{ @@ -129,14 +128,10 @@ func csv(name, replaces string, ownedCRDs, requiredCRDs, ownedAPIServices, requi }, }, } - strategyRaw, err := json.Marshal(strategy) - if err != nil { - panic(err) - } installStrategy := v1alpha1.NamedInstallStrategy{ - StrategyName: install.InstallStrategyNameDeployment, - StrategySpecRaw: strategyRaw, + StrategyName: v1alpha1.InstallStrategyNameDeployment, + StrategySpec: strategy, } requiredCRDDescs := make([]v1alpha1.CRDDescription, 0) @@ -272,7 +267,7 @@ func withBundleObject(bundle *api.Bundle, obj *unstructured.Unstructured) *api.B return bundle } -func bundleWithPermissions(name, pkg, channel, replaces string, providedCRDs, requiredCRDs, providedAPIServices, requiredAPIServices APISet, permissions, clusterPermissions []install.StrategyDeploymentPermissions) *api.Bundle { +func bundleWithPermissions(name, pkg, channel, replaces string, providedCRDs, requiredCRDs, providedAPIServices, requiredAPIServices APISet, permissions, clusterPermissions []v1alpha1.StrategyDeploymentPermissions) *api.Bundle { csvJson, err := json.Marshal(csv(name, replaces, providedCRDs, requiredCRDs, providedAPIServices, requiredAPIServices, permissions, clusterPermissions)) if err != nil { panic(err) diff --git a/pkg/lib/scoped/syncer.go b/pkg/lib/scoped/syncer.go index 06a8f39d39..a7d66a4c0c 100644 --- a/pkg/lib/scoped/syncer.go +++ b/pkg/lib/scoped/syncer.go @@ -99,7 +99,8 @@ func (s *UserDefinedServiceAccountSyncer) update(in *v1.OperatorGroup, ref *core status := out.Status.DeepCopy() status.ServiceAccountRef = ref - status.LastUpdated = metav1.NewTime(s.clock.Now()) + now := metav1.NewTime(s.clock.Now()) + status.LastUpdated = &now out.Status = *status diff --git a/pkg/lib/version/version.go b/pkg/lib/version/version.go index 51e1fac659..713c5779bb 100644 --- a/pkg/lib/version/version.go +++ b/pkg/lib/version/version.go @@ -9,8 +9,9 @@ import ( // +k8s:openapi-gen=true // OperatorVersion is a wrapper around semver.Version which supports correct // marshaling to YAML and JSON. +// +kubebuilder:validation:Type=string type OperatorVersion struct { - semver.Version + semver.Version `json:"-"` } // DeepCopyInto creates a deep-copy of the Version value. diff --git a/test/e2e/csv_e2e_test.go b/test/e2e/csv_e2e_test.go index e873c8d734..c1050f8778 100644 --- a/test/e2e/csv_e2e_test.go +++ b/test/e2e/csv_e2e_test.go @@ -1,7 +1,6 @@ package e2e import ( - "encoding/json" "fmt" "strings" "testing" @@ -24,7 +23,6 @@ import ( v1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1" "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1" "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned" - "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient" ) @@ -443,7 +441,7 @@ func TestCreateCSVWithUnmetPermissionsCRD(t *testing.T) { crc := newCRClient(t) saName := genName("dep-") - permissions := []install.StrategyDeploymentPermissions{ + permissions := []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: saName, Rules: []rbacv1.PolicyRule{ @@ -456,7 +454,7 @@ func TestCreateCSVWithUnmetPermissionsCRD(t *testing.T) { }, } - clusterPermissions := []install.StrategyDeploymentPermissions{ + clusterPermissions := []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: saName, Rules: []rbacv1.PolicyRule{ @@ -615,7 +613,7 @@ func TestCreateCSVWithUnmetPermissionsAPIService(t *testing.T) { crc := newCRClient(t) saName := genName("dep-") - permissions := []install.StrategyDeploymentPermissions{ + permissions := []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: saName, Rules: []rbacv1.PolicyRule{ @@ -628,7 +626,7 @@ func TestCreateCSVWithUnmetPermissionsAPIService(t *testing.T) { }, } - clusterPermissions := []install.StrategyDeploymentPermissions{ + clusterPermissions := []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: saName, Rules: []rbacv1.PolicyRule{ @@ -763,7 +761,7 @@ func TestCreateCSVRequirementsMetCRD(t *testing.T) { _, err := c.CreateServiceAccount(&sa) require.NoError(t, err, "could not create ServiceAccount %#v", sa) - permissions := []install.StrategyDeploymentPermissions{ + permissions := []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: sa.GetName(), Rules: []rbacv1.PolicyRule{ @@ -776,7 +774,7 @@ func TestCreateCSVRequirementsMetCRD(t *testing.T) { }, } - clusterPermissions := []install.StrategyDeploymentPermissions{ + clusterPermissions := []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: sa.GetName(), Rules: []rbacv1.PolicyRule{ @@ -1025,7 +1023,7 @@ func TestCreateCSVRequirementsMetAPIService(t *testing.T) { _, err := c.CreateServiceAccount(&sa) require.NoError(t, err, "could not create ServiceAccount") - permissions := []install.StrategyDeploymentPermissions{ + permissions := []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: sa.GetName(), Rules: []rbacv1.PolicyRule{ @@ -1038,7 +1036,7 @@ func TestCreateCSVRequirementsMetAPIService(t *testing.T) { }, } - clusterPermissions := []install.StrategyDeploymentPermissions{ + clusterPermissions := []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: sa.GetName(), Rules: []rbacv1.PolicyRule{ @@ -1191,15 +1189,14 @@ func TestCreateCSVWithOwnedAPIService(t *testing.T) { apiServiceName := strings.Join([]string{version, mockGroup}, ".") // Create CSV for the package-server - strategy := install.StrategyDetailsDeployment{ - DeploymentSpecs: []install.StrategyDeploymentSpec{ + strategy := v1alpha1.StrategyDetailsDeployment{ + DeploymentSpecs: []v1alpha1.StrategyDeploymentSpec{ { Name: depName, Spec: depSpec, }, }, } - strategyRaw, err := json.Marshal(strategy) owned := make([]v1alpha1.APIServiceDescription, len(mockKinds)) for i, kind := range mockKinds { @@ -1237,8 +1234,8 @@ func TestCreateCSVWithOwnedAPIService(t *testing.T) { }, }, InstallStrategy: v1alpha1.NamedInstallStrategy{ - StrategyName: install.InstallStrategyNameDeployment, - StrategySpecRaw: strategyRaw, + StrategyName: v1alpha1.InstallStrategyNameDeployment, + StrategySpec: strategy, }, APIServiceDefinitions: v1alpha1.APIServiceDefinitions{ Owned: owned, @@ -1315,8 +1312,9 @@ func TestCreateCSVWithOwnedAPIService(t *testing.T) { require.True(t, ok, "expected olm sha annotation not present on existing pod template") // Induce a cert rotation - fetchedCSV.Status.CertsLastUpdated = metav1.Now() - fetchedCSV.Status.CertsRotateAt = metav1.Now() + now := metav1.Now() + fetchedCSV.Status.CertsLastUpdated = &now + fetchedCSV.Status.CertsRotateAt = &now fetchedCSV, err = crc.OperatorsV1alpha1().ClusterServiceVersions(testNamespace).UpdateStatus(fetchedCSV) require.NoError(t, err) @@ -1379,15 +1377,14 @@ func TestUpdateCSVWithOwnedAPIService(t *testing.T) { apiServiceName := strings.Join([]string{version, mockGroup}, ".") // Create CSVs for the hat-server - strategy := install.StrategyDetailsDeployment{ - DeploymentSpecs: []install.StrategyDeploymentSpec{ + strategy := v1alpha1.StrategyDetailsDeployment{ + DeploymentSpecs: []v1alpha1.StrategyDeploymentSpec{ { Name: depName, Spec: depSpec, }, }, } - strategyRaw, err := json.Marshal(strategy) owned := make([]v1alpha1.APIServiceDescription, len(mockKinds)) for i, kind := range mockKinds { @@ -1425,8 +1422,8 @@ func TestUpdateCSVWithOwnedAPIService(t *testing.T) { }, }, InstallStrategy: v1alpha1.NamedInstallStrategy{ - StrategyName: install.InstallStrategyNameDeployment, - StrategySpecRaw: strategyRaw, + StrategyName: v1alpha1.InstallStrategyNameDeployment, + StrategySpec: strategy, }, APIServiceDefinitions: v1alpha1.APIServiceDefinitions{ Owned: owned, @@ -1436,7 +1433,7 @@ func TestUpdateCSVWithOwnedAPIService(t *testing.T) { csv.SetName("csv-hat-1") // Create the APIService CSV - _, err = createCSV(t, c, crc, csv, testNamespace, false, false) + _, err := createCSV(t, c, crc, csv, testNamespace, false, false) require.NoError(t, err) _, err = fetchCSV(t, crc, csv.Name, testNamespace, csvSucceededChecker) @@ -1499,8 +1496,8 @@ func TestUpdateCSVWithOwnedAPIService(t *testing.T) { }, }, InstallStrategy: v1alpha1.NamedInstallStrategy{ - StrategyName: install.InstallStrategyNameDeployment, - StrategySpecRaw: strategyRaw, + StrategyName: v1alpha1.InstallStrategyNameDeployment, + StrategySpec: strategy, }, APIServiceDefinitions: v1alpha1.APIServiceDefinitions{ Owned: owned, @@ -1635,15 +1632,14 @@ func TestCreateSameCSVWithOwnedAPIServiceMultiNamespace(t *testing.T) { apiServiceName := strings.Join([]string{version, mockGroup}, ".") // Create CSVs for the hat-server - strategy := install.StrategyDetailsDeployment{ - DeploymentSpecs: []install.StrategyDeploymentSpec{ + strategy := v1alpha1.StrategyDetailsDeployment{ + DeploymentSpecs: []v1alpha1.StrategyDeploymentSpec{ { Name: depName, Spec: depSpec, }, }, } - strategyRaw, err := json.Marshal(strategy) owned := make([]v1alpha1.APIServiceDescription, len(mockKinds)) for i, kind := range mockKinds { @@ -1681,8 +1677,8 @@ func TestCreateSameCSVWithOwnedAPIServiceMultiNamespace(t *testing.T) { }, }, InstallStrategy: v1alpha1.NamedInstallStrategy{ - StrategyName: install.InstallStrategyNameDeployment, - StrategySpecRaw: strategyRaw, + StrategyName: v1alpha1.InstallStrategyNameDeployment, + StrategySpec: strategy, }, APIServiceDefinitions: v1alpha1.APIServiceDefinitions{ Owned: owned, @@ -1755,8 +1751,8 @@ func TestCreateSameCSVWithOwnedAPIServiceMultiNamespace(t *testing.T) { }, }, InstallStrategy: v1alpha1.NamedInstallStrategy{ - StrategyName: install.InstallStrategyNameDeployment, - StrategySpecRaw: strategyRaw, + StrategyName: v1alpha1.InstallStrategyNameDeployment, + StrategySpec: strategy, }, APIServiceDefinitions: v1alpha1.APIServiceDefinitions{ Owned: owned, @@ -1867,15 +1863,15 @@ func TestUpdateCSVSameDeploymentName(t *testing.T) { // Create "current" CSV nginxName := genName("nginx-") - strategy := install.StrategyDetailsDeployment{ - DeploymentSpecs: []install.StrategyDeploymentSpec{ + strategy := v1alpha1.StrategyDetailsDeployment{ + DeploymentSpecs: []v1alpha1.StrategyDeploymentSpec{ { Name: genName("dep-"), Spec: newNginxDeployment(nginxName), }, }, } - strategyRaw, err := json.Marshal(strategy) + require.NoError(t, err) require.NoError(t, err) @@ -1909,8 +1905,8 @@ func TestUpdateCSVSameDeploymentName(t *testing.T) { }, }, InstallStrategy: v1alpha1.NamedInstallStrategy{ - StrategyName: install.InstallStrategyNameDeployment, - StrategySpecRaw: strategyRaw, + StrategyName: v1alpha1.InstallStrategyNameDeployment, + StrategySpec: strategy, }, CustomResourceDefinitions: v1alpha1.CustomResourceDefinitions{ Owned: []v1alpha1.CRDDescription{ @@ -1940,8 +1936,8 @@ func TestUpdateCSVSameDeploymentName(t *testing.T) { require.NotNil(t, dep) // Create "updated" CSV - strategyNew := install.StrategyDetailsDeployment{ - DeploymentSpecs: []install.StrategyDeploymentSpec{ + strategyNew := v1alpha1.StrategyDetailsDeployment{ + DeploymentSpecs: []v1alpha1.StrategyDeploymentSpec{ { // Same name Name: strategy.DeploymentSpecs[0].Name, @@ -1950,7 +1946,7 @@ func TestUpdateCSVSameDeploymentName(t *testing.T) { }, }, } - strategyNewRaw, err := json.Marshal(strategyNew) + require.NoError(t, err) csvNew := v1alpha1.ClusterServiceVersion{ @@ -1982,8 +1978,8 @@ func TestUpdateCSVSameDeploymentName(t *testing.T) { }, }, InstallStrategy: v1alpha1.NamedInstallStrategy{ - StrategyName: install.InstallStrategyNameDeployment, - StrategySpecRaw: strategyNewRaw, + StrategyName: v1alpha1.InstallStrategyNameDeployment, + StrategySpec: strategyNew, }, CustomResourceDefinitions: v1alpha1.CustomResourceDefinitions{ Owned: []v1alpha1.CRDDescription{ @@ -2052,15 +2048,15 @@ func TestUpdateCSVDifferentDeploymentName(t *testing.T) { defer cleanupCRD() // create "current" CSV - strategy := install.StrategyDetailsDeployment{ - DeploymentSpecs: []install.StrategyDeploymentSpec{ + strategy := v1alpha1.StrategyDetailsDeployment{ + DeploymentSpecs: []v1alpha1.StrategyDeploymentSpec{ { Name: genName("dep-"), Spec: newNginxDeployment(genName("nginx-")), }, }, } - strategyRaw, err := json.Marshal(strategy) + require.NoError(t, err) csv := v1alpha1.ClusterServiceVersion{ @@ -2092,8 +2088,8 @@ func TestUpdateCSVDifferentDeploymentName(t *testing.T) { }, }, InstallStrategy: v1alpha1.NamedInstallStrategy{ - StrategyName: install.InstallStrategyNameDeployment, - StrategySpecRaw: strategyRaw, + StrategyName: v1alpha1.InstallStrategyNameDeployment, + StrategySpec: strategy, }, CustomResourceDefinitions: v1alpha1.CustomResourceDefinitions{ Owned: []v1alpha1.CRDDescription{ @@ -2123,15 +2119,15 @@ func TestUpdateCSVDifferentDeploymentName(t *testing.T) { require.NotNil(t, dep) // Create "updated" CSV - strategyNew := install.StrategyDetailsDeployment{ - DeploymentSpecs: []install.StrategyDeploymentSpec{ + strategyNew := v1alpha1.StrategyDetailsDeployment{ + DeploymentSpecs: []v1alpha1.StrategyDeploymentSpec{ { Name: genName("dep2"), Spec: newNginxDeployment(genName("nginx-")), }, }, } - strategyNewRaw, err := json.Marshal(strategyNew) + require.NoError(t, err) csvNew := v1alpha1.ClusterServiceVersion{ @@ -2163,8 +2159,8 @@ func TestUpdateCSVDifferentDeploymentName(t *testing.T) { }, }, InstallStrategy: v1alpha1.NamedInstallStrategy{ - StrategyName: install.InstallStrategyNameDeployment, - StrategySpecRaw: strategyNewRaw, + StrategyName: v1alpha1.InstallStrategyNameDeployment, + StrategySpec: strategyNew, }, CustomResourceDefinitions: v1alpha1.CustomResourceDefinitions{ Owned: []v1alpha1.CRDDescription{ @@ -2240,15 +2236,15 @@ func TestUpdateCSVMultipleIntermediates(t *testing.T) { defer cleanupCRD() // create "current" CSV - strategy := install.StrategyDetailsDeployment{ - DeploymentSpecs: []install.StrategyDeploymentSpec{ + strategy := v1alpha1.StrategyDetailsDeployment{ + DeploymentSpecs: []v1alpha1.StrategyDeploymentSpec{ { Name: genName("dep-"), Spec: newNginxDeployment(genName("nginx-")), }, }, } - strategyRaw, err := json.Marshal(strategy) + require.NoError(t, err) csv := v1alpha1.ClusterServiceVersion{ @@ -2280,8 +2276,8 @@ func TestUpdateCSVMultipleIntermediates(t *testing.T) { }, }, InstallStrategy: v1alpha1.NamedInstallStrategy{ - StrategyName: install.InstallStrategyNameDeployment, - StrategySpecRaw: strategyRaw, + StrategyName: v1alpha1.InstallStrategyNameDeployment, + StrategySpec: strategy, }, CustomResourceDefinitions: v1alpha1.CustomResourceDefinitions{ Owned: []v1alpha1.CRDDescription{ @@ -2311,15 +2307,15 @@ func TestUpdateCSVMultipleIntermediates(t *testing.T) { require.NotNil(t, dep) // Create "updated" CSV - strategyNew := install.StrategyDetailsDeployment{ - DeploymentSpecs: []install.StrategyDeploymentSpec{ + strategyNew := v1alpha1.StrategyDetailsDeployment{ + DeploymentSpecs: []v1alpha1.StrategyDeploymentSpec{ { Name: genName("dep2"), Spec: newNginxDeployment(genName("nginx-")), }, }, } - strategyNewRaw, err := json.Marshal(strategyNew) + require.NoError(t, err) csvNew := v1alpha1.ClusterServiceVersion{ @@ -2351,8 +2347,8 @@ func TestUpdateCSVMultipleIntermediates(t *testing.T) { }, }, InstallStrategy: v1alpha1.NamedInstallStrategy{ - StrategyName: install.InstallStrategyNameDeployment, - StrategySpecRaw: strategyNewRaw, + StrategyName: v1alpha1.InstallStrategyNameDeployment, + StrategySpec: strategyNew, }, CustomResourceDefinitions: v1alpha1.CustomResourceDefinitions{ Owned: []v1alpha1.CRDDescription{ @@ -2421,15 +2417,15 @@ func TestUpdateCSVInPlace(t *testing.T) { // Create "current" CSV nginxName := genName("nginx-") - strategy := install.StrategyDetailsDeployment{ - DeploymentSpecs: []install.StrategyDeploymentSpec{ + strategy := v1alpha1.StrategyDetailsDeployment{ + DeploymentSpecs: []v1alpha1.StrategyDeploymentSpec{ { Name: genName("dep-"), Spec: newNginxDeployment(nginxName), }, }, } - strategyRaw, err := json.Marshal(strategy) + require.NoError(t, err) require.NoError(t, err) @@ -2463,8 +2459,8 @@ func TestUpdateCSVInPlace(t *testing.T) { }, }, InstallStrategy: v1alpha1.NamedInstallStrategy{ - StrategyName: install.InstallStrategyNameDeployment, - StrategySpecRaw: strategyRaw, + StrategyName: v1alpha1.InstallStrategyNameDeployment, + StrategySpec: strategy, }, CustomResourceDefinitions: v1alpha1.CustomResourceDefinitions{ Owned: []v1alpha1.CRDDescription{ @@ -2523,10 +2519,9 @@ func TestUpdateCSVInPlace(t *testing.T) { var five int32 = 5 strategyNew.DeploymentSpecs[0].Spec.Replicas = &five - strategyNewRaw, err := json.Marshal(strategyNew) require.NoError(t, err) - fetchedCSV.Spec.InstallStrategy.StrategySpecRaw = strategyNewRaw + fetchedCSV.Spec.InstallStrategy.StrategySpec = strategyNew // Update CSV directly _, err = crc.OperatorsV1alpha1().ClusterServiceVersions(testNamespace).Update(fetchedCSV) @@ -2599,15 +2594,15 @@ func TestUpdateCSVMultipleVersionCRD(t *testing.T) { defer cleanupCRD() // create initial deployment strategy - strategy := install.StrategyDetailsDeployment{ - DeploymentSpecs: []install.StrategyDeploymentSpec{ + strategy := v1alpha1.StrategyDetailsDeployment{ + DeploymentSpecs: []v1alpha1.StrategyDeploymentSpec{ { Name: genName("dep1-"), Spec: newNginxDeployment(genName("nginx-")), }, }, } - strategyRaw, err := json.Marshal(strategy) + require.NoError(t, err) // First CSV with owning CRD v1alpha1 @@ -2640,8 +2635,8 @@ func TestUpdateCSVMultipleVersionCRD(t *testing.T) { }, }, InstallStrategy: v1alpha1.NamedInstallStrategy{ - StrategyName: install.InstallStrategyNameDeployment, - StrategySpecRaw: strategyRaw, + StrategyName: v1alpha1.InstallStrategyNameDeployment, + StrategySpec: strategy, }, CustomResourceDefinitions: v1alpha1.CustomResourceDefinitions{ Owned: []v1alpha1.CRDDescription{ @@ -2671,15 +2666,15 @@ func TestUpdateCSVMultipleVersionCRD(t *testing.T) { require.NotNil(t, dep) // Create updated deployment strategy - strategyNew := install.StrategyDetailsDeployment{ - DeploymentSpecs: []install.StrategyDeploymentSpec{ + strategyNew := v1alpha1.StrategyDetailsDeployment{ + DeploymentSpecs: []v1alpha1.StrategyDeploymentSpec{ { Name: genName("dep2-"), Spec: newNginxDeployment(genName("nginx-")), }, }, } - strategyNewRaw, err := json.Marshal(strategyNew) + require.NoError(t, err) // Second CSV with owning CRD v1alpha1 and v1alpha2 @@ -2712,8 +2707,8 @@ func TestUpdateCSVMultipleVersionCRD(t *testing.T) { }, }, InstallStrategy: v1alpha1.NamedInstallStrategy{ - StrategyName: install.InstallStrategyNameDeployment, - StrategySpecRaw: strategyNewRaw, + StrategyName: v1alpha1.InstallStrategyNameDeployment, + StrategySpec: strategyNew, }, CustomResourceDefinitions: v1alpha1.CustomResourceDefinitions{ Owned: []v1alpha1.CRDDescription{ @@ -2757,15 +2752,14 @@ func TestUpdateCSVMultipleVersionCRD(t *testing.T) { require.NoError(t, err) // Create updated deployment strategy - strategyNew2 := install.StrategyDetailsDeployment{ - DeploymentSpecs: []install.StrategyDeploymentSpec{ + strategyNew2 := v1alpha1.StrategyDetailsDeployment{ + DeploymentSpecs: []v1alpha1.StrategyDeploymentSpec{ { Name: genName("dep3-"), Spec: newNginxDeployment(genName("nginx-")), }, }, } - strategyNewRaw2, err := json.Marshal(strategyNew2) require.NoError(t, err) // Third CSV with owning CRD v1alpha2 @@ -2798,8 +2792,8 @@ func TestUpdateCSVMultipleVersionCRD(t *testing.T) { }, }, InstallStrategy: v1alpha1.NamedInstallStrategy{ - StrategyName: install.InstallStrategyNameDeployment, - StrategySpecRaw: strategyNewRaw2, + StrategyName: v1alpha1.InstallStrategyNameDeployment, + StrategySpec: strategyNew2, }, CustomResourceDefinitions: v1alpha1.CustomResourceDefinitions{ Owned: []v1alpha1.CRDDescription{ @@ -2870,8 +2864,8 @@ func TestUpdateCSVModifyDeploymentName(t *testing.T) { defer cleanupCRD() // create "current" CSV - strategy := install.StrategyDetailsDeployment{ - DeploymentSpecs: []install.StrategyDeploymentSpec{ + strategy := v1alpha1.StrategyDetailsDeployment{ + DeploymentSpecs: []v1alpha1.StrategyDeploymentSpec{ { Name: genName("dep-"), Spec: newNginxDeployment(genName("nginx-")), @@ -2882,7 +2876,7 @@ func TestUpdateCSVModifyDeploymentName(t *testing.T) { }, }, } - strategyRaw, err := json.Marshal(strategy) + require.NoError(t, err) csv := v1alpha1.ClusterServiceVersion{ @@ -2913,8 +2907,8 @@ func TestUpdateCSVModifyDeploymentName(t *testing.T) { }, }, InstallStrategy: v1alpha1.NamedInstallStrategy{ - StrategyName: install.InstallStrategyNameDeployment, - StrategySpecRaw: strategyRaw, + StrategyName: v1alpha1.InstallStrategyNameDeployment, + StrategySpec: strategy, }, CustomResourceDefinitions: v1alpha1.CustomResourceDefinitions{ Owned: []v1alpha1.CRDDescription{ @@ -2947,8 +2941,8 @@ func TestUpdateCSVModifyDeploymentName(t *testing.T) { require.NotNil(t, dep2) // Create "updated" CSV - strategyNew := install.StrategyDetailsDeployment{ - DeploymentSpecs: []install.StrategyDeploymentSpec{ + strategyNew := v1alpha1.StrategyDetailsDeployment{ + DeploymentSpecs: []v1alpha1.StrategyDeploymentSpec{ { Name: genName("dep3-"), Spec: newNginxDeployment(genName("nginx3-")), @@ -2959,7 +2953,7 @@ func TestUpdateCSVModifyDeploymentName(t *testing.T) { }, }, } - strategyNewRaw, err := json.Marshal(strategyNew) + require.NoError(t, err) // Fetch the current csv @@ -2967,7 +2961,7 @@ func TestUpdateCSVModifyDeploymentName(t *testing.T) { require.NoError(t, err) // Update csv with same strategy with different deployment's name - fetchedCSV.Spec.InstallStrategy.StrategySpecRaw = strategyNewRaw + fetchedCSV.Spec.InstallStrategy.StrategySpec = strategyNew // Update the current csv with the new csv _, err = crc.OperatorsV1alpha1().ClusterServiceVersions(testNamespace).Update(fetchedCSV) @@ -3006,7 +3000,7 @@ func TestCreateCSVRequirementsEvents(t *testing.T) { _, err := c.CreateServiceAccount(&sa) require.NoError(t, err, "could not create ServiceAccount") - permissions := []install.StrategyDeploymentPermissions{ + permissions := []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: sa.GetName(), Rules: []rbacv1.PolicyRule{ @@ -3024,7 +3018,7 @@ func TestCreateCSVRequirementsEvents(t *testing.T) { }, } - clusterPermissions := []install.StrategyDeploymentPermissions{ + clusterPermissions := []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: sa.GetName(), Rules: []rbacv1.PolicyRule{ @@ -3242,15 +3236,15 @@ func TestCSVStatusInvalidCSV(t *testing.T) { defer cleanupCRD() // create CSV - strategy := install.StrategyDetailsDeployment{ - DeploymentSpecs: []install.StrategyDeploymentSpec{ + strategy := v1alpha1.StrategyDetailsDeployment{ + DeploymentSpecs: []v1alpha1.StrategyDeploymentSpec{ { Name: genName("dep-"), Spec: newNginxDeployment(genName("nginx-")), }, }, } - strategyRaw, err := json.Marshal(strategy) + require.NoError(t, err) csv := v1alpha1.ClusterServiceVersion{ @@ -3281,8 +3275,8 @@ func TestCSVStatusInvalidCSV(t *testing.T) { }, }, InstallStrategy: v1alpha1.NamedInstallStrategy{ - StrategyName: install.InstallStrategyNameDeployment, - StrategySpecRaw: strategyRaw, + StrategyName: v1alpha1.InstallStrategyNameDeployment, + StrategySpec: strategy, }, CustomResourceDefinitions: v1alpha1.CustomResourceDefinitions{ Owned: []v1alpha1.CRDDescription{ diff --git a/test/e2e/installplan_e2e_test.go b/test/e2e/installplan_e2e_test.go index f907b84927..4ba345033e 100644 --- a/test/e2e/installplan_e2e_test.go +++ b/test/e2e/installplan_e2e_test.go @@ -1,7 +1,6 @@ package e2e import ( - "encoding/json" "errors" "fmt" "strings" @@ -25,7 +24,6 @@ import ( "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1" "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned" - "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil" @@ -108,10 +106,10 @@ func fetchInstallPlanWithNamespace(t *testing.T, c versioned.Interface, name str return fetchedInstallPlan, err } -func newNginxInstallStrategy(name string, permissions []install.StrategyDeploymentPermissions, clusterPermissions []install.StrategyDeploymentPermissions) v1alpha1.NamedInstallStrategy { +func newNginxInstallStrategy(name string, permissions []v1alpha1.StrategyDeploymentPermissions, clusterPermissions []v1alpha1.StrategyDeploymentPermissions) v1alpha1.NamedInstallStrategy { // Create an nginx details deployment - details := install.StrategyDetailsDeployment{ - DeploymentSpecs: []install.StrategyDeploymentSpec{ + details := v1alpha1.StrategyDetailsDeployment{ + DeploymentSpecs: []v1alpha1.StrategyDeploymentSpec{ { Name: name, Spec: appsv1.DeploymentSpec{ @@ -138,10 +136,9 @@ func newNginxInstallStrategy(name string, permissions []install.StrategyDeployme Permissions: permissions, ClusterPermissions: clusterPermissions, } - detailsRaw, _ := json.Marshal(details) namedStrategy := v1alpha1.NamedInstallStrategy{ - StrategyName: install.InstallStrategyNameDeployment, - StrategySpecRaw: detailsRaw, + StrategyName: v1alpha1.InstallStrategyNameDeployment, + StrategySpec: details, } return namedStrategy @@ -1333,7 +1330,7 @@ func TestUpdateCatalogForSubscription(t *testing.T) { // Generate permissions serviceAccountName := genName("nginx-sa") - permissions := []install.StrategyDeploymentPermissions{ + permissions := []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: serviceAccountName, Rules: []rbacv1.PolicyRule{ @@ -1346,7 +1343,7 @@ func TestUpdateCatalogForSubscription(t *testing.T) { }, } // Generate permissions - clusterPermissions := []install.StrategyDeploymentPermissions{ + clusterPermissions := []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: serviceAccountName, Rules: []rbacv1.PolicyRule{ @@ -1401,7 +1398,7 @@ func TestUpdateCatalogForSubscription(t *testing.T) { require.NoError(t, err) // Update CatalogSource with a new CSV with more permissions - updatedPermissions := []install.StrategyDeploymentPermissions{ + updatedPermissions := []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: serviceAccountName, Rules: []rbacv1.PolicyRule{ @@ -1418,7 +1415,7 @@ func TestUpdateCatalogForSubscription(t *testing.T) { }, }, } - updatedClusterPermissions := []install.StrategyDeploymentPermissions{ + updatedClusterPermissions := []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: serviceAccountName, Rules: []rbacv1.PolicyRule{ @@ -1508,7 +1505,7 @@ func TestUpdateCatalogForSubscription(t *testing.T) { // Generate permissions serviceAccountName := genName("nginx-sa") - permissions := []install.StrategyDeploymentPermissions{ + permissions := []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: serviceAccountName, Rules: []rbacv1.PolicyRule{ @@ -1526,7 +1523,7 @@ func TestUpdateCatalogForSubscription(t *testing.T) { }, } // Generate permissions - clusterPermissions := []install.StrategyDeploymentPermissions{ + clusterPermissions := []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: serviceAccountName, Rules: []rbacv1.PolicyRule{ @@ -1586,7 +1583,7 @@ func TestUpdateCatalogForSubscription(t *testing.T) { require.NoError(t, err) // Update CatalogSource with a new CSV with more permissions - updatedPermissions := []install.StrategyDeploymentPermissions{ + updatedPermissions := []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: serviceAccountName, Rules: []rbacv1.PolicyRule{ @@ -1598,7 +1595,7 @@ func TestUpdateCatalogForSubscription(t *testing.T) { }, }, } - updatedClusterPermissions := []install.StrategyDeploymentPermissions{ + updatedClusterPermissions := []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: serviceAccountName, Rules: []rbacv1.PolicyRule{ @@ -1718,7 +1715,7 @@ func TestUpdateCatalogForSubscription(t *testing.T) { // Generate permissions serviceAccountName := genName("nginx-sa") - permissions := []install.StrategyDeploymentPermissions{ + permissions := []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: serviceAccountName, Rules: []rbacv1.PolicyRule{ @@ -1731,7 +1728,7 @@ func TestUpdateCatalogForSubscription(t *testing.T) { }, } // Generate permissions - clusterPermissions := []install.StrategyDeploymentPermissions{ + clusterPermissions := []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: serviceAccountName, Rules: []rbacv1.PolicyRule{ @@ -1787,8 +1784,8 @@ func TestUpdateCatalogForSubscription(t *testing.T) { require.NoError(t, err) modifiedEnv := []corev1.EnvVar{{Name: "EXAMPLE", Value: "value"}} - modifiedDetails := install.StrategyDetailsDeployment{ - DeploymentSpecs: []install.StrategyDeploymentSpec{ + modifiedDetails := v1alpha1.StrategyDetailsDeployment{ + DeploymentSpecs: []v1alpha1.StrategyDeploymentSpec{ { Name: deploymentName, Spec: appsv1.DeploymentSpec{ @@ -1816,10 +1813,9 @@ func TestUpdateCatalogForSubscription(t *testing.T) { Permissions: permissions, ClusterPermissions: clusterPermissions, } - detailsRaw, _ := json.Marshal(modifiedDetails) csv.Spec.InstallStrategy = v1alpha1.NamedInstallStrategy{ - StrategyName: install.InstallStrategyNameDeployment, - StrategySpecRaw: detailsRaw, + StrategyName: v1alpha1.InstallStrategyNameDeployment, + StrategySpec: modifiedDetails, } _, err = crc.OperatorsV1alpha1().ClusterServiceVersions(testNamespace).Update(csv) require.NoError(t, err) @@ -2241,7 +2237,7 @@ func TestCreateInstallPlanWithPermissions(t *testing.T) { // Generate permissions serviceAccountName := genName("nginx-sa") - permissions := []install.StrategyDeploymentPermissions{ + permissions := []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: serviceAccountName, Rules: []rbacv1.PolicyRule{ @@ -2254,7 +2250,7 @@ func TestCreateInstallPlanWithPermissions(t *testing.T) { }, } // Generate permissions - clusterPermissions := []install.StrategyDeploymentPermissions{ + clusterPermissions := []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: serviceAccountName, Rules: []rbacv1.PolicyRule{ diff --git a/test/e2e/metrics_e2e_test.go b/test/e2e/metrics_e2e_test.go index bd77107819..0315529438 100644 --- a/test/e2e/metrics_e2e_test.go +++ b/test/e2e/metrics_e2e_test.go @@ -11,7 +11,6 @@ import ( "k8s.io/apimachinery/pkg/util/net" "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1" - "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient" ) @@ -30,8 +29,8 @@ func TestMetricsEndpoint(t *testing.T) { }, Spec: v1alpha1.ClusterServiceVersionSpec{ InstallStrategy: v1alpha1.NamedInstallStrategy{ - StrategyName: install.InstallStrategyNameDeployment, - StrategySpecRaw: strategyRaw, + StrategyName: v1alpha1.InstallStrategyNameDeployment, + StrategySpec: strategy, }, }, } diff --git a/test/e2e/operator_groups_e2e_test.go b/test/e2e/operator_groups_e2e_test.go index 6e274fc601..5be0b50db7 100644 --- a/test/e2e/operator_groups_e2e_test.go +++ b/test/e2e/operator_groups_e2e_test.go @@ -1,7 +1,6 @@ package e2e import ( - "encoding/json" "fmt" "strings" "testing" @@ -161,7 +160,7 @@ func TestOperatorGroup(t *testing.T) { // Generate permissions serviceAccountName := genName("nginx-sa") - permissions := []install.StrategyDeploymentPermissions{ + permissions := []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: serviceAccountName, Rules: []rbacv1.PolicyRule{ @@ -529,15 +528,15 @@ func TestOperatorGroupRoleAggregation(t *testing.T) { mockKinds := []string{"fez", "fedora"} mockNames := []string{"fezs", "fedoras"} depSpec := newMockExtServerDeployment(depName, mockGroupVersion, mockKinds) - strategy := install.StrategyDetailsDeployment{ - DeploymentSpecs: []install.StrategyDeploymentSpec{ + strategy := v1alpha1.StrategyDetailsDeployment{ + DeploymentSpecs: []v1alpha1.StrategyDeploymentSpec{ { Name: depName, Spec: depSpec, }, }, } - strategyRaw, err := json.Marshal(strategy) + owned := make([]v1alpha1.APIServiceDescription, len(mockKinds)) for i, kind := range mockKinds { owned[i] = v1alpha1.APIServiceDescription{ @@ -574,8 +573,8 @@ func TestOperatorGroupRoleAggregation(t *testing.T) { }, }, InstallStrategy: v1alpha1.NamedInstallStrategy{ - StrategyName: install.InstallStrategyNameDeployment, - StrategySpecRaw: strategyRaw, + StrategyName: v1alpha1.InstallStrategyNameDeployment, + StrategySpec: strategy, }, APIServiceDefinitions: v1alpha1.APIServiceDefinitions{ Owned: owned, @@ -1403,7 +1402,7 @@ func TestCSVCopyWatchingAllNamespaces(t *testing.T) { t.Log("Creating CSV") // Generate permissions serviceAccountName := genName("nginx-sa") - permissions := []install.StrategyDeploymentPermissions{ + permissions := []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: serviceAccountName, Rules: []rbacv1.PolicyRule{ diff --git a/test/e2e/subscription_e2e_test.go b/test/e2e/subscription_e2e_test.go index 02e5e28fa0..c3190fa716 100644 --- a/test/e2e/subscription_e2e_test.go +++ b/test/e2e/subscription_e2e_test.go @@ -62,8 +62,8 @@ var ( APIVersion: v1alpha1.GroupVersion, } - strategy = install.StrategyDetailsDeployment{ - DeploymentSpecs: []install.StrategyDeploymentSpec{ + strategy = v1alpha1.StrategyDetailsDeployment{ + DeploymentSpecs: []v1alpha1.StrategyDeploymentSpec{ { Name: genName("dep-"), Spec: newNginxDeployment(genName("nginx-")), @@ -72,8 +72,8 @@ var ( } strategyRaw, _ = json.Marshal(strategy) installStrategy = v1alpha1.NamedInstallStrategy{ - StrategyName: install.InstallStrategyNameDeployment, - StrategySpecRaw: strategyRaw, + StrategyName: v1alpha1.InstallStrategyNameDeployment, + StrategySpec: strategy, } outdatedCSV = v1alpha1.ClusterServiceVersion{ TypeMeta: csvType, @@ -195,8 +195,8 @@ var ( } csvList = []v1alpha1.ClusterServiceVersion{outdatedCSV, stableCSV, betaCSV, alphaCSV} - strategyNew = install.StrategyDetailsDeployment{ - DeploymentSpecs: []install.StrategyDeploymentSpec{ + strategyNew = v1alpha1.StrategyDetailsDeployment{ + DeploymentSpecs: []v1alpha1.StrategyDeploymentSpec{ { Name: genName("dep-"), Spec: appsv1.DeploymentSpec{ @@ -245,12 +245,8 @@ var ( ) func init() { - strategyNewRaw, err := json.Marshal(strategyNew) - if err != nil { - panic(err) - } for i := 0; i < len(csvList); i++ { - csvList[i].Spec.InstallStrategy.StrategySpecRaw = strategyNewRaw + csvList[i].Spec.InstallStrategy.StrategySpec = strategyNew } manifestsRaw, err := yaml.Marshal(dummyManifest) @@ -1412,7 +1408,7 @@ func checkDeploymentWithPodConfiguration(t *testing.T, client operatorclient.Cli strategy, err := resolver.UnmarshalStrategy(csv.Spec.InstallStrategy) require.NoError(t, err) - strategyDetailsDeployment, ok := strategy.(*install.StrategyDetailsDeployment) + strategyDetailsDeployment, ok := strategy.(*v1alpha1.StrategyDetailsDeployment) require.Truef(t, ok, "could not cast install strategy as type %T", strategyDetailsDeployment) findEnvVar := func(envVar []corev1.EnvVar, name string) (foundEnvVar *corev1.EnvVar, found bool) { diff --git a/test/e2e/user_defined_sa_test.go b/test/e2e/user_defined_sa_test.go index 40b74850b4..386325a7a0 100644 --- a/test/e2e/user_defined_sa_test.go +++ b/test/e2e/user_defined_sa_test.go @@ -8,7 +8,6 @@ import ( v1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1" "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1" "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned" - "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient" "github.com/stretchr/testify/assert" @@ -250,7 +249,7 @@ func newOperatorGroupWithServiceAccount(t *testing.T, client versioned.Interface return } -func newCatalogSource(t *testing.T, kubeclient operatorclient.ClientInterface, crclient versioned.Interface, prefix, namespace string, permissions []install.StrategyDeploymentPermissions) (catsrc *v1alpha1.CatalogSource, subscriptionSpec *v1alpha1.SubscriptionSpec, cleanup cleanupFunc) { +func newCatalogSource(t *testing.T, kubeclient operatorclient.ClientInterface, crclient versioned.Interface, prefix, namespace string, permissions []v1alpha1.StrategyDeploymentPermissions) (catsrc *v1alpha1.CatalogSource, subscriptionSpec *v1alpha1.SubscriptionSpec, cleanup cleanupFunc) { crdPlural := genName("ins") crdName := crdPlural + ".cluster.com" @@ -310,8 +309,7 @@ func newCatalogSource(t *testing.T, kubeclient operatorclient.ClientInterface, c return } - -func newCatalogSourceWithDependencies(t *testing.T, kubeclient operatorclient.ClientInterface, crclient versioned.Interface, prefix, namespace string, permissions []install.StrategyDeploymentPermissions) (catsrc *v1alpha1.CatalogSource, subscriptionSpec *v1alpha1.SubscriptionSpec, cleanup cleanupFunc) { +func newCatalogSourceWithDependencies(t *testing.T, kubeclient operatorclient.ClientInterface, crclient versioned.Interface, prefix, namespace string, permissions []v1alpha1.StrategyDeploymentPermissions) (catsrc *v1alpha1.CatalogSource, subscriptionSpec *v1alpha1.SubscriptionSpec, cleanup cleanupFunc) { crdPlural := genName("ins") crdName := crdPlural + ".cluster.com" @@ -391,10 +389,10 @@ func mustHaveCondition(t *testing.T, ip *v1alpha1.InstallPlan, conditionType v1a return } -func deploymentPermissions(t *testing.T) []install.StrategyDeploymentPermissions { +func deploymentPermissions(t *testing.T) []v1alpha1.StrategyDeploymentPermissions { // Generate permissions serviceAccountName := genName("nginx-sa-") - permissions := []install.StrategyDeploymentPermissions{ + permissions := []v1alpha1.StrategyDeploymentPermissions{ { ServiceAccountName: serviceAccountName, Rules: []rbacv1.PolicyRule{ From de4c8e1e56093fa425aaec2b77ec75beb9653d68 Mon Sep 17 00:00:00 2001 From: awgreene Date: Tue, 3 Dec 2019 15:59:35 -0500 Subject: [PATCH 2/4] Implement controller-gen This commit updates OLM's Makefile to use the controller-gen tool to generate the structural schemas for its CRDs. --- Makefile | 12 +- go.mod | 3 +- go.sum | 24 + tools.go | 2 + vendor/github.com/fatih/color/.travis.yml | 5 + vendor/github.com/fatih/color/Gopkg.lock | 27 + vendor/github.com/fatih/color/Gopkg.toml | 30 + vendor/github.com/fatih/color/LICENSE.md | 20 + vendor/github.com/fatih/color/README.md | 179 + vendor/github.com/fatih/color/color.go | 603 ++++ vendor/github.com/fatih/color/doc.go | 133 + .../bson/bson_corpus_spec_test_generator.go | 294 ++ vendor/github.com/gobuffalo/flect/.gitignore | 29 + .../gobuffalo/flect/.gometalinter.json | 3 + vendor/github.com/gobuffalo/flect/.travis.yml | 36 + vendor/github.com/gobuffalo/flect/LICENSE | 21 + vendor/github.com/gobuffalo/flect/Makefile | 61 + vendor/github.com/gobuffalo/flect/README.md | 36 + .../github.com/gobuffalo/flect/SHOULDERS.md | 10 + vendor/github.com/gobuffalo/flect/acronyms.go | 152 + .../gobuffalo/flect/azure-pipelines.yml | 71 + .../gobuffalo/flect/azure-tests.yml | 19 + vendor/github.com/gobuffalo/flect/camelize.go | 48 + .../github.com/gobuffalo/flect/capitalize.go | 27 + .../github.com/gobuffalo/flect/custom_data.go | 83 + .../github.com/gobuffalo/flect/dasherize.go | 34 + vendor/github.com/gobuffalo/flect/flect.go | 43 + vendor/github.com/gobuffalo/flect/go.mod | 8 + vendor/github.com/gobuffalo/flect/go.sum | 9 + vendor/github.com/gobuffalo/flect/humanize.go | 31 + vendor/github.com/gobuffalo/flect/ident.go | 106 + .../github.com/gobuffalo/flect/lower_upper.go | 13 + .../github.com/gobuffalo/flect/ordinalize.go | 43 + .../github.com/gobuffalo/flect/pascalize.go | 25 + .../gobuffalo/flect/plural_rules.go | 240 ++ .../github.com/gobuffalo/flect/pluralize.go | 49 + vendor/github.com/gobuffalo/flect/rule.go | 10 + .../gobuffalo/flect/singular_rules.go | 122 + .../github.com/gobuffalo/flect/singularize.go | 44 + vendor/github.com/gobuffalo/flect/titleize.go | 30 + .../github.com/gobuffalo/flect/underscore.go | 34 + vendor/github.com/gobuffalo/flect/version.go | 4 + .../github.com/mattn/go-colorable/.travis.yml | 9 + vendor/github.com/mattn/go-colorable/LICENSE | 21 + .../github.com/mattn/go-colorable/README.md | 48 + .../mattn/go-colorable/colorable_appengine.go | 29 + .../mattn/go-colorable/colorable_others.go | 30 + .../mattn/go-colorable/colorable_windows.go | 980 ++++++ vendor/github.com/mattn/go-colorable/go.mod | 3 + vendor/github.com/mattn/go-colorable/go.sum | 4 + .../mattn/go-colorable/noncolorable.go | 55 + vendor/github.com/mattn/go-isatty/.travis.yml | 13 + vendor/github.com/mattn/go-isatty/LICENSE | 9 + vendor/github.com/mattn/go-isatty/README.md | 50 + vendor/github.com/mattn/go-isatty/doc.go | 2 + vendor/github.com/mattn/go-isatty/go.mod | 3 + vendor/github.com/mattn/go-isatty/go.sum | 2 + .../mattn/go-isatty/isatty_android.go | 23 + .../github.com/mattn/go-isatty/isatty_bsd.go | 24 + .../mattn/go-isatty/isatty_others.go | 15 + .../mattn/go-isatty/isatty_solaris.go | 22 + .../mattn/go-isatty/isatty_tcgets.go | 19 + .../mattn/go-isatty/isatty_windows.go | 94 + .../github.com/mikefarah/yaml/v2/.travis.yml | 12 + vendor/github.com/mikefarah/yaml/v2/LICENSE | 201 ++ .../mikefarah/yaml/v2/LICENSE.libyaml | 31 + vendor/github.com/mikefarah/yaml/v2/NOTICE | 13 + vendor/github.com/mikefarah/yaml/v2/README.md | 133 + vendor/github.com/mikefarah/yaml/v2/apic.go | 739 ++++ vendor/github.com/mikefarah/yaml/v2/decode.go | 776 +++++ .../github.com/mikefarah/yaml/v2/emitterc.go | 1685 ++++++++++ vendor/github.com/mikefarah/yaml/v2/encode.go | 362 ++ vendor/github.com/mikefarah/yaml/v2/go.mod | 5 + .../github.com/mikefarah/yaml/v2/parserc.go | 1095 ++++++ .../github.com/mikefarah/yaml/v2/readerc.go | 412 +++ .../github.com/mikefarah/yaml/v2/resolve.go | 258 ++ .../github.com/mikefarah/yaml/v2/scannerc.go | 2696 +++++++++++++++ vendor/github.com/mikefarah/yaml/v2/sorter.go | 113 + .../github.com/mikefarah/yaml/v2/writerc.go | 26 + vendor/github.com/mikefarah/yaml/v2/yaml.go | 466 +++ vendor/github.com/mikefarah/yaml/v2/yamlh.go | 738 ++++ .../mikefarah/yaml/v2/yamlprivateh.go | 173 + .../github.com/mikefarah/yq/v2/.dockerignore | 1 + vendor/github.com/mikefarah/yq/v2/.gitignore | 39 + vendor/github.com/mikefarah/yq/v2/.travis.yml | 6 + vendor/github.com/mikefarah/yq/v2/Dockerfile | 23 + .../github.com/mikefarah/yq/v2/Dockerfile.dev | 30 + vendor/github.com/mikefarah/yq/v2/LICENSE | 19 + vendor/github.com/mikefarah/yq/v2/Makefile | 114 + .../mikefarah/yq/v2/Makefile.variables | 38 + vendor/github.com/mikefarah/yq/v2/README.md | 127 + .../mikefarah/yq/v2/data_navigator.go | 349 ++ vendor/github.com/mikefarah/yq/v2/go.mod | 12 + vendor/github.com/mikefarah/yq/v2/go.sum | 50 + .../mikefarah/yq/v2/json_converter.go | 44 + vendor/github.com/mikefarah/yq/v2/merge.go | 12 + vendor/github.com/mikefarah/yq/v2/mkdocs.yml | 28 + .../github.com/mikefarah/yq/v2/path_parser.go | 55 + .../mikefarah/yq/v2/release_instructions.txt | 46 + vendor/github.com/mikefarah/yq/v2/test.yml | 2 + vendor/github.com/mikefarah/yq/v2/version.go | 49 + vendor/github.com/mikefarah/yq/v2/yq.go | 709 ++++ vendor/golang.org/x/sync/AUTHORS | 3 - vendor/golang.org/x/sync/CONTRIBUTORS | 3 - vendor/golang.org/x/sync/PATENTS | 22 - vendor/golang.org/x/sync/errgroup/errgroup.go | 66 - vendor/golang.org/x/sys/unix/mkasm_darwin.go | 61 + vendor/golang.org/x/sys/unix/mkpost.go | 122 + vendor/golang.org/x/sys/unix/mksyscall.go | 407 +++ .../x/sys/unix/mksyscall_aix_ppc.go | 415 +++ .../x/sys/unix/mksyscall_aix_ppc64.go | 614 ++++ .../x/sys/unix/mksyscall_solaris.go | 335 ++ .../golang.org/x/sys/unix/mksysctl_openbsd.go | 355 ++ vendor/golang.org/x/sys/unix/mksysnum.go | 190 ++ vendor/golang.org/x/sys/unix/types_aix.go | 237 ++ vendor/golang.org/x/sys/unix/types_darwin.go | 283 ++ .../golang.org/x/sys/unix/types_dragonfly.go | 263 ++ vendor/golang.org/x/sys/unix/types_freebsd.go | 400 +++ vendor/golang.org/x/sys/unix/types_netbsd.go | 290 ++ vendor/golang.org/x/sys/unix/types_openbsd.go | 283 ++ vendor/golang.org/x/sys/unix/types_solaris.go | 266 ++ vendor/golang.org/x/text/unicode/bidi/gen.go | 133 + .../x/text/unicode/bidi/gen_ranges.go | 57 + .../x/text/unicode/bidi/gen_trieval.go | 64 + .../x/text/unicode/norm/maketables.go | 986 ++++++ .../golang.org/x/text/unicode/norm/triegen.go | 117 + vendor/golang.org/x/text/width/gen.go | 115 + vendor/golang.org/x/text/width/gen_common.go | 96 + vendor/golang.org/x/text/width/gen_trieval.go | 34 + .../x/tools/go/gcexportdata/gcexportdata.go | 2 +- .../x/tools/go/gcexportdata/main.go | 99 + .../x/tools/go/internal/gcimporter/bimport.go | 11 +- .../x/tools/go/internal/gcimporter/iexport.go | 34 +- .../x/tools/go/internal/gcimporter/iimport.go | 68 +- .../tools/go/internal/packagesdriver/sizes.go | 27 +- .../x/tools/go/packages/external.go | 37 +- .../golang.org/x/tools/go/packages/golist.go | 353 +- .../x/tools/go/packages/golist_overlay.go | 61 +- .../x/tools/go/packages/packages.go | 116 +- .../x/tools/internal/gopathwalk/walk.go | 42 +- .../x/tools/internal/imports/fix.go | 360 +- .../x/tools/internal/imports/imports.go | 103 +- .../x/tools/internal/imports/mkindex.go | 173 + .../x/tools/internal/imports/mkstdlib.go | 128 + .../x/tools/internal/imports/mod.go | 562 +++- .../x/tools/internal/imports/mod_cache.go | 123 + .../x/tools/internal/imports/sortimports.go | 47 + .../x/tools/internal/imports/zstdlib.go | 116 +- .../golang.org/x/tools/internal/span/parse.go | 100 + .../golang.org/x/tools/internal/span/span.go | 285 ++ .../golang.org/x/tools/internal/span/token.go | 151 + .../x/tools/internal/span/token111.go | 39 + .../x/tools/internal/span/token112.go | 16 + .../golang.org/x/tools/internal/span/uri.go | 152 + .../golang.org/x/tools/internal/span/utf16.go | 94 + vendor/gopkg.in/imdario/mergo.v0/.gitignore | 33 + vendor/gopkg.in/imdario/mergo.v0/.travis.yml | 7 + .../imdario/mergo.v0/CODE_OF_CONDUCT.md | 46 + vendor/gopkg.in/imdario/mergo.v0/LICENSE | 28 + vendor/gopkg.in/imdario/mergo.v0/README.md | 238 ++ vendor/gopkg.in/imdario/mergo.v0/doc.go | 44 + vendor/gopkg.in/imdario/mergo.v0/map.go | 175 + vendor/gopkg.in/imdario/mergo.v0/merge.go | 255 ++ vendor/gopkg.in/imdario/mergo.v0/mergo.go | 97 + vendor/gopkg.in/op/go-logging.v1/.travis.yml | 6 + vendor/gopkg.in/op/go-logging.v1/CONTRIBUTORS | 5 + .../op/go-logging.v1}/LICENSE | 2 +- vendor/gopkg.in/op/go-logging.v1/README.md | 89 + vendor/gopkg.in/op/go-logging.v1/backend.go | 39 + vendor/gopkg.in/op/go-logging.v1/format.go | 400 +++ vendor/gopkg.in/op/go-logging.v1/level.go | 128 + vendor/gopkg.in/op/go-logging.v1/log_nix.go | 109 + .../gopkg.in/op/go-logging.v1/log_windows.go | 107 + vendor/gopkg.in/op/go-logging.v1/logger.go | 249 ++ vendor/gopkg.in/op/go-logging.v1/memory.go | 237 ++ vendor/gopkg.in/op/go-logging.v1/multi.go | 65 + vendor/gopkg.in/op/go-logging.v1/syslog.go | 53 + .../op/go-logging.v1/syslog_fallback.go | 28 + vendor/gopkg.in/yaml.v3/.travis.yml | 15 + vendor/gopkg.in/yaml.v3/LICENSE | 50 + vendor/gopkg.in/yaml.v3/NOTICE | 13 + vendor/gopkg.in/yaml.v3/README.md | 150 + vendor/gopkg.in/yaml.v3/apic.go | 746 +++++ vendor/gopkg.in/yaml.v3/decode.go | 902 +++++ vendor/gopkg.in/yaml.v3/emitterc.go | 1992 +++++++++++ vendor/gopkg.in/yaml.v3/encode.go | 542 +++ vendor/gopkg.in/yaml.v3/go.mod | 5 + vendor/gopkg.in/yaml.v3/parserc.go | 1210 +++++++ vendor/gopkg.in/yaml.v3/readerc.go | 434 +++ vendor/gopkg.in/yaml.v3/resolve.go | 326 ++ vendor/gopkg.in/yaml.v3/scannerc.go | 2974 +++++++++++++++++ vendor/gopkg.in/yaml.v3/sorter.go | 134 + vendor/gopkg.in/yaml.v3/writerc.go | 48 + vendor/gopkg.in/yaml.v3/yaml.go | 662 ++++ vendor/gopkg.in/yaml.v3/yamlh.go | 803 +++++ vendor/gopkg.in/yaml.v3/yamlprivateh.go | 198 ++ vendor/modules.txt | 634 ++-- vendor/sigs.k8s.io/controller-tools/LICENSE | 201 ++ .../cmd/controller-gen/main.go | 263 ++ .../controller-tools/pkg/crd/conv.go | 122 + .../controller-tools/pkg/crd/desc_visitor.go | 78 + .../controller-tools/pkg/crd/doc.go | 63 + .../controller-tools/pkg/crd/flatten.go | 441 +++ .../controller-tools/pkg/crd/gen.go | 264 ++ .../controller-tools/pkg/crd/known_types.go | 115 + .../controller-tools/pkg/crd/markers/crd.go | 293 ++ .../controller-tools/pkg/crd/markers/doc.go | 46 + .../pkg/crd/markers/package.go | 40 + .../pkg/crd/markers/register.go | 83 + .../pkg/crd/markers/validation.go | 350 ++ .../crd/markers/zz_generated.markerhelp.go | 353 ++ .../controller-tools/pkg/crd/parser.go | 243 ++ .../controller-tools/pkg/crd/schema.go | 419 +++ .../pkg/crd/schema_visitor.go | 104 + .../controller-tools/pkg/crd/spec.go | 161 + .../pkg/crd/zz_generated.markerhelp.go | 49 + .../controller-tools/pkg/deepcopy/doc.go | 23 + .../controller-tools/pkg/deepcopy/gen.go | 300 ++ .../controller-tools/pkg/deepcopy/traverse.go | 812 +++++ .../pkg/deepcopy/zz_generated.markerhelp.go | 45 + .../controller-tools/pkg/genall/doc.go | 58 + .../controller-tools/pkg/genall/genall.go | 178 + .../controller-tools/pkg/genall/help/doc.go | 23 + .../pkg/genall/help/pretty/doc.go | 30 + .../pkg/genall/help/pretty/help.go | 171 + .../pkg/genall/help/pretty/print.go | 304 ++ .../pkg/genall/help/pretty/table.go | 64 + .../controller-tools/pkg/genall/help/sort.go | 106 + .../controller-tools/pkg/genall/help/types.go | 215 ++ .../controller-tools/pkg/genall/input.go | 37 + .../controller-tools/pkg/genall/options.go | 192 ++ .../controller-tools/pkg/genall/output.go | 160 + .../pkg/genall/zz_generated.markerhelp.go | 89 + .../controller-tools/pkg/loader/doc.go | 60 + .../controller-tools/pkg/loader/errors.go | 63 + .../controller-tools/pkg/loader/loader.go | 361 ++ .../controller-tools/pkg/loader/paths.go | 32 + .../controller-tools/pkg/loader/refs.go | 254 ++ .../controller-tools/pkg/loader/visit.go | 81 + .../controller-tools/pkg/markers/collect.go | 422 +++ .../controller-tools/pkg/markers/doc.go | 113 + .../controller-tools/pkg/markers/help.go | 81 + .../controller-tools/pkg/markers/parse.go | 908 +++++ .../controller-tools/pkg/markers/reg.go | 153 + .../controller-tools/pkg/markers/regutil.go | 36 + .../controller-tools/pkg/markers/zip.go | 191 ++ .../controller-tools/pkg/rbac/parser.go | 258 ++ .../pkg/rbac/zz_generated.markerhelp.go | 73 + .../controller-tools/pkg/schemapatcher/gen.go | 508 +++ .../schemapatcher/internal/yaml/convert.go | 61 + .../pkg/schemapatcher/internal/yaml/nested.go | 87 + .../pkg/schemapatcher/internal/yaml/set.go | 80 + .../schemapatcher/zz_generated.markerhelp.go | 45 + .../controller-tools/pkg/version/version.go | 49 + .../controller-tools/pkg/webhook/parser.go | 250 ++ .../pkg/webhook/zz_generated.markerhelp.go | 80 + 256 files changed, 49567 insertions(+), 883 deletions(-) create mode 100644 vendor/github.com/fatih/color/.travis.yml create mode 100644 vendor/github.com/fatih/color/Gopkg.lock create mode 100644 vendor/github.com/fatih/color/Gopkg.toml create mode 100644 vendor/github.com/fatih/color/LICENSE.md create mode 100644 vendor/github.com/fatih/color/README.md create mode 100644 vendor/github.com/fatih/color/color.go create mode 100644 vendor/github.com/fatih/color/doc.go create mode 100644 vendor/github.com/globalsign/mgo/bson/bson_corpus_spec_test_generator.go create mode 100644 vendor/github.com/gobuffalo/flect/.gitignore create mode 100644 vendor/github.com/gobuffalo/flect/.gometalinter.json create mode 100644 vendor/github.com/gobuffalo/flect/.travis.yml create mode 100644 vendor/github.com/gobuffalo/flect/LICENSE create mode 100644 vendor/github.com/gobuffalo/flect/Makefile create mode 100644 vendor/github.com/gobuffalo/flect/README.md create mode 100644 vendor/github.com/gobuffalo/flect/SHOULDERS.md create mode 100644 vendor/github.com/gobuffalo/flect/acronyms.go create mode 100644 vendor/github.com/gobuffalo/flect/azure-pipelines.yml create mode 100644 vendor/github.com/gobuffalo/flect/azure-tests.yml create mode 100644 vendor/github.com/gobuffalo/flect/camelize.go create mode 100644 vendor/github.com/gobuffalo/flect/capitalize.go create mode 100644 vendor/github.com/gobuffalo/flect/custom_data.go create mode 100644 vendor/github.com/gobuffalo/flect/dasherize.go create mode 100644 vendor/github.com/gobuffalo/flect/flect.go create mode 100644 vendor/github.com/gobuffalo/flect/go.mod create mode 100644 vendor/github.com/gobuffalo/flect/go.sum create mode 100644 vendor/github.com/gobuffalo/flect/humanize.go create mode 100644 vendor/github.com/gobuffalo/flect/ident.go create mode 100644 vendor/github.com/gobuffalo/flect/lower_upper.go create mode 100644 vendor/github.com/gobuffalo/flect/ordinalize.go create mode 100644 vendor/github.com/gobuffalo/flect/pascalize.go create mode 100644 vendor/github.com/gobuffalo/flect/plural_rules.go create mode 100644 vendor/github.com/gobuffalo/flect/pluralize.go create mode 100644 vendor/github.com/gobuffalo/flect/rule.go create mode 100644 vendor/github.com/gobuffalo/flect/singular_rules.go create mode 100644 vendor/github.com/gobuffalo/flect/singularize.go create mode 100644 vendor/github.com/gobuffalo/flect/titleize.go create mode 100644 vendor/github.com/gobuffalo/flect/underscore.go create mode 100644 vendor/github.com/gobuffalo/flect/version.go create mode 100644 vendor/github.com/mattn/go-colorable/.travis.yml create mode 100644 vendor/github.com/mattn/go-colorable/LICENSE create mode 100644 vendor/github.com/mattn/go-colorable/README.md create mode 100644 vendor/github.com/mattn/go-colorable/colorable_appengine.go create mode 100644 vendor/github.com/mattn/go-colorable/colorable_others.go create mode 100644 vendor/github.com/mattn/go-colorable/colorable_windows.go create mode 100644 vendor/github.com/mattn/go-colorable/go.mod create mode 100644 vendor/github.com/mattn/go-colorable/go.sum create mode 100644 vendor/github.com/mattn/go-colorable/noncolorable.go create mode 100644 vendor/github.com/mattn/go-isatty/.travis.yml create mode 100644 vendor/github.com/mattn/go-isatty/LICENSE create mode 100644 vendor/github.com/mattn/go-isatty/README.md create mode 100644 vendor/github.com/mattn/go-isatty/doc.go create mode 100644 vendor/github.com/mattn/go-isatty/go.mod create mode 100644 vendor/github.com/mattn/go-isatty/go.sum create mode 100644 vendor/github.com/mattn/go-isatty/isatty_android.go create mode 100644 vendor/github.com/mattn/go-isatty/isatty_bsd.go create mode 100644 vendor/github.com/mattn/go-isatty/isatty_others.go create mode 100644 vendor/github.com/mattn/go-isatty/isatty_solaris.go create mode 100644 vendor/github.com/mattn/go-isatty/isatty_tcgets.go create mode 100644 vendor/github.com/mattn/go-isatty/isatty_windows.go create mode 100644 vendor/github.com/mikefarah/yaml/v2/.travis.yml create mode 100644 vendor/github.com/mikefarah/yaml/v2/LICENSE create mode 100644 vendor/github.com/mikefarah/yaml/v2/LICENSE.libyaml create mode 100644 vendor/github.com/mikefarah/yaml/v2/NOTICE create mode 100644 vendor/github.com/mikefarah/yaml/v2/README.md create mode 100644 vendor/github.com/mikefarah/yaml/v2/apic.go create mode 100644 vendor/github.com/mikefarah/yaml/v2/decode.go create mode 100644 vendor/github.com/mikefarah/yaml/v2/emitterc.go create mode 100644 vendor/github.com/mikefarah/yaml/v2/encode.go create mode 100644 vendor/github.com/mikefarah/yaml/v2/go.mod create mode 100644 vendor/github.com/mikefarah/yaml/v2/parserc.go create mode 100644 vendor/github.com/mikefarah/yaml/v2/readerc.go create mode 100644 vendor/github.com/mikefarah/yaml/v2/resolve.go create mode 100644 vendor/github.com/mikefarah/yaml/v2/scannerc.go create mode 100644 vendor/github.com/mikefarah/yaml/v2/sorter.go create mode 100644 vendor/github.com/mikefarah/yaml/v2/writerc.go create mode 100644 vendor/github.com/mikefarah/yaml/v2/yaml.go create mode 100644 vendor/github.com/mikefarah/yaml/v2/yamlh.go create mode 100644 vendor/github.com/mikefarah/yaml/v2/yamlprivateh.go create mode 100644 vendor/github.com/mikefarah/yq/v2/.dockerignore create mode 100644 vendor/github.com/mikefarah/yq/v2/.gitignore create mode 100644 vendor/github.com/mikefarah/yq/v2/.travis.yml create mode 100644 vendor/github.com/mikefarah/yq/v2/Dockerfile create mode 100644 vendor/github.com/mikefarah/yq/v2/Dockerfile.dev create mode 100644 vendor/github.com/mikefarah/yq/v2/LICENSE create mode 100644 vendor/github.com/mikefarah/yq/v2/Makefile create mode 100644 vendor/github.com/mikefarah/yq/v2/Makefile.variables create mode 100644 vendor/github.com/mikefarah/yq/v2/README.md create mode 100644 vendor/github.com/mikefarah/yq/v2/data_navigator.go create mode 100644 vendor/github.com/mikefarah/yq/v2/go.mod create mode 100644 vendor/github.com/mikefarah/yq/v2/go.sum create mode 100644 vendor/github.com/mikefarah/yq/v2/json_converter.go create mode 100644 vendor/github.com/mikefarah/yq/v2/merge.go create mode 100644 vendor/github.com/mikefarah/yq/v2/mkdocs.yml create mode 100644 vendor/github.com/mikefarah/yq/v2/path_parser.go create mode 100644 vendor/github.com/mikefarah/yq/v2/release_instructions.txt create mode 100644 vendor/github.com/mikefarah/yq/v2/test.yml create mode 100644 vendor/github.com/mikefarah/yq/v2/version.go create mode 100644 vendor/github.com/mikefarah/yq/v2/yq.go delete mode 100644 vendor/golang.org/x/sync/AUTHORS delete mode 100644 vendor/golang.org/x/sync/CONTRIBUTORS delete mode 100644 vendor/golang.org/x/sync/PATENTS delete mode 100644 vendor/golang.org/x/sync/errgroup/errgroup.go create mode 100644 vendor/golang.org/x/sys/unix/mkasm_darwin.go create mode 100644 vendor/golang.org/x/sys/unix/mkpost.go create mode 100644 vendor/golang.org/x/sys/unix/mksyscall.go create mode 100644 vendor/golang.org/x/sys/unix/mksyscall_aix_ppc.go create mode 100644 vendor/golang.org/x/sys/unix/mksyscall_aix_ppc64.go create mode 100644 vendor/golang.org/x/sys/unix/mksyscall_solaris.go create mode 100644 vendor/golang.org/x/sys/unix/mksysctl_openbsd.go create mode 100644 vendor/golang.org/x/sys/unix/mksysnum.go create mode 100644 vendor/golang.org/x/sys/unix/types_aix.go create mode 100644 vendor/golang.org/x/sys/unix/types_darwin.go create mode 100644 vendor/golang.org/x/sys/unix/types_dragonfly.go create mode 100644 vendor/golang.org/x/sys/unix/types_freebsd.go create mode 100644 vendor/golang.org/x/sys/unix/types_netbsd.go create mode 100644 vendor/golang.org/x/sys/unix/types_openbsd.go create mode 100644 vendor/golang.org/x/sys/unix/types_solaris.go create mode 100644 vendor/golang.org/x/text/unicode/bidi/gen.go create mode 100644 vendor/golang.org/x/text/unicode/bidi/gen_ranges.go create mode 100644 vendor/golang.org/x/text/unicode/bidi/gen_trieval.go create mode 100644 vendor/golang.org/x/text/unicode/norm/maketables.go create mode 100644 vendor/golang.org/x/text/unicode/norm/triegen.go create mode 100644 vendor/golang.org/x/text/width/gen.go create mode 100644 vendor/golang.org/x/text/width/gen_common.go create mode 100644 vendor/golang.org/x/text/width/gen_trieval.go create mode 100644 vendor/golang.org/x/tools/go/gcexportdata/main.go create mode 100644 vendor/golang.org/x/tools/internal/imports/mkindex.go create mode 100644 vendor/golang.org/x/tools/internal/imports/mkstdlib.go create mode 100644 vendor/golang.org/x/tools/internal/imports/mod_cache.go create mode 100644 vendor/golang.org/x/tools/internal/span/parse.go create mode 100644 vendor/golang.org/x/tools/internal/span/span.go create mode 100644 vendor/golang.org/x/tools/internal/span/token.go create mode 100644 vendor/golang.org/x/tools/internal/span/token111.go create mode 100644 vendor/golang.org/x/tools/internal/span/token112.go create mode 100644 vendor/golang.org/x/tools/internal/span/uri.go create mode 100644 vendor/golang.org/x/tools/internal/span/utf16.go create mode 100644 vendor/gopkg.in/imdario/mergo.v0/.gitignore create mode 100644 vendor/gopkg.in/imdario/mergo.v0/.travis.yml create mode 100644 vendor/gopkg.in/imdario/mergo.v0/CODE_OF_CONDUCT.md create mode 100644 vendor/gopkg.in/imdario/mergo.v0/LICENSE create mode 100644 vendor/gopkg.in/imdario/mergo.v0/README.md create mode 100644 vendor/gopkg.in/imdario/mergo.v0/doc.go create mode 100644 vendor/gopkg.in/imdario/mergo.v0/map.go create mode 100644 vendor/gopkg.in/imdario/mergo.v0/merge.go create mode 100644 vendor/gopkg.in/imdario/mergo.v0/mergo.go create mode 100644 vendor/gopkg.in/op/go-logging.v1/.travis.yml create mode 100644 vendor/gopkg.in/op/go-logging.v1/CONTRIBUTORS rename vendor/{golang.org/x/sync => gopkg.in/op/go-logging.v1}/LICENSE (96%) create mode 100644 vendor/gopkg.in/op/go-logging.v1/README.md create mode 100644 vendor/gopkg.in/op/go-logging.v1/backend.go create mode 100644 vendor/gopkg.in/op/go-logging.v1/format.go create mode 100644 vendor/gopkg.in/op/go-logging.v1/level.go create mode 100644 vendor/gopkg.in/op/go-logging.v1/log_nix.go create mode 100644 vendor/gopkg.in/op/go-logging.v1/log_windows.go create mode 100644 vendor/gopkg.in/op/go-logging.v1/logger.go create mode 100644 vendor/gopkg.in/op/go-logging.v1/memory.go create mode 100644 vendor/gopkg.in/op/go-logging.v1/multi.go create mode 100644 vendor/gopkg.in/op/go-logging.v1/syslog.go create mode 100644 vendor/gopkg.in/op/go-logging.v1/syslog_fallback.go create mode 100644 vendor/gopkg.in/yaml.v3/.travis.yml create mode 100644 vendor/gopkg.in/yaml.v3/LICENSE create mode 100644 vendor/gopkg.in/yaml.v3/NOTICE create mode 100644 vendor/gopkg.in/yaml.v3/README.md create mode 100644 vendor/gopkg.in/yaml.v3/apic.go create mode 100644 vendor/gopkg.in/yaml.v3/decode.go create mode 100644 vendor/gopkg.in/yaml.v3/emitterc.go create mode 100644 vendor/gopkg.in/yaml.v3/encode.go create mode 100644 vendor/gopkg.in/yaml.v3/go.mod create mode 100644 vendor/gopkg.in/yaml.v3/parserc.go create mode 100644 vendor/gopkg.in/yaml.v3/readerc.go create mode 100644 vendor/gopkg.in/yaml.v3/resolve.go create mode 100644 vendor/gopkg.in/yaml.v3/scannerc.go create mode 100644 vendor/gopkg.in/yaml.v3/sorter.go create mode 100644 vendor/gopkg.in/yaml.v3/writerc.go create mode 100644 vendor/gopkg.in/yaml.v3/yaml.go create mode 100644 vendor/gopkg.in/yaml.v3/yamlh.go create mode 100644 vendor/gopkg.in/yaml.v3/yamlprivateh.go create mode 100644 vendor/sigs.k8s.io/controller-tools/LICENSE create mode 100644 vendor/sigs.k8s.io/controller-tools/cmd/controller-gen/main.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/crd/conv.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/crd/desc_visitor.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/crd/doc.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/crd/flatten.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/crd/gen.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/crd/known_types.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/crd/markers/crd.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/crd/markers/doc.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/crd/markers/package.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/crd/markers/register.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/crd/markers/validation.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/crd/markers/zz_generated.markerhelp.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/crd/parser.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/crd/schema.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/crd/schema_visitor.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/crd/spec.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/crd/zz_generated.markerhelp.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/deepcopy/doc.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/deepcopy/gen.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/deepcopy/traverse.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/deepcopy/zz_generated.markerhelp.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/genall/doc.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/genall/genall.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/genall/help/doc.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/genall/help/pretty/doc.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/genall/help/pretty/help.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/genall/help/pretty/print.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/genall/help/pretty/table.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/genall/help/sort.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/genall/help/types.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/genall/input.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/genall/options.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/genall/output.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/genall/zz_generated.markerhelp.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/loader/doc.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/loader/errors.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/loader/loader.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/loader/paths.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/loader/refs.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/loader/visit.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/markers/collect.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/markers/doc.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/markers/help.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/markers/parse.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/markers/reg.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/markers/regutil.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/markers/zip.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/rbac/parser.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/rbac/zz_generated.markerhelp.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/schemapatcher/gen.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/schemapatcher/internal/yaml/convert.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/schemapatcher/internal/yaml/nested.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/schemapatcher/internal/yaml/set.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/schemapatcher/zz_generated.markerhelp.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/version/version.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/webhook/parser.go create mode 100644 vendor/sigs.k8s.io/controller-tools/pkg/webhook/zz_generated.markerhelp.go diff --git a/Makefile b/Makefile index b3300c76b2..f52df1b9f2 100644 --- a/Makefile +++ b/Makefile @@ -22,6 +22,8 @@ IMAGE_TAG ?= "dev" SPECIFIC_UNIT_TEST := $(if $(TEST),-run $(TEST),) LOCAL_NAMESPACE := "olm" export GO111MODULE=on +CONTROLLER_GEN := go run ./vendor/sigs.k8s.io/controller-tools/cmd/controller-gen +YQ_INTERNAL := go run ./vendor/github.com/mikefarah/yq/v2/ # ART builds are performed in dist-git, with content (but not commits) copied # from the source repo. Thus at build time if your code is inspecting the local @@ -156,7 +158,9 @@ codegen: go run vendor/k8s.io/kube-openapi/cmd/openapi-gen/openapi-gen.go --logtostderr -i ./vendor/k8s.io/apimachinery/pkg/runtime,./vendor/k8s.io/apimachinery/pkg/apis/meta/v1,./vendor/k8s.io/apimachinery/pkg/version,./pkg/package-server/apis/operators/v1,./pkg/package-server/apis/apps/v1alpha1,./pkg/api/apis/operators/v1alpha1,./pkg/lib/version -p $(PKG)/pkg/package-server/apis/openapi -O zz_generated.openapi -h boilerplate.go.txt -r /dev/null $(CODEGEN_INTERNAL) deepcopy,conversion,client,lister,informer $(PKG)/pkg/api/client $(PKG)/pkg/api/apis $(PKG)/pkg/api/apis "operators:v1alpha1,v1" $(CODEGEN_INTERNAL) all $(PKG)/pkg/package-server/client $(PKG)/pkg/package-server/apis $(PKG)/pkg/package-server/apis "operators:v1 apps:v1alpha1" - + $(CONTROLLER_GEN) schemapatch:manifests=./deploy/chart/templates paths=./pkg/api/apis/operators/v1alpha1/... output:dir=./deploy/chart/templates + $(CONTROLLER_GEN) schemapatch:manifests=./deploy/chart/templates paths=./pkg/api/apis/operators/v1/... output:dir=./deploy/chart/templates + $(YQ_INTERNAL) w --inplace deploy/chart/templates/0000_50_olm_03-clusterserviceversion.crd.yaml spec.validation.openAPIV3Schema.properties.spec.properties.install.properties.spec.properties.deployments.items.properties.spec.properties.template.properties.metadata.x-kubernetes-preserve-unknown-fields true container-codegen: docker build -t olm:codegen -f codegen.Dockerfile . docker run --name temp-codegen olm:codegen /bin/true @@ -226,9 +230,9 @@ endif ifndef ver $(error ver is undefined) endif - yq w -i deploy/$(target)/values.yaml olm.image.ref $(olmref) - yq w -i deploy/$(target)/values.yaml catalog.image.ref $(olmref) - yq w -i deploy/$(target)/values.yaml package.image.ref $(olmref) + $(YQ_INTERNAL) w -i deploy/$(target)/values.yaml olm.image.ref $(olmref) + $(YQ_INTERNAL) w -i deploy/$(target)/values.yaml catalog.image.ref $(olmref) + $(YQ_INTERNAL) w -i deploy/$(target)/values.yaml package.image.ref $(olmref) ./scripts/package_release.sh $(ver) deploy/$(target)/manifests/$(ver) deploy/$(target)/values.yaml ln -sfFn ./$(ver) deploy/$(target)/manifests/latest ifeq ($(quickstart), true) diff --git a/go.mod b/go.mod index 4c61059ebb..f00f63084f 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 // indirect github.com/grpc-ecosystem/grpc-gateway v1.9.4 // indirect github.com/maxbrunsfeld/counterfeiter/v6 v6.2.1 + github.com/mikefarah/yq/v2 v2.4.1 github.com/mitchellh/hashstructure v1.0.0 github.com/munnerz/goautoneg v0.0.0-20190414153302-2ae31c8b6b30 // indirect github.com/openshift/api v3.9.1-0.20190924102528-32369d4db2ad+incompatible @@ -33,7 +34,6 @@ require ( github.com/stretchr/testify v1.4.0 github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 // indirect github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect - golang.org/x/sync v0.0.0-20190423024810-112230192c58 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 gonum.org/v1/gonum v0.0.0-20190710053202-4340aa3071a0 // indirect google.golang.org/grpc v1.23.0 @@ -48,6 +48,7 @@ require ( k8s.io/kube-aggregator v0.0.0 k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf k8s.io/kubernetes v1.16.0 + sigs.k8s.io/controller-tools v0.2.4 ) replace ( diff --git a/go.sum b/go.sum index 661fb2582c..aba4ad1866 100644 --- a/go.sum +++ b/go.sum @@ -152,6 +152,8 @@ github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLi github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -215,6 +217,8 @@ github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2K github.com/go-ozzo/ozzo-validation v3.5.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobuffalo/flect v0.1.5 h1:xpKq9ap8MbYfhuPCF0dBH854Gp9CxZjr/IocxELFflo= +github.com/gobuffalo/flect v0.1.5/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80= github.com/gocql/gocql v0.0.0-20190301043612-f6df8288f9b4/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0= github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -364,7 +368,11 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63 h1:nTT4s92Dgz2HlrB github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-shellwords v1.0.5/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o= github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= @@ -376,6 +384,10 @@ github.com/mesos/mesos-go v0.0.9/go.mod h1:kPYCMQ9gsOXVAle1OsoY4I1+9kPu8GHkf88aV github.com/mholt/certmagic v0.6.2-0.20190624175158-6a42ef9fe8c2/go.mod h1:g4cOPxcjV0oFq3qwpjSA30LReKD8AoIfwAY9VvG35NY= github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.4/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mikefarah/yaml/v2 v2.4.0 h1:eYqfooY0BnvKTJxr7+ABJs13n3dg9n347GScDaU2Lww= +github.com/mikefarah/yaml/v2 v2.4.0/go.mod h1:ahVqZF4n1W4NqwvVnZzC4es67xsW9uR/RRf2RRxieJU= +github.com/mikefarah/yq/v2 v2.4.1 h1:tajDonaFK6WqitSZExB6fKlWQy/yCkptqxh2AXEe3N4= +github.com/mikefarah/yq/v2 v2.4.1/go.mod h1:i8SYf1XdgUvY2OFwSqGAtWOOgimD2McJ6iutoxRm4k0= github.com/mindprince/gonvml v0.0.0-20171110221305-fee913ce8fb2/go.mod h1:2eu9pRWp8mo84xCg6KswZ+USQHjwgRhNp06sozOdsTY= github.com/mistifyio/go-zfs v2.1.1+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -624,6 +636,7 @@ golang.org/x/sys v0.0.0-20190102155601-82a175fd1598/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -663,6 +676,9 @@ golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db h1:9hRk1xeL9LTT3yX/941DqeBz87XgHAQuj+TbimYJuiw= golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= +golang.org/x/tools v0.0.0-20191030203535-5e247c9ad0a0 h1:s5lp4ug7qHzUccgyFdjsX7OZDzHXRaePrF3B3vmUiuM= +golang.org/x/tools v0.0.0-20191030203535-5e247c9ad0a0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= gonum.org/v1/gonum v0.0.0-20190710053202-4340aa3071a0 h1:2qZ38BsejXrhuetzb8UxucqrWDZKjypFSZA82hLCpZ4= gonum.org/v1/gonum v0.0.0-20190710053202-4340aa3071a0/go.mod h1:03dgh78c4UvU1WksguQ/lvJQXbezKQGJSrwwRq5MraQ= @@ -704,12 +720,16 @@ gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/imdario/mergo.v0 v0.3.7 h1:QDotlIZtaO/p+Um0ok18HRTpq5i5/SAk/qprsor+9c8= +gopkg.in/imdario/mergo.v0 v0.3.7/go.mod h1:9qPP6AGrlC1G2PTNXko614FwGZvorN7MiBU0Eppok+U= gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/mcuadros/go-syslog.v2 v2.2.1/go.mod h1:l5LPIyOOyIdQquNg+oU6Z3524YwrcqEm0aKH+5zpt2U= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 h1:6D+BvnJ/j6e222UW8s2qTSe3wGBtvo0MbVQG/c5k8RE= +gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473/go.mod h1:N1eN2tsCx0Ydtgjl4cqmbRCsY4/+z4cYDeqwZTk6zog= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= @@ -719,6 +739,8 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20190905181640-827449938966 h1:B0J02caTR6tpSJozBJyiAzT6CtBzjclw4pgm9gg8Ys0= +gopkg.in/yaml.v3 v3.0.0-20190905181640-827449938966/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/gotestsum v0.3.5/go.mod h1:Mnf3e5FUzXbkCfynWBGOwLssY7gTQgCHObK9tMpAriY= @@ -778,6 +800,8 @@ modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= +sigs.k8s.io/controller-tools v0.2.4 h1:la1h46EzElvWefWLqfsXrnsO3lZjpkI0asTpX6h8PLA= +sigs.k8s.io/controller-tools v0.2.4/go.mod h1:m/ztfQNocGYBgTTCmFdnK94uVvgxeZeE3LtJvd/jIzA= sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca h1:6dsH6AYQWbyZmtttJNe8Gq1cXOeS1BdV3eW37zHilAQ= diff --git a/tools.go b/tools.go index b678e29506..199df0190e 100644 --- a/tools.go +++ b/tools.go @@ -5,6 +5,7 @@ package tools import ( _ "github.com/golang/mock/mockgen" _ "github.com/maxbrunsfeld/counterfeiter/v6" + _ "github.com/mikefarah/yq/v2" _ "k8s.io/code-generator/cmd/client-gen" _ "k8s.io/code-generator/cmd/conversion-gen/" _ "k8s.io/code-generator/cmd/deepcopy-gen" @@ -16,4 +17,5 @@ import ( _ "k8s.io/code-generator/cmd/openapi-gen" _ "k8s.io/code-generator/cmd/set-gen" _ "k8s.io/kube-openapi/cmd/openapi-gen" + _ "sigs.k8s.io/controller-tools/cmd/controller-gen" ) diff --git a/vendor/github.com/fatih/color/.travis.yml b/vendor/github.com/fatih/color/.travis.yml new file mode 100644 index 0000000000..95f8a1ff5c --- /dev/null +++ b/vendor/github.com/fatih/color/.travis.yml @@ -0,0 +1,5 @@ +language: go +go: + - 1.8.x + - tip + diff --git a/vendor/github.com/fatih/color/Gopkg.lock b/vendor/github.com/fatih/color/Gopkg.lock new file mode 100644 index 0000000000..7d879e9caf --- /dev/null +++ b/vendor/github.com/fatih/color/Gopkg.lock @@ -0,0 +1,27 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + name = "github.com/mattn/go-colorable" + packages = ["."] + revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072" + version = "v0.0.9" + +[[projects]] + name = "github.com/mattn/go-isatty" + packages = ["."] + revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39" + version = "v0.0.3" + +[[projects]] + branch = "master" + name = "golang.org/x/sys" + packages = ["unix"] + revision = "37707fdb30a5b38865cfb95e5aab41707daec7fd" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + inputs-digest = "e8a50671c3cb93ea935bf210b1cd20702876b9d9226129be581ef646d1565cdc" + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/vendor/github.com/fatih/color/Gopkg.toml b/vendor/github.com/fatih/color/Gopkg.toml new file mode 100644 index 0000000000..ff1617f71d --- /dev/null +++ b/vendor/github.com/fatih/color/Gopkg.toml @@ -0,0 +1,30 @@ + +# Gopkg.toml example +# +# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md +# for detailed Gopkg.toml documentation. +# +# required = ["github.com/user/thing/cmd/thing"] +# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] +# +# [[constraint]] +# name = "github.com/user/project" +# version = "1.0.0" +# +# [[constraint]] +# name = "github.com/user/project2" +# branch = "dev" +# source = "github.com/myfork/project2" +# +# [[override]] +# name = "github.com/x/y" +# version = "2.4.0" + + +[[constraint]] + name = "github.com/mattn/go-colorable" + version = "0.0.9" + +[[constraint]] + name = "github.com/mattn/go-isatty" + version = "0.0.3" diff --git a/vendor/github.com/fatih/color/LICENSE.md b/vendor/github.com/fatih/color/LICENSE.md new file mode 100644 index 0000000000..25fdaf639d --- /dev/null +++ b/vendor/github.com/fatih/color/LICENSE.md @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 Fatih Arslan + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/fatih/color/README.md b/vendor/github.com/fatih/color/README.md new file mode 100644 index 0000000000..3fc9544602 --- /dev/null +++ b/vendor/github.com/fatih/color/README.md @@ -0,0 +1,179 @@ +# Color [![GoDoc](https://godoc.org/github.com/fatih/color?status.svg)](https://godoc.org/github.com/fatih/color) [![Build Status](https://img.shields.io/travis/fatih/color.svg?style=flat-square)](https://travis-ci.org/fatih/color) + + + +Color lets you use colorized outputs in terms of [ANSI Escape +Codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors) in Go (Golang). It +has support for Windows too! The API can be used in several ways, pick one that +suits you. + + +![Color](https://i.imgur.com/c1JI0lA.png) + + +## Install + +```bash +go get github.com/fatih/color +``` + +Note that the `vendor` folder is here for stability. Remove the folder if you +already have the dependencies in your GOPATH. + +## Examples + +### Standard colors + +```go +// Print with default helper functions +color.Cyan("Prints text in cyan.") + +// A newline will be appended automatically +color.Blue("Prints %s in blue.", "text") + +// These are using the default foreground colors +color.Red("We have red") +color.Magenta("And many others ..") + +``` + +### Mix and reuse colors + +```go +// Create a new color object +c := color.New(color.FgCyan).Add(color.Underline) +c.Println("Prints cyan text with an underline.") + +// Or just add them to New() +d := color.New(color.FgCyan, color.Bold) +d.Printf("This prints bold cyan %s\n", "too!.") + +// Mix up foreground and background colors, create new mixes! +red := color.New(color.FgRed) + +boldRed := red.Add(color.Bold) +boldRed.Println("This will print text in bold red.") + +whiteBackground := red.Add(color.BgWhite) +whiteBackground.Println("Red text with white background.") +``` + +### Use your own output (io.Writer) + +```go +// Use your own io.Writer output +color.New(color.FgBlue).Fprintln(myWriter, "blue color!") + +blue := color.New(color.FgBlue) +blue.Fprint(writer, "This will print text in blue.") +``` + +### Custom print functions (PrintFunc) + +```go +// Create a custom print function for convenience +red := color.New(color.FgRed).PrintfFunc() +red("Warning") +red("Error: %s", err) + +// Mix up multiple attributes +notice := color.New(color.Bold, color.FgGreen).PrintlnFunc() +notice("Don't forget this...") +``` + +### Custom fprint functions (FprintFunc) + +```go +blue := color.New(FgBlue).FprintfFunc() +blue(myWriter, "important notice: %s", stars) + +// Mix up with multiple attributes +success := color.New(color.Bold, color.FgGreen).FprintlnFunc() +success(myWriter, "Don't forget this...") +``` + +### Insert into noncolor strings (SprintFunc) + +```go +// Create SprintXxx functions to mix strings with other non-colorized strings: +yellow := color.New(color.FgYellow).SprintFunc() +red := color.New(color.FgRed).SprintFunc() +fmt.Printf("This is a %s and this is %s.\n", yellow("warning"), red("error")) + +info := color.New(color.FgWhite, color.BgGreen).SprintFunc() +fmt.Printf("This %s rocks!\n", info("package")) + +// Use helper functions +fmt.Println("This", color.RedString("warning"), "should be not neglected.") +fmt.Printf("%v %v\n", color.GreenString("Info:"), "an important message.") + +// Windows supported too! Just don't forget to change the output to color.Output +fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS")) +``` + +### Plug into existing code + +```go +// Use handy standard colors +color.Set(color.FgYellow) + +fmt.Println("Existing text will now be in yellow") +fmt.Printf("This one %s\n", "too") + +color.Unset() // Don't forget to unset + +// You can mix up parameters +color.Set(color.FgMagenta, color.Bold) +defer color.Unset() // Use it in your function + +fmt.Println("All text will now be bold magenta.") +``` + +### Disable/Enable color + +There might be a case where you want to explicitly disable/enable color output. the +`go-isatty` package will automatically disable color output for non-tty output streams +(for example if the output were piped directly to `less`) + +`Color` has support to disable/enable colors both globally and for single color +definitions. For example suppose you have a CLI app and a `--no-color` bool flag. You +can easily disable the color output with: + +```go + +var flagNoColor = flag.Bool("no-color", false, "Disable color output") + +if *flagNoColor { + color.NoColor = true // disables colorized output +} +``` + +It also has support for single color definitions (local). You can +disable/enable color output on the fly: + +```go +c := color.New(color.FgCyan) +c.Println("Prints cyan text") + +c.DisableColor() +c.Println("This is printed without any color") + +c.EnableColor() +c.Println("This prints again cyan...") +``` + +## Todo + +* Save/Return previous values +* Evaluate fmt.Formatter interface + + +## Credits + + * [Fatih Arslan](https://github.com/fatih) + * Windows support via @mattn: [colorable](https://github.com/mattn/go-colorable) + +## License + +The MIT License (MIT) - see [`LICENSE.md`](https://github.com/fatih/color/blob/master/LICENSE.md) for more details + diff --git a/vendor/github.com/fatih/color/color.go b/vendor/github.com/fatih/color/color.go new file mode 100644 index 0000000000..91c8e9f062 --- /dev/null +++ b/vendor/github.com/fatih/color/color.go @@ -0,0 +1,603 @@ +package color + +import ( + "fmt" + "io" + "os" + "strconv" + "strings" + "sync" + + "github.com/mattn/go-colorable" + "github.com/mattn/go-isatty" +) + +var ( + // NoColor defines if the output is colorized or not. It's dynamically set to + // false or true based on the stdout's file descriptor referring to a terminal + // or not. This is a global option and affects all colors. For more control + // over each color block use the methods DisableColor() individually. + NoColor = os.Getenv("TERM") == "dumb" || + (!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd())) + + // Output defines the standard output of the print functions. By default + // os.Stdout is used. + Output = colorable.NewColorableStdout() + + // Error defines a color supporting writer for os.Stderr. + Error = colorable.NewColorableStderr() + + // colorsCache is used to reduce the count of created Color objects and + // allows to reuse already created objects with required Attribute. + colorsCache = make(map[Attribute]*Color) + colorsCacheMu sync.Mutex // protects colorsCache +) + +// Color defines a custom color object which is defined by SGR parameters. +type Color struct { + params []Attribute + noColor *bool +} + +// Attribute defines a single SGR Code +type Attribute int + +const escape = "\x1b" + +// Base attributes +const ( + Reset Attribute = iota + Bold + Faint + Italic + Underline + BlinkSlow + BlinkRapid + ReverseVideo + Concealed + CrossedOut +) + +// Foreground text colors +const ( + FgBlack Attribute = iota + 30 + FgRed + FgGreen + FgYellow + FgBlue + FgMagenta + FgCyan + FgWhite +) + +// Foreground Hi-Intensity text colors +const ( + FgHiBlack Attribute = iota + 90 + FgHiRed + FgHiGreen + FgHiYellow + FgHiBlue + FgHiMagenta + FgHiCyan + FgHiWhite +) + +// Background text colors +const ( + BgBlack Attribute = iota + 40 + BgRed + BgGreen + BgYellow + BgBlue + BgMagenta + BgCyan + BgWhite +) + +// Background Hi-Intensity text colors +const ( + BgHiBlack Attribute = iota + 100 + BgHiRed + BgHiGreen + BgHiYellow + BgHiBlue + BgHiMagenta + BgHiCyan + BgHiWhite +) + +// New returns a newly created color object. +func New(value ...Attribute) *Color { + c := &Color{params: make([]Attribute, 0)} + c.Add(value...) + return c +} + +// Set sets the given parameters immediately. It will change the color of +// output with the given SGR parameters until color.Unset() is called. +func Set(p ...Attribute) *Color { + c := New(p...) + c.Set() + return c +} + +// Unset resets all escape attributes and clears the output. Usually should +// be called after Set(). +func Unset() { + if NoColor { + return + } + + fmt.Fprintf(Output, "%s[%dm", escape, Reset) +} + +// Set sets the SGR sequence. +func (c *Color) Set() *Color { + if c.isNoColorSet() { + return c + } + + fmt.Fprintf(Output, c.format()) + return c +} + +func (c *Color) unset() { + if c.isNoColorSet() { + return + } + + Unset() +} + +func (c *Color) setWriter(w io.Writer) *Color { + if c.isNoColorSet() { + return c + } + + fmt.Fprintf(w, c.format()) + return c +} + +func (c *Color) unsetWriter(w io.Writer) { + if c.isNoColorSet() { + return + } + + if NoColor { + return + } + + fmt.Fprintf(w, "%s[%dm", escape, Reset) +} + +// Add is used to chain SGR parameters. Use as many as parameters to combine +// and create custom color objects. Example: Add(color.FgRed, color.Underline). +func (c *Color) Add(value ...Attribute) *Color { + c.params = append(c.params, value...) + return c +} + +func (c *Color) prepend(value Attribute) { + c.params = append(c.params, 0) + copy(c.params[1:], c.params[0:]) + c.params[0] = value +} + +// Fprint formats using the default formats for its operands and writes to w. +// Spaces are added between operands when neither is a string. +// It returns the number of bytes written and any write error encountered. +// On Windows, users should wrap w with colorable.NewColorable() if w is of +// type *os.File. +func (c *Color) Fprint(w io.Writer, a ...interface{}) (n int, err error) { + c.setWriter(w) + defer c.unsetWriter(w) + + return fmt.Fprint(w, a...) +} + +// Print formats using the default formats for its operands and writes to +// standard output. Spaces are added between operands when neither is a +// string. It returns the number of bytes written and any write error +// encountered. This is the standard fmt.Print() method wrapped with the given +// color. +func (c *Color) Print(a ...interface{}) (n int, err error) { + c.Set() + defer c.unset() + + return fmt.Fprint(Output, a...) +} + +// Fprintf formats according to a format specifier and writes to w. +// It returns the number of bytes written and any write error encountered. +// On Windows, users should wrap w with colorable.NewColorable() if w is of +// type *os.File. +func (c *Color) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { + c.setWriter(w) + defer c.unsetWriter(w) + + return fmt.Fprintf(w, format, a...) +} + +// Printf formats according to a format specifier and writes to standard output. +// It returns the number of bytes written and any write error encountered. +// This is the standard fmt.Printf() method wrapped with the given color. +func (c *Color) Printf(format string, a ...interface{}) (n int, err error) { + c.Set() + defer c.unset() + + return fmt.Fprintf(Output, format, a...) +} + +// Fprintln formats using the default formats for its operands and writes to w. +// Spaces are always added between operands and a newline is appended. +// On Windows, users should wrap w with colorable.NewColorable() if w is of +// type *os.File. +func (c *Color) Fprintln(w io.Writer, a ...interface{}) (n int, err error) { + c.setWriter(w) + defer c.unsetWriter(w) + + return fmt.Fprintln(w, a...) +} + +// Println formats using the default formats for its operands and writes to +// standard output. Spaces are always added between operands and a newline is +// appended. It returns the number of bytes written and any write error +// encountered. This is the standard fmt.Print() method wrapped with the given +// color. +func (c *Color) Println(a ...interface{}) (n int, err error) { + c.Set() + defer c.unset() + + return fmt.Fprintln(Output, a...) +} + +// Sprint is just like Print, but returns a string instead of printing it. +func (c *Color) Sprint(a ...interface{}) string { + return c.wrap(fmt.Sprint(a...)) +} + +// Sprintln is just like Println, but returns a string instead of printing it. +func (c *Color) Sprintln(a ...interface{}) string { + return c.wrap(fmt.Sprintln(a...)) +} + +// Sprintf is just like Printf, but returns a string instead of printing it. +func (c *Color) Sprintf(format string, a ...interface{}) string { + return c.wrap(fmt.Sprintf(format, a...)) +} + +// FprintFunc returns a new function that prints the passed arguments as +// colorized with color.Fprint(). +func (c *Color) FprintFunc() func(w io.Writer, a ...interface{}) { + return func(w io.Writer, a ...interface{}) { + c.Fprint(w, a...) + } +} + +// PrintFunc returns a new function that prints the passed arguments as +// colorized with color.Print(). +func (c *Color) PrintFunc() func(a ...interface{}) { + return func(a ...interface{}) { + c.Print(a...) + } +} + +// FprintfFunc returns a new function that prints the passed arguments as +// colorized with color.Fprintf(). +func (c *Color) FprintfFunc() func(w io.Writer, format string, a ...interface{}) { + return func(w io.Writer, format string, a ...interface{}) { + c.Fprintf(w, format, a...) + } +} + +// PrintfFunc returns a new function that prints the passed arguments as +// colorized with color.Printf(). +func (c *Color) PrintfFunc() func(format string, a ...interface{}) { + return func(format string, a ...interface{}) { + c.Printf(format, a...) + } +} + +// FprintlnFunc returns a new function that prints the passed arguments as +// colorized with color.Fprintln(). +func (c *Color) FprintlnFunc() func(w io.Writer, a ...interface{}) { + return func(w io.Writer, a ...interface{}) { + c.Fprintln(w, a...) + } +} + +// PrintlnFunc returns a new function that prints the passed arguments as +// colorized with color.Println(). +func (c *Color) PrintlnFunc() func(a ...interface{}) { + return func(a ...interface{}) { + c.Println(a...) + } +} + +// SprintFunc returns a new function that returns colorized strings for the +// given arguments with fmt.Sprint(). Useful to put into or mix into other +// string. Windows users should use this in conjunction with color.Output, example: +// +// put := New(FgYellow).SprintFunc() +// fmt.Fprintf(color.Output, "This is a %s", put("warning")) +func (c *Color) SprintFunc() func(a ...interface{}) string { + return func(a ...interface{}) string { + return c.wrap(fmt.Sprint(a...)) + } +} + +// SprintfFunc returns a new function that returns colorized strings for the +// given arguments with fmt.Sprintf(). Useful to put into or mix into other +// string. Windows users should use this in conjunction with color.Output. +func (c *Color) SprintfFunc() func(format string, a ...interface{}) string { + return func(format string, a ...interface{}) string { + return c.wrap(fmt.Sprintf(format, a...)) + } +} + +// SprintlnFunc returns a new function that returns colorized strings for the +// given arguments with fmt.Sprintln(). Useful to put into or mix into other +// string. Windows users should use this in conjunction with color.Output. +func (c *Color) SprintlnFunc() func(a ...interface{}) string { + return func(a ...interface{}) string { + return c.wrap(fmt.Sprintln(a...)) + } +} + +// sequence returns a formatted SGR sequence to be plugged into a "\x1b[...m" +// an example output might be: "1;36" -> bold cyan +func (c *Color) sequence() string { + format := make([]string, len(c.params)) + for i, v := range c.params { + format[i] = strconv.Itoa(int(v)) + } + + return strings.Join(format, ";") +} + +// wrap wraps the s string with the colors attributes. The string is ready to +// be printed. +func (c *Color) wrap(s string) string { + if c.isNoColorSet() { + return s + } + + return c.format() + s + c.unformat() +} + +func (c *Color) format() string { + return fmt.Sprintf("%s[%sm", escape, c.sequence()) +} + +func (c *Color) unformat() string { + return fmt.Sprintf("%s[%dm", escape, Reset) +} + +// DisableColor disables the color output. Useful to not change any existing +// code and still being able to output. Can be used for flags like +// "--no-color". To enable back use EnableColor() method. +func (c *Color) DisableColor() { + c.noColor = boolPtr(true) +} + +// EnableColor enables the color output. Use it in conjunction with +// DisableColor(). Otherwise this method has no side effects. +func (c *Color) EnableColor() { + c.noColor = boolPtr(false) +} + +func (c *Color) isNoColorSet() bool { + // check first if we have user setted action + if c.noColor != nil { + return *c.noColor + } + + // if not return the global option, which is disabled by default + return NoColor +} + +// Equals returns a boolean value indicating whether two colors are equal. +func (c *Color) Equals(c2 *Color) bool { + if len(c.params) != len(c2.params) { + return false + } + + for _, attr := range c.params { + if !c2.attrExists(attr) { + return false + } + } + + return true +} + +func (c *Color) attrExists(a Attribute) bool { + for _, attr := range c.params { + if attr == a { + return true + } + } + + return false +} + +func boolPtr(v bool) *bool { + return &v +} + +func getCachedColor(p Attribute) *Color { + colorsCacheMu.Lock() + defer colorsCacheMu.Unlock() + + c, ok := colorsCache[p] + if !ok { + c = New(p) + colorsCache[p] = c + } + + return c +} + +func colorPrint(format string, p Attribute, a ...interface{}) { + c := getCachedColor(p) + + if !strings.HasSuffix(format, "\n") { + format += "\n" + } + + if len(a) == 0 { + c.Print(format) + } else { + c.Printf(format, a...) + } +} + +func colorString(format string, p Attribute, a ...interface{}) string { + c := getCachedColor(p) + + if len(a) == 0 { + return c.SprintFunc()(format) + } + + return c.SprintfFunc()(format, a...) +} + +// Black is a convenient helper function to print with black foreground. A +// newline is appended to format by default. +func Black(format string, a ...interface{}) { colorPrint(format, FgBlack, a...) } + +// Red is a convenient helper function to print with red foreground. A +// newline is appended to format by default. +func Red(format string, a ...interface{}) { colorPrint(format, FgRed, a...) } + +// Green is a convenient helper function to print with green foreground. A +// newline is appended to format by default. +func Green(format string, a ...interface{}) { colorPrint(format, FgGreen, a...) } + +// Yellow is a convenient helper function to print with yellow foreground. +// A newline is appended to format by default. +func Yellow(format string, a ...interface{}) { colorPrint(format, FgYellow, a...) } + +// Blue is a convenient helper function to print with blue foreground. A +// newline is appended to format by default. +func Blue(format string, a ...interface{}) { colorPrint(format, FgBlue, a...) } + +// Magenta is a convenient helper function to print with magenta foreground. +// A newline is appended to format by default. +func Magenta(format string, a ...interface{}) { colorPrint(format, FgMagenta, a...) } + +// Cyan is a convenient helper function to print with cyan foreground. A +// newline is appended to format by default. +func Cyan(format string, a ...interface{}) { colorPrint(format, FgCyan, a...) } + +// White is a convenient helper function to print with white foreground. A +// newline is appended to format by default. +func White(format string, a ...interface{}) { colorPrint(format, FgWhite, a...) } + +// BlackString is a convenient helper function to return a string with black +// foreground. +func BlackString(format string, a ...interface{}) string { return colorString(format, FgBlack, a...) } + +// RedString is a convenient helper function to return a string with red +// foreground. +func RedString(format string, a ...interface{}) string { return colorString(format, FgRed, a...) } + +// GreenString is a convenient helper function to return a string with green +// foreground. +func GreenString(format string, a ...interface{}) string { return colorString(format, FgGreen, a...) } + +// YellowString is a convenient helper function to return a string with yellow +// foreground. +func YellowString(format string, a ...interface{}) string { return colorString(format, FgYellow, a...) } + +// BlueString is a convenient helper function to return a string with blue +// foreground. +func BlueString(format string, a ...interface{}) string { return colorString(format, FgBlue, a...) } + +// MagentaString is a convenient helper function to return a string with magenta +// foreground. +func MagentaString(format string, a ...interface{}) string { + return colorString(format, FgMagenta, a...) +} + +// CyanString is a convenient helper function to return a string with cyan +// foreground. +func CyanString(format string, a ...interface{}) string { return colorString(format, FgCyan, a...) } + +// WhiteString is a convenient helper function to return a string with white +// foreground. +func WhiteString(format string, a ...interface{}) string { return colorString(format, FgWhite, a...) } + +// HiBlack is a convenient helper function to print with hi-intensity black foreground. A +// newline is appended to format by default. +func HiBlack(format string, a ...interface{}) { colorPrint(format, FgHiBlack, a...) } + +// HiRed is a convenient helper function to print with hi-intensity red foreground. A +// newline is appended to format by default. +func HiRed(format string, a ...interface{}) { colorPrint(format, FgHiRed, a...) } + +// HiGreen is a convenient helper function to print with hi-intensity green foreground. A +// newline is appended to format by default. +func HiGreen(format string, a ...interface{}) { colorPrint(format, FgHiGreen, a...) } + +// HiYellow is a convenient helper function to print with hi-intensity yellow foreground. +// A newline is appended to format by default. +func HiYellow(format string, a ...interface{}) { colorPrint(format, FgHiYellow, a...) } + +// HiBlue is a convenient helper function to print with hi-intensity blue foreground. A +// newline is appended to format by default. +func HiBlue(format string, a ...interface{}) { colorPrint(format, FgHiBlue, a...) } + +// HiMagenta is a convenient helper function to print with hi-intensity magenta foreground. +// A newline is appended to format by default. +func HiMagenta(format string, a ...interface{}) { colorPrint(format, FgHiMagenta, a...) } + +// HiCyan is a convenient helper function to print with hi-intensity cyan foreground. A +// newline is appended to format by default. +func HiCyan(format string, a ...interface{}) { colorPrint(format, FgHiCyan, a...) } + +// HiWhite is a convenient helper function to print with hi-intensity white foreground. A +// newline is appended to format by default. +func HiWhite(format string, a ...interface{}) { colorPrint(format, FgHiWhite, a...) } + +// HiBlackString is a convenient helper function to return a string with hi-intensity black +// foreground. +func HiBlackString(format string, a ...interface{}) string { + return colorString(format, FgHiBlack, a...) +} + +// HiRedString is a convenient helper function to return a string with hi-intensity red +// foreground. +func HiRedString(format string, a ...interface{}) string { return colorString(format, FgHiRed, a...) } + +// HiGreenString is a convenient helper function to return a string with hi-intensity green +// foreground. +func HiGreenString(format string, a ...interface{}) string { + return colorString(format, FgHiGreen, a...) +} + +// HiYellowString is a convenient helper function to return a string with hi-intensity yellow +// foreground. +func HiYellowString(format string, a ...interface{}) string { + return colorString(format, FgHiYellow, a...) +} + +// HiBlueString is a convenient helper function to return a string with hi-intensity blue +// foreground. +func HiBlueString(format string, a ...interface{}) string { return colorString(format, FgHiBlue, a...) } + +// HiMagentaString is a convenient helper function to return a string with hi-intensity magenta +// foreground. +func HiMagentaString(format string, a ...interface{}) string { + return colorString(format, FgHiMagenta, a...) +} + +// HiCyanString is a convenient helper function to return a string with hi-intensity cyan +// foreground. +func HiCyanString(format string, a ...interface{}) string { return colorString(format, FgHiCyan, a...) } + +// HiWhiteString is a convenient helper function to return a string with hi-intensity white +// foreground. +func HiWhiteString(format string, a ...interface{}) string { + return colorString(format, FgHiWhite, a...) +} diff --git a/vendor/github.com/fatih/color/doc.go b/vendor/github.com/fatih/color/doc.go new file mode 100644 index 0000000000..cf1e96500f --- /dev/null +++ b/vendor/github.com/fatih/color/doc.go @@ -0,0 +1,133 @@ +/* +Package color is an ANSI color package to output colorized or SGR defined +output to the standard output. The API can be used in several way, pick one +that suits you. + +Use simple and default helper functions with predefined foreground colors: + + color.Cyan("Prints text in cyan.") + + // a newline will be appended automatically + color.Blue("Prints %s in blue.", "text") + + // More default foreground colors.. + color.Red("We have red") + color.Yellow("Yellow color too!") + color.Magenta("And many others ..") + + // Hi-intensity colors + color.HiGreen("Bright green color.") + color.HiBlack("Bright black means gray..") + color.HiWhite("Shiny white color!") + +However there are times where custom color mixes are required. Below are some +examples to create custom color objects and use the print functions of each +separate color object. + + // Create a new color object + c := color.New(color.FgCyan).Add(color.Underline) + c.Println("Prints cyan text with an underline.") + + // Or just add them to New() + d := color.New(color.FgCyan, color.Bold) + d.Printf("This prints bold cyan %s\n", "too!.") + + + // Mix up foreground and background colors, create new mixes! + red := color.New(color.FgRed) + + boldRed := red.Add(color.Bold) + boldRed.Println("This will print text in bold red.") + + whiteBackground := red.Add(color.BgWhite) + whiteBackground.Println("Red text with White background.") + + // Use your own io.Writer output + color.New(color.FgBlue).Fprintln(myWriter, "blue color!") + + blue := color.New(color.FgBlue) + blue.Fprint(myWriter, "This will print text in blue.") + +You can create PrintXxx functions to simplify even more: + + // Create a custom print function for convenient + red := color.New(color.FgRed).PrintfFunc() + red("warning") + red("error: %s", err) + + // Mix up multiple attributes + notice := color.New(color.Bold, color.FgGreen).PrintlnFunc() + notice("don't forget this...") + +You can also FprintXxx functions to pass your own io.Writer: + + blue := color.New(FgBlue).FprintfFunc() + blue(myWriter, "important notice: %s", stars) + + // Mix up with multiple attributes + success := color.New(color.Bold, color.FgGreen).FprintlnFunc() + success(myWriter, don't forget this...") + + +Or create SprintXxx functions to mix strings with other non-colorized strings: + + yellow := New(FgYellow).SprintFunc() + red := New(FgRed).SprintFunc() + + fmt.Printf("this is a %s and this is %s.\n", yellow("warning"), red("error")) + + info := New(FgWhite, BgGreen).SprintFunc() + fmt.Printf("this %s rocks!\n", info("package")) + +Windows support is enabled by default. All Print functions work as intended. +However only for color.SprintXXX functions, user should use fmt.FprintXXX and +set the output to color.Output: + + fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS")) + + info := New(FgWhite, BgGreen).SprintFunc() + fmt.Fprintf(color.Output, "this %s rocks!\n", info("package")) + +Using with existing code is possible. Just use the Set() method to set the +standard output to the given parameters. That way a rewrite of an existing +code is not required. + + // Use handy standard colors. + color.Set(color.FgYellow) + + fmt.Println("Existing text will be now in Yellow") + fmt.Printf("This one %s\n", "too") + + color.Unset() // don't forget to unset + + // You can mix up parameters + color.Set(color.FgMagenta, color.Bold) + defer color.Unset() // use it in your function + + fmt.Println("All text will be now bold magenta.") + +There might be a case where you want to disable color output (for example to +pipe the standard output of your app to somewhere else). `Color` has support to +disable colors both globally and for single color definition. For example +suppose you have a CLI app and a `--no-color` bool flag. You can easily disable +the color output with: + + var flagNoColor = flag.Bool("no-color", false, "Disable color output") + + if *flagNoColor { + color.NoColor = true // disables colorized output + } + +It also has support for single color definitions (local). You can +disable/enable color output on the fly: + + c := color.New(color.FgCyan) + c.Println("Prints cyan text") + + c.DisableColor() + c.Println("This is printed without any color") + + c.EnableColor() + c.Println("This prints again cyan...") +*/ +package color diff --git a/vendor/github.com/globalsign/mgo/bson/bson_corpus_spec_test_generator.go b/vendor/github.com/globalsign/mgo/bson/bson_corpus_spec_test_generator.go new file mode 100644 index 0000000000..3525a004b6 --- /dev/null +++ b/vendor/github.com/globalsign/mgo/bson/bson_corpus_spec_test_generator.go @@ -0,0 +1,294 @@ +// +build ignore + +package main + +import ( + "bytes" + "fmt" + "go/format" + "html/template" + "io/ioutil" + "log" + "path/filepath" + "strings" + + "github.com/globalsign/mgo/internal/json" +) + +func main() { + log.SetFlags(0) + log.SetPrefix(name + ": ") + + var g Generator + + fmt.Fprintf(&g, "// Code generated by \"%s.go\"; DO NOT EDIT\n\n", name) + + src := g.generate() + + err := ioutil.WriteFile(fmt.Sprintf("%s.go", strings.TrimSuffix(name, "_generator")), src, 0644) + if err != nil { + log.Fatalf("writing output: %s", err) + } +} + +// Generator holds the state of the analysis. Primarily used to buffer +// the output for format.Source. +type Generator struct { + bytes.Buffer // Accumulated output. +} + +// format returns the gofmt-ed contents of the Generator's buffer. +func (g *Generator) format() []byte { + src, err := format.Source(g.Bytes()) + if err != nil { + // Should never happen, but can arise when developing this code. + // The user can compile the output to see the error. + log.Printf("warning: internal error: invalid Go generated: %s", err) + log.Printf("warning: compile the package to analyze the error") + return g.Bytes() + } + return src +} + +// EVERYTHING ABOVE IS CONSTANT BETWEEN THE GENERATORS + +const name = "bson_corpus_spec_test_generator" + +func (g *Generator) generate() []byte { + + testFiles, err := filepath.Glob("./specdata/specifications/source/bson-corpus/tests/*.json") + if err != nil { + log.Fatalf("error reading bson-corpus files: %s", err) + } + + tests, err := g.loadTests(testFiles) + if err != nil { + log.Fatalf("error loading tests: %s", err) + } + + tmpl, err := g.getTemplate() + if err != nil { + log.Fatalf("error loading template: %s", err) + } + + tmpl.Execute(&g.Buffer, tests) + + return g.format() +} + +func (g *Generator) loadTests(filenames []string) ([]*testDef, error) { + var tests []*testDef + for _, filename := range filenames { + test, err := g.loadTest(filename) + if err != nil { + return nil, err + } + + tests = append(tests, test) + } + + return tests, nil +} + +func (g *Generator) loadTest(filename string) (*testDef, error) { + content, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + + var testDef testDef + err = json.Unmarshal(content, &testDef) + if err != nil { + return nil, err + } + + names := make(map[string]struct{}) + + for i := len(testDef.Valid) - 1; i >= 0; i-- { + if testDef.BsonType == "0x05" && testDef.Valid[i].Description == "subtype 0x02" { + testDef.Valid = append(testDef.Valid[:i], testDef.Valid[i+1:]...) + continue + } + + name := cleanupFuncName(testDef.Description + "_" + testDef.Valid[i].Description) + nameIdx := name + j := 1 + for { + if _, ok := names[nameIdx]; !ok { + break + } + + nameIdx = fmt.Sprintf("%s_%d", name, j) + } + + names[nameIdx] = struct{}{} + + testDef.Valid[i].TestDef = &testDef + testDef.Valid[i].Name = nameIdx + testDef.Valid[i].StructTest = testDef.TestKey != "" && + (testDef.BsonType != "0x05" || strings.Contains(testDef.Valid[i].Description, "0x00")) && + !testDef.Deprecated + } + + for i := len(testDef.DecodeErrors) - 1; i >= 0; i-- { + if strings.Contains(testDef.DecodeErrors[i].Description, "UTF-8") { + testDef.DecodeErrors = append(testDef.DecodeErrors[:i], testDef.DecodeErrors[i+1:]...) + continue + } + + name := cleanupFuncName(testDef.Description + "_" + testDef.DecodeErrors[i].Description) + nameIdx := name + j := 1 + for { + if _, ok := names[nameIdx]; !ok { + break + } + + nameIdx = fmt.Sprintf("%s_%d", name, j) + } + names[nameIdx] = struct{}{} + + testDef.DecodeErrors[i].Name = nameIdx + } + + return &testDef, nil +} + +func (g *Generator) getTemplate() (*template.Template, error) { + content := `package bson_test + +import ( + "encoding/hex" + "time" + + . "gopkg.in/check.v1" + "github.com/globalsign/mgo/bson" +) + +func testValid(c *C, in []byte, expected []byte, result interface{}) { + err := bson.Unmarshal(in, result) + c.Assert(err, IsNil) + + out, err := bson.Marshal(result) + c.Assert(err, IsNil) + + c.Assert(string(expected), Equals, string(out), Commentf("roundtrip failed for %T, expected '%x' but got '%x'", result, expected, out)) +} + +func testDecodeSkip(c *C, in []byte) { + err := bson.Unmarshal(in, &struct{}{}) + c.Assert(err, IsNil) +} + +func testDecodeError(c *C, in []byte, result interface{}) { + err := bson.Unmarshal(in, result) + c.Assert(err, Not(IsNil)) +} + +{{range .}} +{{range .Valid}} +func (s *S) Test{{.Name}}(c *C) { + b, err := hex.DecodeString("{{.Bson}}") + c.Assert(err, IsNil) + + {{if .CanonicalBson}} + cb, err := hex.DecodeString("{{.CanonicalBson}}") + c.Assert(err, IsNil) + {{else}} + cb := b + {{end}} + + var resultD bson.D + testValid(c, b, cb, &resultD) + {{if .StructTest}}var resultS struct { + Element {{.TestDef.GoType}} ` + "`bson:\"{{.TestDef.TestKey}}\"`" + ` + } + testValid(c, b, cb, &resultS){{end}} + + testDecodeSkip(c, b) +} +{{end}} + +{{range .DecodeErrors}} +func (s *S) Test{{.Name}}(c *C) { + b, err := hex.DecodeString("{{.Bson}}") + c.Assert(err, IsNil) + + var resultD bson.D + testDecodeError(c, b, &resultD) +} +{{end}} +{{end}} +` + tmpl, err := template.New("").Parse(content) + if err != nil { + return nil, err + } + return tmpl, nil +} + +func cleanupFuncName(name string) string { + return strings.Map(func(r rune) rune { + if (r >= 48 && r <= 57) || (r >= 65 && r <= 90) || (r >= 97 && r <= 122) { + return r + } + return '_' + }, name) +} + +type testDef struct { + Description string `json:"description"` + BsonType string `json:"bson_type"` + TestKey string `json:"test_key"` + Valid []*valid `json:"valid"` + DecodeErrors []*decodeError `json:"decodeErrors"` + Deprecated bool `json:"deprecated"` +} + +func (t *testDef) GoType() string { + switch t.BsonType { + case "0x01": + return "float64" + case "0x02": + return "string" + case "0x03": + return "bson.D" + case "0x04": + return "[]interface{}" + case "0x05": + return "[]byte" + case "0x07": + return "bson.ObjectId" + case "0x08": + return "bool" + case "0x09": + return "time.Time" + case "0x0E": + return "string" + case "0x10": + return "int32" + case "0x12": + return "int64" + case "0x13": + return "bson.Decimal" + default: + return "interface{}" + } +} + +type valid struct { + Description string `json:"description"` + Bson string `json:"bson"` + CanonicalBson string `json:"canonical_bson"` + + Name string + StructTest bool + TestDef *testDef +} + +type decodeError struct { + Description string `json:"description"` + Bson string `json:"bson"` + + Name string +} diff --git a/vendor/github.com/gobuffalo/flect/.gitignore b/vendor/github.com/gobuffalo/flect/.gitignore new file mode 100644 index 0000000000..3689718594 --- /dev/null +++ b/vendor/github.com/gobuffalo/flect/.gitignore @@ -0,0 +1,29 @@ +*.log +.DS_Store +doc +tmp +pkg +*.gem +*.pid +coverage +coverage.data +build/* +*.pbxuser +*.mode1v3 +.svn +profile +.console_history +.sass-cache/* +.rake_tasks~ +*.log.lck +solr/ +.jhw-cache/ +jhw.* +*.sublime* +node_modules/ +dist/ +generated/ +.vendor/ +bin/* +gin-bin +.idea/ diff --git a/vendor/github.com/gobuffalo/flect/.gometalinter.json b/vendor/github.com/gobuffalo/flect/.gometalinter.json new file mode 100644 index 0000000000..e4f65a36e8 --- /dev/null +++ b/vendor/github.com/gobuffalo/flect/.gometalinter.json @@ -0,0 +1,3 @@ +{ + "Enable": ["vet", "golint", "goimports", "deadcode", "gotype", "ineffassign", "misspell", "nakedret", "unconvert", "megacheck", "varcheck"] +} diff --git a/vendor/github.com/gobuffalo/flect/.travis.yml b/vendor/github.com/gobuffalo/flect/.travis.yml new file mode 100644 index 0000000000..1fb041a259 --- /dev/null +++ b/vendor/github.com/gobuffalo/flect/.travis.yml @@ -0,0 +1,36 @@ +language: go + +sudo: false + +matrix: + include: + - os: linux + go: "1.9.x" + - os: windows + go: "1.9.x" + - os: linux + go: "1.10.x" + - os: windows + go: "1.10.x" + - os: linux + go: "1.11.x" + env: + - GO111MODULE=off + - os: windows + go: "1.11.x" + env: + - GO111MODULE=off + - os: linux + go: "1.11.x" + env: + - GO111MODULE=on + - os: windows + go: "1.11.x" + env: + - GO111MODULE=on + +install: false + +script: + - go get -v -t ./... + - go test -v -timeout=5s -race ./... diff --git a/vendor/github.com/gobuffalo/flect/LICENSE b/vendor/github.com/gobuffalo/flect/LICENSE new file mode 100644 index 0000000000..649efd4372 --- /dev/null +++ b/vendor/github.com/gobuffalo/flect/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2019 Mark Bates + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/gobuffalo/flect/Makefile b/vendor/github.com/gobuffalo/flect/Makefile new file mode 100644 index 0000000000..0ac539f1c2 --- /dev/null +++ b/vendor/github.com/gobuffalo/flect/Makefile @@ -0,0 +1,61 @@ +TAGS ?= "" +GO_BIN ?= "go" + +install: + $(GO_BIN) install -tags ${TAGS} -v . + make tidy + +tidy: +ifeq ($(GO111MODULE),on) + $(GO_BIN) mod tidy +else + echo skipping go mod tidy +endif + +deps: + $(GO_BIN) get -tags ${TAGS} -t ./... + make tidy + +build: + $(GO_BIN) build -v . + make tidy + +test: + $(GO_BIN) test -cover -tags ${TAGS} ./... + make tidy + +ci-deps: + $(GO_BIN) get -tags ${TAGS} -t ./... + +ci-test: + $(GO_BIN) test -tags ${TAGS} -race ./... + +lint: + go get github.com/golangci/golangci-lint/cmd/golangci-lint + golangci-lint run --enable-all + make tidy + +update: +ifeq ($(GO111MODULE),on) + rm go.* + $(GO_BIN) mod init + $(GO_BIN) mod tidy +else + $(GO_BIN) get -u -tags ${TAGS} +endif + make test + make install + make tidy + +release-test: + $(GO_BIN) test -tags ${TAGS} -race ./... + make tidy + +release: + $(GO_BIN) get github.com/gobuffalo/release + make tidy + release -y -f version.go --skip-packr + make tidy + + + diff --git a/vendor/github.com/gobuffalo/flect/README.md b/vendor/github.com/gobuffalo/flect/README.md new file mode 100644 index 0000000000..2d9a1bd3b6 --- /dev/null +++ b/vendor/github.com/gobuffalo/flect/README.md @@ -0,0 +1,36 @@ +# Flect + +

+GoDoc +CI +Go Report Card +

+ +This is a new inflection engine to replace [https://github.com/markbates/inflect](https://github.com/markbates/inflect) designed to be more modular, more readable, and easier to fix issues on than the original. + +## Installation + +```bash +$ go get -u -v github.com/gobuffalo/flect +``` + +## `github.com/gobuffalo/flect` +GoDoc + +The `github.com/gobuffalo/flect` package contains "basic" inflection tools, like pluralization, singularization, etc... + +### The `Ident` Type + +In addition to helpful methods that take in a `string` and return a `string`, there is an `Ident` type that can be used to create new, custom, inflection rules. + +The `Ident` type contains two fields. + +* `Original` - This is the original `string` that was used to create the `Ident` +* `Parts` - This is a `[]string` that represents all of the "parts" of the string, that have been split apart, making the segments easier to work with + +Examples of creating new inflection rules using `Ident` can be found in the `github.com/gobuffalo/flect/name` package. + +## `github.com/gobuffalo/flect/name` +GoDoc + +The `github.com/gobuffalo/flect/name` package contains more "business" inflection rules like creating proper names, table names, etc... diff --git a/vendor/github.com/gobuffalo/flect/SHOULDERS.md b/vendor/github.com/gobuffalo/flect/SHOULDERS.md new file mode 100644 index 0000000000..8c359f157e --- /dev/null +++ b/vendor/github.com/gobuffalo/flect/SHOULDERS.md @@ -0,0 +1,10 @@ +# github.com/gobuffalo/flect Stands on the Shoulders of Giants + +github.com/gobuffalo/flect does not try to reinvent the wheel! Instead, it uses the already great wheels developed by the Go community and puts them all together in the best way possible. Without these giants, this project would not be possible. Please make sure to check them out and thank them for all of their hard work. + +Thank you to the following **GIANTS**: + + +* [github.com/davecgh/go-spew](https://godoc.org/github.com/davecgh/go-spew) + +* [github.com/stretchr/testify](https://godoc.org/github.com/stretchr/testify) diff --git a/vendor/github.com/gobuffalo/flect/acronyms.go b/vendor/github.com/gobuffalo/flect/acronyms.go new file mode 100644 index 0000000000..b169724a4c --- /dev/null +++ b/vendor/github.com/gobuffalo/flect/acronyms.go @@ -0,0 +1,152 @@ +package flect + +import "sync" + +var acronymsMoot = &sync.RWMutex{} + +var baseAcronyms = map[string]bool{ + "OK": true, + "UTF8": true, + "HTML": true, + "JSON": true, + "JWT": true, + "ID": true, + "UUID": true, + "SQL": true, + "ACK": true, + "ACL": true, + "ADSL": true, + "AES": true, + "ANSI": true, + "API": true, + "ARP": true, + "ATM": true, + "BGP": true, + "BSS": true, + "CCITT": true, + "CHAP": true, + "CIDR": true, + "CIR": true, + "CLI": true, + "CPE": true, + "CPU": true, + "CRC": true, + "CRT": true, + "CSMA": true, + "CMOS": true, + "DCE": true, + "DEC": true, + "DES": true, + "DHCP": true, + "DNS": true, + "DRAM": true, + "DSL": true, + "DSLAM": true, + "DTE": true, + "DMI": true, + "EHA": true, + "EIA": true, + "EIGRP": true, + "EOF": true, + "ESS": true, + "FCC": true, + "FCS": true, + "FDDI": true, + "FTP": true, + "GBIC": true, + "gbps": true, + "GEPOF": true, + "HDLC": true, + "HTTP": true, + "HTTPS": true, + "IANA": true, + "ICMP": true, + "IDF": true, + "IDS": true, + "IEEE": true, + "IETF": true, + "IMAP": true, + "IP": true, + "IPS": true, + "ISDN": true, + "ISP": true, + "kbps": true, + "LACP": true, + "LAN": true, + "LAPB": true, + "LAPF": true, + "LLC": true, + "MAC": true, + "Mbps": true, + "MC": true, + "MDF": true, + "MIB": true, + "MoCA": true, + "MPLS": true, + "MTU": true, + "NAC": true, + "NAT": true, + "NBMA": true, + "NIC": true, + "NRZ": true, + "NRZI": true, + "NVRAM": true, + "OSI": true, + "OSPF": true, + "OUI": true, + "PAP": true, + "PAT": true, + "PC": true, + "PIM": true, + "PCM": true, + "PDU": true, + "POP3": true, + "POTS": true, + "PPP": true, + "PPTP": true, + "PTT": true, + "PVST": true, + "RAM": true, + "RARP": true, + "RFC": true, + "RIP": true, + "RLL": true, + "ROM": true, + "RSTP": true, + "RTP": true, + "RCP": true, + "SDLC": true, + "SFD": true, + "SFP": true, + "SLARP": true, + "SLIP": true, + "SMTP": true, + "SNA": true, + "SNAP": true, + "SNMP": true, + "SOF": true, + "SRAM": true, + "SSH": true, + "SSID": true, + "STP": true, + "SYN": true, + "TDM": true, + "TFTP": true, + "TIA": true, + "TOFU": true, + "UDP": true, + "URL": true, + "URI": true, + "USB": true, + "UTP": true, + "VC": true, + "VLAN": true, + "VLSM": true, + "VPN": true, + "W3C": true, + "WAN": true, + "WEP": true, + "WiFi": true, + "WPA": true, + "WWW": true, +} diff --git a/vendor/github.com/gobuffalo/flect/azure-pipelines.yml b/vendor/github.com/gobuffalo/flect/azure-pipelines.yml new file mode 100644 index 0000000000..417e2c5792 --- /dev/null +++ b/vendor/github.com/gobuffalo/flect/azure-pipelines.yml @@ -0,0 +1,71 @@ +variables: + GOBIN: "$(GOPATH)/bin" # Go binaries path + GOPATH: "$(system.defaultWorkingDirectory)/gopath" # Go workspace path + modulePath: "$(GOPATH)/src/github.com/$(build.repository.name)" # Path to the module"s code + +jobs: +- job: Windows + pool: + vmImage: "vs2017-win2016" + strategy: + matrix: + go 1.10: + go_version: "1.10" + go 1.11 (on): + go_version: "1.11.5" + GO111MODULE: "on" + go 1.11 (off): + go_version: "1.11.5" + GO111MODULE: "off" + go 1.12 (on): + go_version: "1.12" + GO111MODULE: "on" + go 1.12 (off): + go_version: "1.12" + GO111MODULE: "off" + steps: + - template: azure-tests.yml + +- job: macOS + pool: + vmImage: "macOS-10.13" + strategy: + matrix: + go 1.10: + go_version: "1.10" + go 1.11 (on): + go_version: "1.11.5" + GO111MODULE: "on" + go 1.11 (off): + go_version: "1.11.5" + GO111MODULE: "off" + go 1.12 (on): + go_version: "1.12" + GO111MODULE: "on" + go 1.12 (off): + go_version: "1.12" + GO111MODULE: "off" + steps: + - template: azure-tests.yml + +- job: Linux + pool: + vmImage: "ubuntu-16.04" + strategy: + matrix: + go 1.10: + go_version: "1.10" + go 1.11 (on): + go_version: "1.11.5" + GO111MODULE: "on" + go 1.11 (off): + go_version: "1.11.5" + GO111MODULE: "off" + go 1.12 (on): + go_version: "1.12" + GO111MODULE: "on" + go 1.12 (off): + go_version: "1.12" + GO111MODULE: "off" + steps: + - template: azure-tests.yml diff --git a/vendor/github.com/gobuffalo/flect/azure-tests.yml b/vendor/github.com/gobuffalo/flect/azure-tests.yml new file mode 100644 index 0000000000..eea5822fad --- /dev/null +++ b/vendor/github.com/gobuffalo/flect/azure-tests.yml @@ -0,0 +1,19 @@ +steps: + - task: GoTool@0 + inputs: + version: $(go_version) + - task: Bash@3 + inputs: + targetType: inline + script: | + mkdir -p "$(GOBIN)" + mkdir -p "$(GOPATH)/pkg" + mkdir -p "$(modulePath)" + shopt -s extglob + mv !(gopath) "$(modulePath)" + displayName: "Setup Go Workspace" + - script: | + go get -t -v ./... + go test -race ./... + workingDirectory: "$(modulePath)" + displayName: "Tests" diff --git a/vendor/github.com/gobuffalo/flect/camelize.go b/vendor/github.com/gobuffalo/flect/camelize.go new file mode 100644 index 0000000000..8a9928e8be --- /dev/null +++ b/vendor/github.com/gobuffalo/flect/camelize.go @@ -0,0 +1,48 @@ +package flect + +import ( + "strings" + "unicode" +) + +// Camelize returns a camelize version of a string +// bob dylan = bobDylan +// widget_id = widgetID +// WidgetID = widgetID +func Camelize(s string) string { + return New(s).Camelize().String() +} + +// Camelize returns a camelize version of a string +// bob dylan = bobDylan +// widget_id = widgetID +// WidgetID = widgetID +func (i Ident) Camelize() Ident { + var out []string + for i, part := range i.Parts { + var x string + var capped bool + if strings.ToLower(part) == "id" { + out = append(out, "ID") + continue + } + for _, c := range part { + if unicode.IsLetter(c) || unicode.IsDigit(c) { + if i == 0 { + x += string(unicode.ToLower(c)) + continue + } + if !capped { + capped = true + x += string(unicode.ToUpper(c)) + continue + } + x += string(c) + } + } + if x != "" { + out = append(out, x) + } + } + return New(strings.Join(out, "")) +} diff --git a/vendor/github.com/gobuffalo/flect/capitalize.go b/vendor/github.com/gobuffalo/flect/capitalize.go new file mode 100644 index 0000000000..42ecc166cb --- /dev/null +++ b/vendor/github.com/gobuffalo/flect/capitalize.go @@ -0,0 +1,27 @@ +package flect + +import "unicode" + +// Capitalize will cap the first letter of string +// user = User +// bob dylan = Bob dylan +// widget_id = Widget_id +func Capitalize(s string) string { + return New(s).Capitalize().String() +} + +// Capitalize will cap the first letter of string +// user = User +// bob dylan = Bob dylan +// widget_id = Widget_id +func (i Ident) Capitalize() Ident { + var x string + if len(i.Parts) == 0 { + return New("") + } + x = string(unicode.ToTitle(rune(i.Original[0]))) + if len(i.Original) > 1 { + x += i.Original[1:] + } + return New(x) +} diff --git a/vendor/github.com/gobuffalo/flect/custom_data.go b/vendor/github.com/gobuffalo/flect/custom_data.go new file mode 100644 index 0000000000..9a2dfc74ad --- /dev/null +++ b/vendor/github.com/gobuffalo/flect/custom_data.go @@ -0,0 +1,83 @@ +package flect + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" +) + +func init() { + loadCustomData("inflections.json", "INFLECT_PATH", "could not read inflection file", LoadInflections) + loadCustomData("acronyms.json", "ACRONYMS_PATH", "could not read acronyms file", LoadAcronyms) +} + +//CustomDataParser are functions that parse data like acronyms or +//plurals in the shape of a io.Reader it receives. +type CustomDataParser func(io.Reader) error + +func loadCustomData(defaultFile, env, readErrorMessage string, parser CustomDataParser) { + pwd, _ := os.Getwd() + path, found := os.LookupEnv(env) + if !found { + path = filepath.Join(pwd, defaultFile) + } + + if _, err := os.Stat(path); err != nil { + return + } + + b, err := ioutil.ReadFile(path) + if err != nil { + fmt.Printf("%s %s (%s)\n", readErrorMessage, path, err) + return + } + + if err = parser(bytes.NewReader(b)); err != nil { + fmt.Println(err) + } +} + +//LoadAcronyms loads rules from io.Reader param +func LoadAcronyms(r io.Reader) error { + m := []string{} + err := json.NewDecoder(r).Decode(&m) + + if err != nil { + return fmt.Errorf("could not decode acronyms JSON from reader: %s", err) + } + + acronymsMoot.Lock() + defer acronymsMoot.Unlock() + + for _, acronym := range m { + baseAcronyms[acronym] = true + } + + return nil +} + +//LoadInflections loads rules from io.Reader param +func LoadInflections(r io.Reader) error { + m := map[string]string{} + + err := json.NewDecoder(r).Decode(&m) + if err != nil { + return fmt.Errorf("could not decode inflection JSON from reader: %s", err) + } + + pluralMoot.Lock() + defer pluralMoot.Unlock() + singularMoot.Lock() + defer singularMoot.Unlock() + + for s, p := range m { + singleToPlural[s] = p + pluralToSingle[p] = s + } + + return nil +} diff --git a/vendor/github.com/gobuffalo/flect/dasherize.go b/vendor/github.com/gobuffalo/flect/dasherize.go new file mode 100644 index 0000000000..c7a8a33e36 --- /dev/null +++ b/vendor/github.com/gobuffalo/flect/dasherize.go @@ -0,0 +1,34 @@ +package flect + +import ( + "strings" + "unicode" +) + +// Dasherize returns an alphanumeric, lowercased, dashed string +// Donald E. Knuth = donald-e-knuth +// Test with + sign = test-with-sign +// admin/WidgetID = admin-widget-id +func Dasherize(s string) string { + return New(s).Dasherize().String() +} + +// Dasherize returns an alphanumeric, lowercased, dashed string +// Donald E. Knuth = donald-e-knuth +// Test with + sign = test-with-sign +// admin/WidgetID = admin-widget-id +func (i Ident) Dasherize() Ident { + var parts []string + + for _, part := range i.Parts { + var x string + for _, c := range part { + if unicode.IsLetter(c) || unicode.IsDigit(c) { + x += string(c) + } + } + parts = xappend(parts, x) + } + + return New(strings.ToLower(strings.Join(parts, "-"))) +} diff --git a/vendor/github.com/gobuffalo/flect/flect.go b/vendor/github.com/gobuffalo/flect/flect.go new file mode 100644 index 0000000000..ee81b6f2bc --- /dev/null +++ b/vendor/github.com/gobuffalo/flect/flect.go @@ -0,0 +1,43 @@ +/* +Package flect is a new inflection engine to replace [https://github.com/markbates/inflect](https://github.com/markbates/inflect) designed to be more modular, more readable, and easier to fix issues on than the original. +*/ +package flect + +import ( + "strings" + "unicode" +) + +var spaces = []rune{'_', ' ', ':', '-', '/'} + +func isSpace(c rune) bool { + for _, r := range spaces { + if r == c { + return true + } + } + return unicode.IsSpace(c) +} + +func xappend(a []string, ss ...string) []string { + for _, s := range ss { + s = strings.TrimSpace(s) + for _, x := range spaces { + s = strings.Trim(s, string(x)) + } + if _, ok := baseAcronyms[strings.ToUpper(s)]; ok { + s = strings.ToUpper(s) + } + if s != "" { + a = append(a, s) + } + } + return a +} + +func abs(x int) int { + if x < 0 { + return -x + } + return x +} diff --git a/vendor/github.com/gobuffalo/flect/go.mod b/vendor/github.com/gobuffalo/flect/go.mod new file mode 100644 index 0000000000..cd02d074b9 --- /dev/null +++ b/vendor/github.com/gobuffalo/flect/go.mod @@ -0,0 +1,8 @@ +module github.com/gobuffalo/flect + +go 1.12 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/stretchr/testify v1.3.0 +) diff --git a/vendor/github.com/gobuffalo/flect/go.sum b/vendor/github.com/gobuffalo/flect/go.sum new file mode 100644 index 0000000000..4f76e62c1f --- /dev/null +++ b/vendor/github.com/gobuffalo/flect/go.sum @@ -0,0 +1,9 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= diff --git a/vendor/github.com/gobuffalo/flect/humanize.go b/vendor/github.com/gobuffalo/flect/humanize.go new file mode 100644 index 0000000000..ea70fe0b65 --- /dev/null +++ b/vendor/github.com/gobuffalo/flect/humanize.go @@ -0,0 +1,31 @@ +package flect + +import ( + "strings" +) + +// Humanize returns first letter of sentence capitalized +// employee_salary = Employee salary +// employee_id = employee ID +// employee_mobile_number = Employee mobile number +func Humanize(s string) string { + return New(s).Humanize().String() +} + +// Humanize First letter of sentence capitalized +func (i Ident) Humanize() Ident { + if len(i.Original) == 0 { + return New("") + } + + var parts []string + for index, part := range i.Parts { + if index == 0 { + part = strings.Title(i.Parts[0]) + } + + parts = xappend(parts, part) + } + + return New(strings.Join(parts, " ")) +} diff --git a/vendor/github.com/gobuffalo/flect/ident.go b/vendor/github.com/gobuffalo/flect/ident.go new file mode 100644 index 0000000000..78b51d4576 --- /dev/null +++ b/vendor/github.com/gobuffalo/flect/ident.go @@ -0,0 +1,106 @@ +package flect + +import ( + "encoding" + "strings" + "unicode" + "unicode/utf8" +) + +// Ident represents the string and it's parts +type Ident struct { + Original string + Parts []string +} + +// String implements fmt.Stringer and returns the original string +func (i Ident) String() string { + return i.Original +} + +// New creates a new Ident from the string +func New(s string) Ident { + i := Ident{ + Original: s, + Parts: toParts(s), + } + + return i +} + +func toParts(s string) []string { + parts := []string{} + s = strings.TrimSpace(s) + if len(s) == 0 { + return parts + } + if _, ok := baseAcronyms[strings.ToUpper(s)]; ok { + return []string{strings.ToUpper(s)} + } + var prev rune + var x string + for _, c := range s { + cs := string(c) + // fmt.Println("### cs ->", cs) + // fmt.Println("### unicode.IsControl(c) ->", unicode.IsControl(c)) + // fmt.Println("### unicode.IsDigit(c) ->", unicode.IsDigit(c)) + // fmt.Println("### unicode.IsGraphic(c) ->", unicode.IsGraphic(c)) + // fmt.Println("### unicode.IsLetter(c) ->", unicode.IsLetter(c)) + // fmt.Println("### unicode.IsLower(c) ->", unicode.IsLower(c)) + // fmt.Println("### unicode.IsMark(c) ->", unicode.IsMark(c)) + // fmt.Println("### unicode.IsPrint(c) ->", unicode.IsPrint(c)) + // fmt.Println("### unicode.IsPunct(c) ->", unicode.IsPunct(c)) + // fmt.Println("### unicode.IsSpace(c) ->", unicode.IsSpace(c)) + // fmt.Println("### unicode.IsTitle(c) ->", unicode.IsTitle(c)) + // fmt.Println("### unicode.IsUpper(c) ->", unicode.IsUpper(c)) + if !utf8.ValidRune(c) { + continue + } + + if isSpace(c) { + parts = xappend(parts, x) + x = cs + prev = c + continue + } + + if unicode.IsUpper(c) && !unicode.IsUpper(prev) { + parts = xappend(parts, x) + x = cs + prev = c + continue + } + if unicode.IsUpper(c) && baseAcronyms[strings.ToUpper(x)] { + parts = xappend(parts, x) + x = cs + prev = c + continue + } + if unicode.IsLetter(c) || unicode.IsDigit(c) || unicode.IsPunct(c) || c == '`' { + prev = c + x += cs + continue + } + + parts = xappend(parts, x) + x = "" + prev = c + } + parts = xappend(parts, x) + + return parts +} + +var _ encoding.TextUnmarshaler = &Ident{} +var _ encoding.TextMarshaler = &Ident{} + +//UnmarshalText unmarshalls byte array into the Ident +func (i *Ident) UnmarshalText(data []byte) error { + (*i) = New(string(data)) + return nil +} + +//MarshalText marshals Ident into byte array +func (i Ident) MarshalText() ([]byte, error) { + return []byte(i.Original), nil +} diff --git a/vendor/github.com/gobuffalo/flect/lower_upper.go b/vendor/github.com/gobuffalo/flect/lower_upper.go new file mode 100644 index 0000000000..930da58d8c --- /dev/null +++ b/vendor/github.com/gobuffalo/flect/lower_upper.go @@ -0,0 +1,13 @@ +package flect + +import "strings" + +// ToUpper is a convience wrapper for strings.ToUpper +func (i Ident) ToUpper() Ident { + return New(strings.ToUpper(i.Original)) +} + +// ToLower is a convience wrapper for strings.ToLower +func (i Ident) ToLower() Ident { + return New(strings.ToLower(i.Original)) +} diff --git a/vendor/github.com/gobuffalo/flect/ordinalize.go b/vendor/github.com/gobuffalo/flect/ordinalize.go new file mode 100644 index 0000000000..1ce27b3a0d --- /dev/null +++ b/vendor/github.com/gobuffalo/flect/ordinalize.go @@ -0,0 +1,43 @@ +package flect + +import ( + "fmt" + "strconv" +) + +// Ordinalize converts a number to an ordinal version +// 42 = 42nd +// 45 = 45th +// 1 = 1st +func Ordinalize(s string) string { + return New(s).Ordinalize().String() +} + +// Ordinalize converts a number to an ordinal version +// 42 = 42nd +// 45 = 45th +// 1 = 1st +func (i Ident) Ordinalize() Ident { + number, err := strconv.Atoi(i.Original) + if err != nil { + return i + } + var s string + switch abs(number) % 100 { + case 11, 12, 13: + s = fmt.Sprintf("%dth", number) + default: + switch abs(number) % 10 { + case 1: + s = fmt.Sprintf("%dst", number) + case 2: + s = fmt.Sprintf("%dnd", number) + case 3: + s = fmt.Sprintf("%drd", number) + } + } + if s != "" { + return New(s) + } + return New(fmt.Sprintf("%dth", number)) +} diff --git a/vendor/github.com/gobuffalo/flect/pascalize.go b/vendor/github.com/gobuffalo/flect/pascalize.go new file mode 100644 index 0000000000..76f0c6a706 --- /dev/null +++ b/vendor/github.com/gobuffalo/flect/pascalize.go @@ -0,0 +1,25 @@ +package flect + +import ( + "unicode" +) + +// Pascalize returns a string with each segment capitalized +// user = User +// bob dylan = BobDylan +// widget_id = WidgetID +func Pascalize(s string) string { + return New(s).Pascalize().String() +} + +// Pascalize returns a string with each segment capitalized +// user = User +// bob dylan = BobDylan +// widget_id = WidgetID +func (i Ident) Pascalize() Ident { + c := i.Camelize() + if len(c.String()) == 0 { + return c + } + return New(string(unicode.ToUpper(rune(c.Original[0]))) + c.Original[1:]) +} diff --git a/vendor/github.com/gobuffalo/flect/plural_rules.go b/vendor/github.com/gobuffalo/flect/plural_rules.go new file mode 100644 index 0000000000..1ebee22ab6 --- /dev/null +++ b/vendor/github.com/gobuffalo/flect/plural_rules.go @@ -0,0 +1,240 @@ +package flect + +var pluralRules = []rule{} + +// AddPlural adds a rule that will replace the given suffix with the replacement suffix. +func AddPlural(suffix string, repl string) { + pluralMoot.Lock() + defer pluralMoot.Unlock() + pluralRules = append(pluralRules, rule{ + suffix: suffix, + fn: func(s string) string { + s = s[:len(s)-len(suffix)] + return s + repl + }, + }) + + pluralRules = append(pluralRules, rule{ + suffix: repl, + fn: noop, + }) +} + +var singleToPlural = map[string]string{ + "human": "humans", + "matrix": "matrices", + "vertix": "vertices", + "index": "indices", + "mouse": "mice", + "louse": "lice", + "ress": "resses", + "ox": "oxen", + "quiz": "quizzes", + "series": "series", + "octopus": "octopi", + "equipment": "equipment", + "information": "information", + "rice": "rice", + "money": "money", + "species": "species", + "fish": "fish", + "sheep": "sheep", + "jeans": "jeans", + "police": "police", + "dear": "dear", + "goose": "geese", + "tooth": "teeth", + "foot": "feet", + "bus": "buses", + "fez": "fezzes", + "piano": "pianos", + "halo": "halos", + "photo": "photos", + "aircraft": "aircraft", + "alumna": "alumnae", + "alumnus": "alumni", + "analysis": "analyses", + "antenna": "antennas", + "antithesis": "antitheses", + "apex": "apexes", + "appendix": "appendices", + "axis": "axes", + "bacillus": "bacilli", + "bacterium": "bacteria", + "basis": "bases", + "beau": "beaus", + "bison": "bison", + "bureau": "bureaus", + "campus": "campuses", + "château": "châteaux", + "codex": "codices", + "concerto": "concertos", + "corpus": "corpora", + "crisis": "crises", + "curriculum": "curriculums", + "deer": "deer", + "diagnosis": "diagnoses", + "die": "dice", + "dwarf": "dwarves", + "ellipsis": "ellipses", + "erratum": "errata", + "faux pas": "faux pas", + "focus": "foci", + "formula": "formulas", + "fungus": "fungi", + "genus": "genera", + "graffito": "graffiti", + "grouse": "grouse", + "half": "halves", + "hoof": "hooves", + "hypothesis": "hypotheses", + "larva": "larvae", + "libretto": "librettos", + "loaf": "loaves", + "locus": "loci", + "minutia": "minutiae", + "moose": "moose", + "nebula": "nebulae", + "nucleus": "nuclei", + "oasis": "oases", + "offspring": "offspring", + "opus": "opera", + "parenthesis": "parentheses", + "phenomenon": "phenomena", + "phylum": "phyla", + "prognosis": "prognoses", + "radius": "radiuses", + "referendum": "referendums", + "salmon": "salmon", + "shrimp": "shrimp", + "stimulus": "stimuli", + "stratum": "strata", + "swine": "swine", + "syllabus": "syllabi", + "symposium": "symposiums", + "synopsis": "synopses", + "tableau": "tableaus", + "thesis": "theses", + "thief": "thieves", + "trout": "trout", + "tuna": "tuna", + "vertebra": "vertebrae", + "vita": "vitae", + "vortex": "vortices", + "wharf": "wharves", + "wife": "wives", + "wolf": "wolves", + "datum": "data", + "testis": "testes", + "alias": "aliases", + "house": "houses", + "shoe": "shoes", + "news": "news", + "ovum": "ova", + "foo": "foos", +} + +var pluralToSingle = map[string]string{} + +func init() { + for k, v := range singleToPlural { + pluralToSingle[v] = k + } +} +func init() { + AddPlural("campus", "campuses") + AddPlural("man", "men") + AddPlural("tz", "tzes") + AddPlural("alias", "aliases") + AddPlural("oasis", "oasis") + AddPlural("wife", "wives") + AddPlural("basis", "basis") + AddPlural("atum", "ata") + AddPlural("adium", "adia") + AddPlural("actus", "acti") + AddPlural("irus", "iri") + AddPlural("iterion", "iteria") + AddPlural("dium", "diums") + AddPlural("ovum", "ova") + AddPlural("ize", "izes") + AddPlural("dge", "dges") + AddPlural("focus", "foci") + AddPlural("child", "children") + AddPlural("oaf", "oaves") + AddPlural("randum", "randa") + AddPlural("base", "bases") + AddPlural("atus", "atuses") + AddPlural("ode", "odes") + AddPlural("person", "people") + AddPlural("va", "vae") + AddPlural("leus", "li") + AddPlural("oot", "eet") + AddPlural("oose", "eese") + AddPlural("box", "boxes") + AddPlural("ium", "ia") + AddPlural("sis", "ses") + AddPlural("nna", "nnas") + AddPlural("eses", "esis") + AddPlural("stis", "stes") + AddPlural("ex", "ices") + AddPlural("ula", "ulae") + AddPlural("isis", "ises") + AddPlural("ouses", "ouse") + AddPlural("olves", "olf") + AddPlural("lf", "lves") + AddPlural("rf", "rves") + AddPlural("afe", "aves") + AddPlural("bfe", "bves") + AddPlural("cfe", "cves") + AddPlural("dfe", "dves") + AddPlural("efe", "eves") + AddPlural("gfe", "gves") + AddPlural("hfe", "hves") + AddPlural("ife", "ives") + AddPlural("jfe", "jves") + AddPlural("kfe", "kves") + AddPlural("lfe", "lves") + AddPlural("mfe", "mves") + AddPlural("nfe", "nves") + AddPlural("ofe", "oves") + AddPlural("pfe", "pves") + AddPlural("qfe", "qves") + AddPlural("rfe", "rves") + AddPlural("sfe", "sves") + AddPlural("tfe", "tves") + AddPlural("ufe", "uves") + AddPlural("vfe", "vves") + AddPlural("wfe", "wves") + AddPlural("xfe", "xves") + AddPlural("yfe", "yves") + AddPlural("zfe", "zves") + AddPlural("hive", "hives") + AddPlural("quy", "quies") + AddPlural("by", "bies") + AddPlural("cy", "cies") + AddPlural("dy", "dies") + AddPlural("fy", "fies") + AddPlural("gy", "gies") + AddPlural("hy", "hies") + AddPlural("jy", "jies") + AddPlural("ky", "kies") + AddPlural("ly", "lies") + AddPlural("my", "mies") + AddPlural("ny", "nies") + AddPlural("py", "pies") + AddPlural("qy", "qies") + AddPlural("ry", "ries") + AddPlural("sy", "sies") + AddPlural("ty", "ties") + AddPlural("vy", "vies") + AddPlural("wy", "wies") + AddPlural("xy", "xies") + AddPlural("zy", "zies") + AddPlural("x", "xes") + AddPlural("ch", "ches") + AddPlural("ss", "sses") + AddPlural("sh", "shes") + AddPlural("oe", "oes") + AddPlural("io", "ios") + AddPlural("o", "oes") +} diff --git a/vendor/github.com/gobuffalo/flect/pluralize.go b/vendor/github.com/gobuffalo/flect/pluralize.go new file mode 100644 index 0000000000..1b9d43e462 --- /dev/null +++ b/vendor/github.com/gobuffalo/flect/pluralize.go @@ -0,0 +1,49 @@ +package flect + +import ( + "strings" + "sync" +) + +var pluralMoot = &sync.RWMutex{} + +// Pluralize returns a plural version of the string +// user = users +// person = people +// datum = data +func Pluralize(s string) string { + return New(s).Pluralize().String() +} + +// Pluralize returns a plural version of the string +// user = users +// person = people +// datum = data +func (i Ident) Pluralize() Ident { + s := i.Original + if len(s) == 0 { + return New("") + } + + pluralMoot.RLock() + defer pluralMoot.RUnlock() + + ls := strings.ToLower(s) + if _, ok := pluralToSingle[ls]; ok { + return i + } + if p, ok := singleToPlural[ls]; ok { + return New(p) + } + for _, r := range pluralRules { + if strings.HasSuffix(ls, r.suffix) { + return New(r.fn(s)) + } + } + + if strings.HasSuffix(ls, "s") { + return i + } + + return New(i.String() + "s") +} diff --git a/vendor/github.com/gobuffalo/flect/rule.go b/vendor/github.com/gobuffalo/flect/rule.go new file mode 100644 index 0000000000..dc616b337d --- /dev/null +++ b/vendor/github.com/gobuffalo/flect/rule.go @@ -0,0 +1,10 @@ +package flect + +type ruleFn func(string) string + +type rule struct { + suffix string + fn ruleFn +} + +func noop(s string) string { return s } diff --git a/vendor/github.com/gobuffalo/flect/singular_rules.go b/vendor/github.com/gobuffalo/flect/singular_rules.go new file mode 100644 index 0000000000..14b471cd00 --- /dev/null +++ b/vendor/github.com/gobuffalo/flect/singular_rules.go @@ -0,0 +1,122 @@ +package flect + +var singularRules = []rule{} + +// AddSingular adds a rule that will replace the given suffix with the replacement suffix. +func AddSingular(ext string, repl string) { + singularMoot.Lock() + defer singularMoot.Unlock() + singularRules = append(singularRules, rule{ + suffix: ext, + fn: func(s string) string { + s = s[:len(s)-len(ext)] + return s + repl + }, + }) + + singularRules = append(singularRules, rule{ + suffix: repl, + fn: func(s string) string { + return s + }, + }) +} + +func init() { + AddSingular("ria", "rion") + AddSingular("news", "news") + AddSingular("halves", "half") + AddSingular("appendix", "appendix") + AddSingular("zzes", "zz") + AddSingular("ulas", "ula") + AddSingular("psis", "pse") + AddSingular("genus", "genera") + AddSingular("phyla", "phylum") + AddSingular("odice", "odex") + AddSingular("oxen", "ox") + AddSingular("ianos", "iano") + AddSingular("ulus", "uli") + AddSingular("mice", "mouse") + AddSingular("ouses", "ouse") + AddSingular("mni", "mnus") + AddSingular("ocus", "oci") + AddSingular("shoes", "shoe") + AddSingular("oasis", "oasis") + AddSingular("lice", "louse") + AddSingular("men", "man") + AddSingular("ta", "tum") + AddSingular("ia", "ium") + AddSingular("tives", "tive") + AddSingular("ldren", "ld") + AddSingular("people", "person") + AddSingular("aves", "afe") + AddSingular("uses", "us") + AddSingular("bves", "bfe") + AddSingular("cves", "cfe") + AddSingular("dves", "dfe") + AddSingular("eves", "efe") + AddSingular("gves", "gfe") + AddSingular("hves", "hfe") + AddSingular("chives", "chive") + AddSingular("ives", "ife") + AddSingular("movies", "movie") + AddSingular("jeans", "jeans") + AddSingular("cesses", "cess") + AddSingular("cess", "cess") + AddSingular("acti", "actus") + AddSingular("itzes", "itz") + AddSingular("usses", "uss") + AddSingular("uss", "uss") + AddSingular("jves", "jfe") + AddSingular("kves", "kfe") + AddSingular("mves", "mfe") + AddSingular("nves", "nfe") + AddSingular("moves", "move") + AddSingular("oves", "ofe") + AddSingular("pves", "pfe") + AddSingular("qves", "qfe") + AddSingular("sves", "sfe") + AddSingular("tves", "tfe") + AddSingular("uves", "ufe") + AddSingular("vves", "vfe") + AddSingular("wves", "wfe") + AddSingular("xves", "xfe") + AddSingular("yves", "yfe") + AddSingular("zves", "zfe") + AddSingular("hives", "hive") + AddSingular("lves", "lf") + AddSingular("rves", "rf") + AddSingular("quies", "quy") + AddSingular("bies", "by") + AddSingular("cies", "cy") + AddSingular("dies", "dy") + AddSingular("fies", "fy") + AddSingular("gies", "gy") + AddSingular("hies", "hy") + AddSingular("jies", "jy") + AddSingular("kies", "ky") + AddSingular("lies", "ly") + AddSingular("mies", "my") + AddSingular("nies", "ny") + AddSingular("pies", "py") + AddSingular("qies", "qy") + AddSingular("ries", "ry") + AddSingular("sies", "sy") + AddSingular("ties", "ty") + AddSingular("vies", "vy") + AddSingular("wies", "wy") + AddSingular("xies", "xy") + AddSingular("zies", "zy") + AddSingular("xes", "x") + AddSingular("ches", "ch") + AddSingular("sses", "ss") + AddSingular("shes", "sh") + AddSingular("oes", "o") + AddSingular("ress", "ress") + AddSingular("iri", "irus") + AddSingular("irus", "irus") + AddSingular("tuses", "tus") + AddSingular("tus", "tus") + AddSingular("s", "") + AddSingular("ss", "ss") +} diff --git a/vendor/github.com/gobuffalo/flect/singularize.go b/vendor/github.com/gobuffalo/flect/singularize.go new file mode 100644 index 0000000000..a08cbd5862 --- /dev/null +++ b/vendor/github.com/gobuffalo/flect/singularize.go @@ -0,0 +1,44 @@ +package flect + +import ( + "strings" + "sync" +) + +var singularMoot = &sync.RWMutex{} + +// Singularize returns a singular version of the string +// users = user +// data = datum +// people = person +func Singularize(s string) string { + return New(s).Singularize().String() +} + +// Singularize returns a singular version of the string +// users = user +// data = datum +// people = person +func (i Ident) Singularize() Ident { + s := i.Original + if len(s) == 0 { + return i + } + + singularMoot.RLock() + defer singularMoot.RUnlock() + ls := strings.ToLower(s) + if p, ok := pluralToSingle[ls]; ok { + return New(p) + } + if _, ok := singleToPlural[ls]; ok { + return i + } + for _, r := range singularRules { + if strings.HasSuffix(ls, r.suffix) { + return New(r.fn(s)) + } + } + + return i +} diff --git a/vendor/github.com/gobuffalo/flect/titleize.go b/vendor/github.com/gobuffalo/flect/titleize.go new file mode 100644 index 0000000000..cbbf08a5aa --- /dev/null +++ b/vendor/github.com/gobuffalo/flect/titleize.go @@ -0,0 +1,30 @@ +package flect + +import ( + "strings" + "unicode" +) + +// Titleize will capitalize the start of each part +// "Nice to see you!" = "Nice To See You!" +// "i've read a book! have you?" = "I've Read A Book! Have You?" +// "This is `code` ok" = "This Is `code` OK" +func Titleize(s string) string { + return New(s).Titleize().String() +} + +// Titleize will capitalize the start of each part +// "Nice to see you!" = "Nice To See You!" +// "i've read a book! have you?" = "I've Read A Book! Have You?" +// "This is `code` ok" = "This Is `code` OK" +func (i Ident) Titleize() Ident { + var parts []string + for _, part := range i.Parts { + x := string(unicode.ToTitle(rune(part[0]))) + if len(part) > 1 { + x += part[1:] + } + parts = append(parts, x) + } + return New(strings.Join(parts, " ")) +} diff --git a/vendor/github.com/gobuffalo/flect/underscore.go b/vendor/github.com/gobuffalo/flect/underscore.go new file mode 100644 index 0000000000..b92488aa00 --- /dev/null +++ b/vendor/github.com/gobuffalo/flect/underscore.go @@ -0,0 +1,34 @@ +package flect + +import ( + "strings" + "unicode" +) + +// Underscore a string +// bob dylan = bob_dylan +// Nice to see you! = nice_to_see_you +// widgetID = widget_id +func Underscore(s string) string { + return New(s).Underscore().String() +} + +// Underscore a string +// bob dylan = bob_dylan +// Nice to see you! = nice_to_see_you +// widgetID = widget_id +func (i Ident) Underscore() Ident { + var out []string + for _, part := range i.Parts { + var x string + for _, c := range part { + if unicode.IsLetter(c) || unicode.IsDigit(c) { + x += string(c) + } + } + if x != "" { + out = append(out, x) + } + } + return New(strings.ToLower(strings.Join(out, "_"))) +} diff --git a/vendor/github.com/gobuffalo/flect/version.go b/vendor/github.com/gobuffalo/flect/version.go new file mode 100644 index 0000000000..e06098370d --- /dev/null +++ b/vendor/github.com/gobuffalo/flect/version.go @@ -0,0 +1,4 @@ +package flect + +//Version holds Flect version number +const Version = "v0.1.5" diff --git a/vendor/github.com/mattn/go-colorable/.travis.yml b/vendor/github.com/mattn/go-colorable/.travis.yml new file mode 100644 index 0000000000..98db8f060b --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/.travis.yml @@ -0,0 +1,9 @@ +language: go +go: + - tip + +before_install: + - go get github.com/mattn/goveralls + - go get golang.org/x/tools/cmd/cover +script: + - $HOME/gopath/bin/goveralls -repotoken xnXqRGwgW3SXIguzxf90ZSK1GPYZPaGrw diff --git a/vendor/github.com/mattn/go-colorable/LICENSE b/vendor/github.com/mattn/go-colorable/LICENSE new file mode 100644 index 0000000000..91b5cef30e --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Yasuhiro Matsumoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/mattn/go-colorable/README.md b/vendor/github.com/mattn/go-colorable/README.md new file mode 100644 index 0000000000..56729a92ca --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/README.md @@ -0,0 +1,48 @@ +# go-colorable + +[![Godoc Reference](https://godoc.org/github.com/mattn/go-colorable?status.svg)](http://godoc.org/github.com/mattn/go-colorable) +[![Build Status](https://travis-ci.org/mattn/go-colorable.svg?branch=master)](https://travis-ci.org/mattn/go-colorable) +[![Coverage Status](https://coveralls.io/repos/github/mattn/go-colorable/badge.svg?branch=master)](https://coveralls.io/github/mattn/go-colorable?branch=master) +[![Go Report Card](https://goreportcard.com/badge/mattn/go-colorable)](https://goreportcard.com/report/mattn/go-colorable) + +Colorable writer for windows. + +For example, most of logger packages doesn't show colors on windows. (I know we can do it with ansicon. But I don't want.) +This package is possible to handle escape sequence for ansi color on windows. + +## Too Bad! + +![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/bad.png) + + +## So Good! + +![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/good.png) + +## Usage + +```go +logrus.SetFormatter(&logrus.TextFormatter{ForceColors: true}) +logrus.SetOutput(colorable.NewColorableStdout()) + +logrus.Info("succeeded") +logrus.Warn("not correct") +logrus.Error("something error") +logrus.Fatal("panic") +``` + +You can compile above code on non-windows OSs. + +## Installation + +``` +$ go get github.com/mattn/go-colorable +``` + +# License + +MIT + +# Author + +Yasuhiro Matsumoto (a.k.a mattn) diff --git a/vendor/github.com/mattn/go-colorable/colorable_appengine.go b/vendor/github.com/mattn/go-colorable/colorable_appengine.go new file mode 100644 index 0000000000..1f28d773d7 --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/colorable_appengine.go @@ -0,0 +1,29 @@ +// +build appengine + +package colorable + +import ( + "io" + "os" + + _ "github.com/mattn/go-isatty" +) + +// NewColorable return new instance of Writer which handle escape sequence. +func NewColorable(file *os.File) io.Writer { + if file == nil { + panic("nil passed instead of *os.File to NewColorable()") + } + + return file +} + +// NewColorableStdout return new instance of Writer which handle escape sequence for stdout. +func NewColorableStdout() io.Writer { + return os.Stdout +} + +// NewColorableStderr return new instance of Writer which handle escape sequence for stderr. +func NewColorableStderr() io.Writer { + return os.Stderr +} diff --git a/vendor/github.com/mattn/go-colorable/colorable_others.go b/vendor/github.com/mattn/go-colorable/colorable_others.go new file mode 100644 index 0000000000..887f203dc7 --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/colorable_others.go @@ -0,0 +1,30 @@ +// +build !windows +// +build !appengine + +package colorable + +import ( + "io" + "os" + + _ "github.com/mattn/go-isatty" +) + +// NewColorable return new instance of Writer which handle escape sequence. +func NewColorable(file *os.File) io.Writer { + if file == nil { + panic("nil passed instead of *os.File to NewColorable()") + } + + return file +} + +// NewColorableStdout return new instance of Writer which handle escape sequence for stdout. +func NewColorableStdout() io.Writer { + return os.Stdout +} + +// NewColorableStderr return new instance of Writer which handle escape sequence for stderr. +func NewColorableStderr() io.Writer { + return os.Stderr +} diff --git a/vendor/github.com/mattn/go-colorable/colorable_windows.go b/vendor/github.com/mattn/go-colorable/colorable_windows.go new file mode 100644 index 0000000000..404e10ca02 --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/colorable_windows.go @@ -0,0 +1,980 @@ +// +build windows +// +build !appengine + +package colorable + +import ( + "bytes" + "io" + "math" + "os" + "strconv" + "strings" + "syscall" + "unsafe" + + "github.com/mattn/go-isatty" +) + +const ( + foregroundBlue = 0x1 + foregroundGreen = 0x2 + foregroundRed = 0x4 + foregroundIntensity = 0x8 + foregroundMask = (foregroundRed | foregroundBlue | foregroundGreen | foregroundIntensity) + backgroundBlue = 0x10 + backgroundGreen = 0x20 + backgroundRed = 0x40 + backgroundIntensity = 0x80 + backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity) +) + +const ( + genericRead = 0x80000000 + genericWrite = 0x40000000 +) + +const ( + consoleTextmodeBuffer = 0x1 +) + +type wchar uint16 +type short int16 +type dword uint32 +type word uint16 + +type coord struct { + x short + y short +} + +type smallRect struct { + left short + top short + right short + bottom short +} + +type consoleScreenBufferInfo struct { + size coord + cursorPosition coord + attributes word + window smallRect + maximumWindowSize coord +} + +type consoleCursorInfo struct { + size dword + visible int32 +} + +var ( + kernel32 = syscall.NewLazyDLL("kernel32.dll") + procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") + procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute") + procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") + procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") + procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute") + procGetConsoleCursorInfo = kernel32.NewProc("GetConsoleCursorInfo") + procSetConsoleCursorInfo = kernel32.NewProc("SetConsoleCursorInfo") + procSetConsoleTitle = kernel32.NewProc("SetConsoleTitleW") + procCreateConsoleScreenBuffer = kernel32.NewProc("CreateConsoleScreenBuffer") +) + +// Writer provide colorable Writer to the console +type Writer struct { + out io.Writer + handle syscall.Handle + althandle syscall.Handle + oldattr word + oldpos coord + rest bytes.Buffer +} + +// NewColorable return new instance of Writer which handle escape sequence from File. +func NewColorable(file *os.File) io.Writer { + if file == nil { + panic("nil passed instead of *os.File to NewColorable()") + } + + if isatty.IsTerminal(file.Fd()) { + var csbi consoleScreenBufferInfo + handle := syscall.Handle(file.Fd()) + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + return &Writer{out: file, handle: handle, oldattr: csbi.attributes, oldpos: coord{0, 0}} + } + return file +} + +// NewColorableStdout return new instance of Writer which handle escape sequence for stdout. +func NewColorableStdout() io.Writer { + return NewColorable(os.Stdout) +} + +// NewColorableStderr return new instance of Writer which handle escape sequence for stderr. +func NewColorableStderr() io.Writer { + return NewColorable(os.Stderr) +} + +var color256 = map[int]int{ + 0: 0x000000, + 1: 0x800000, + 2: 0x008000, + 3: 0x808000, + 4: 0x000080, + 5: 0x800080, + 6: 0x008080, + 7: 0xc0c0c0, + 8: 0x808080, + 9: 0xff0000, + 10: 0x00ff00, + 11: 0xffff00, + 12: 0x0000ff, + 13: 0xff00ff, + 14: 0x00ffff, + 15: 0xffffff, + 16: 0x000000, + 17: 0x00005f, + 18: 0x000087, + 19: 0x0000af, + 20: 0x0000d7, + 21: 0x0000ff, + 22: 0x005f00, + 23: 0x005f5f, + 24: 0x005f87, + 25: 0x005faf, + 26: 0x005fd7, + 27: 0x005fff, + 28: 0x008700, + 29: 0x00875f, + 30: 0x008787, + 31: 0x0087af, + 32: 0x0087d7, + 33: 0x0087ff, + 34: 0x00af00, + 35: 0x00af5f, + 36: 0x00af87, + 37: 0x00afaf, + 38: 0x00afd7, + 39: 0x00afff, + 40: 0x00d700, + 41: 0x00d75f, + 42: 0x00d787, + 43: 0x00d7af, + 44: 0x00d7d7, + 45: 0x00d7ff, + 46: 0x00ff00, + 47: 0x00ff5f, + 48: 0x00ff87, + 49: 0x00ffaf, + 50: 0x00ffd7, + 51: 0x00ffff, + 52: 0x5f0000, + 53: 0x5f005f, + 54: 0x5f0087, + 55: 0x5f00af, + 56: 0x5f00d7, + 57: 0x5f00ff, + 58: 0x5f5f00, + 59: 0x5f5f5f, + 60: 0x5f5f87, + 61: 0x5f5faf, + 62: 0x5f5fd7, + 63: 0x5f5fff, + 64: 0x5f8700, + 65: 0x5f875f, + 66: 0x5f8787, + 67: 0x5f87af, + 68: 0x5f87d7, + 69: 0x5f87ff, + 70: 0x5faf00, + 71: 0x5faf5f, + 72: 0x5faf87, + 73: 0x5fafaf, + 74: 0x5fafd7, + 75: 0x5fafff, + 76: 0x5fd700, + 77: 0x5fd75f, + 78: 0x5fd787, + 79: 0x5fd7af, + 80: 0x5fd7d7, + 81: 0x5fd7ff, + 82: 0x5fff00, + 83: 0x5fff5f, + 84: 0x5fff87, + 85: 0x5fffaf, + 86: 0x5fffd7, + 87: 0x5fffff, + 88: 0x870000, + 89: 0x87005f, + 90: 0x870087, + 91: 0x8700af, + 92: 0x8700d7, + 93: 0x8700ff, + 94: 0x875f00, + 95: 0x875f5f, + 96: 0x875f87, + 97: 0x875faf, + 98: 0x875fd7, + 99: 0x875fff, + 100: 0x878700, + 101: 0x87875f, + 102: 0x878787, + 103: 0x8787af, + 104: 0x8787d7, + 105: 0x8787ff, + 106: 0x87af00, + 107: 0x87af5f, + 108: 0x87af87, + 109: 0x87afaf, + 110: 0x87afd7, + 111: 0x87afff, + 112: 0x87d700, + 113: 0x87d75f, + 114: 0x87d787, + 115: 0x87d7af, + 116: 0x87d7d7, + 117: 0x87d7ff, + 118: 0x87ff00, + 119: 0x87ff5f, + 120: 0x87ff87, + 121: 0x87ffaf, + 122: 0x87ffd7, + 123: 0x87ffff, + 124: 0xaf0000, + 125: 0xaf005f, + 126: 0xaf0087, + 127: 0xaf00af, + 128: 0xaf00d7, + 129: 0xaf00ff, + 130: 0xaf5f00, + 131: 0xaf5f5f, + 132: 0xaf5f87, + 133: 0xaf5faf, + 134: 0xaf5fd7, + 135: 0xaf5fff, + 136: 0xaf8700, + 137: 0xaf875f, + 138: 0xaf8787, + 139: 0xaf87af, + 140: 0xaf87d7, + 141: 0xaf87ff, + 142: 0xafaf00, + 143: 0xafaf5f, + 144: 0xafaf87, + 145: 0xafafaf, + 146: 0xafafd7, + 147: 0xafafff, + 148: 0xafd700, + 149: 0xafd75f, + 150: 0xafd787, + 151: 0xafd7af, + 152: 0xafd7d7, + 153: 0xafd7ff, + 154: 0xafff00, + 155: 0xafff5f, + 156: 0xafff87, + 157: 0xafffaf, + 158: 0xafffd7, + 159: 0xafffff, + 160: 0xd70000, + 161: 0xd7005f, + 162: 0xd70087, + 163: 0xd700af, + 164: 0xd700d7, + 165: 0xd700ff, + 166: 0xd75f00, + 167: 0xd75f5f, + 168: 0xd75f87, + 169: 0xd75faf, + 170: 0xd75fd7, + 171: 0xd75fff, + 172: 0xd78700, + 173: 0xd7875f, + 174: 0xd78787, + 175: 0xd787af, + 176: 0xd787d7, + 177: 0xd787ff, + 178: 0xd7af00, + 179: 0xd7af5f, + 180: 0xd7af87, + 181: 0xd7afaf, + 182: 0xd7afd7, + 183: 0xd7afff, + 184: 0xd7d700, + 185: 0xd7d75f, + 186: 0xd7d787, + 187: 0xd7d7af, + 188: 0xd7d7d7, + 189: 0xd7d7ff, + 190: 0xd7ff00, + 191: 0xd7ff5f, + 192: 0xd7ff87, + 193: 0xd7ffaf, + 194: 0xd7ffd7, + 195: 0xd7ffff, + 196: 0xff0000, + 197: 0xff005f, + 198: 0xff0087, + 199: 0xff00af, + 200: 0xff00d7, + 201: 0xff00ff, + 202: 0xff5f00, + 203: 0xff5f5f, + 204: 0xff5f87, + 205: 0xff5faf, + 206: 0xff5fd7, + 207: 0xff5fff, + 208: 0xff8700, + 209: 0xff875f, + 210: 0xff8787, + 211: 0xff87af, + 212: 0xff87d7, + 213: 0xff87ff, + 214: 0xffaf00, + 215: 0xffaf5f, + 216: 0xffaf87, + 217: 0xffafaf, + 218: 0xffafd7, + 219: 0xffafff, + 220: 0xffd700, + 221: 0xffd75f, + 222: 0xffd787, + 223: 0xffd7af, + 224: 0xffd7d7, + 225: 0xffd7ff, + 226: 0xffff00, + 227: 0xffff5f, + 228: 0xffff87, + 229: 0xffffaf, + 230: 0xffffd7, + 231: 0xffffff, + 232: 0x080808, + 233: 0x121212, + 234: 0x1c1c1c, + 235: 0x262626, + 236: 0x303030, + 237: 0x3a3a3a, + 238: 0x444444, + 239: 0x4e4e4e, + 240: 0x585858, + 241: 0x626262, + 242: 0x6c6c6c, + 243: 0x767676, + 244: 0x808080, + 245: 0x8a8a8a, + 246: 0x949494, + 247: 0x9e9e9e, + 248: 0xa8a8a8, + 249: 0xb2b2b2, + 250: 0xbcbcbc, + 251: 0xc6c6c6, + 252: 0xd0d0d0, + 253: 0xdadada, + 254: 0xe4e4e4, + 255: 0xeeeeee, +} + +// `\033]0;TITLESTR\007` +func doTitleSequence(er *bytes.Reader) error { + var c byte + var err error + + c, err = er.ReadByte() + if err != nil { + return err + } + if c != '0' && c != '2' { + return nil + } + c, err = er.ReadByte() + if err != nil { + return err + } + if c != ';' { + return nil + } + title := make([]byte, 0, 80) + for { + c, err = er.ReadByte() + if err != nil { + return err + } + if c == 0x07 || c == '\n' { + break + } + title = append(title, c) + } + if len(title) > 0 { + title8, err := syscall.UTF16PtrFromString(string(title)) + if err == nil { + procSetConsoleTitle.Call(uintptr(unsafe.Pointer(title8))) + } + } + return nil +} + +// Write write data on console +func (w *Writer) Write(data []byte) (n int, err error) { + var csbi consoleScreenBufferInfo + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + + handle := w.handle + + var er *bytes.Reader + if w.rest.Len() > 0 { + var rest bytes.Buffer + w.rest.WriteTo(&rest) + w.rest.Reset() + rest.Write(data) + er = bytes.NewReader(rest.Bytes()) + } else { + er = bytes.NewReader(data) + } + var bw [1]byte +loop: + for { + c1, err := er.ReadByte() + if err != nil { + break loop + } + if c1 != 0x1b { + bw[0] = c1 + w.out.Write(bw[:]) + continue + } + c2, err := er.ReadByte() + if err != nil { + break loop + } + + switch c2 { + case '>': + continue + case ']': + w.rest.WriteByte(c1) + w.rest.WriteByte(c2) + er.WriteTo(&w.rest) + if bytes.IndexByte(w.rest.Bytes(), 0x07) == -1 { + break loop + } + er = bytes.NewReader(w.rest.Bytes()[2:]) + err := doTitleSequence(er) + if err != nil { + break loop + } + w.rest.Reset() + continue + // https://github.com/mattn/go-colorable/issues/27 + case '7': + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + w.oldpos = csbi.cursorPosition + continue + case '8': + procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&w.oldpos))) + continue + case 0x5b: + // execute part after switch + default: + continue + } + + w.rest.WriteByte(c1) + w.rest.WriteByte(c2) + er.WriteTo(&w.rest) + + var buf bytes.Buffer + var m byte + for i, c := range w.rest.Bytes()[2:] { + if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' { + m = c + er = bytes.NewReader(w.rest.Bytes()[2+i+1:]) + w.rest.Reset() + break + } + buf.Write([]byte(string(c))) + } + if m == 0 { + break loop + } + + switch m { + case 'A': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.y -= short(n) + procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'B': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.y += short(n) + procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'C': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x += short(n) + procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'D': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x -= short(n) + if csbi.cursorPosition.x < 0 { + csbi.cursorPosition.x = 0 + } + procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'E': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x = 0 + csbi.cursorPosition.y += short(n) + procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'F': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x = 0 + csbi.cursorPosition.y -= short(n) + procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'G': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x = short(n - 1) + procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'H', 'f': + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + if buf.Len() > 0 { + token := strings.Split(buf.String(), ";") + switch len(token) { + case 1: + n1, err := strconv.Atoi(token[0]) + if err != nil { + continue + } + csbi.cursorPosition.y = short(n1 - 1) + case 2: + n1, err := strconv.Atoi(token[0]) + if err != nil { + continue + } + n2, err := strconv.Atoi(token[1]) + if err != nil { + continue + } + csbi.cursorPosition.x = short(n2 - 1) + csbi.cursorPosition.y = short(n1 - 1) + } + } else { + csbi.cursorPosition.y = 0 + } + procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'J': + n := 0 + if buf.Len() > 0 { + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + } + var count, written dword + var cursor coord + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + switch n { + case 0: + cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} + count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.size.y-csbi.cursorPosition.y)*dword(csbi.size.x) + case 1: + cursor = coord{x: csbi.window.left, y: csbi.window.top} + count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.window.top-csbi.cursorPosition.y)*dword(csbi.size.x) + case 2: + cursor = coord{x: csbi.window.left, y: csbi.window.top} + count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.size.y-csbi.cursorPosition.y)*dword(csbi.size.x) + } + procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) + procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) + case 'K': + n := 0 + if buf.Len() > 0 { + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + } + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + var cursor coord + var count, written dword + switch n { + case 0: + cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} + count = dword(csbi.size.x - csbi.cursorPosition.x) + case 1: + cursor = coord{x: csbi.window.left, y: csbi.cursorPosition.y} + count = dword(csbi.size.x - csbi.cursorPosition.x) + case 2: + cursor = coord{x: csbi.window.left, y: csbi.cursorPosition.y} + count = dword(csbi.size.x) + } + procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) + procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) + case 'm': + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + attr := csbi.attributes + cs := buf.String() + if cs == "" { + procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(w.oldattr)) + continue + } + token := strings.Split(cs, ";") + for i := 0; i < len(token); i++ { + ns := token[i] + if n, err = strconv.Atoi(ns); err == nil { + switch { + case n == 0 || n == 100: + attr = w.oldattr + case 1 <= n && n <= 5: + attr |= foregroundIntensity + case n == 7: + attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4) + case n == 22 || n == 25: + attr |= foregroundIntensity + case n == 27: + attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4) + case 30 <= n && n <= 37: + attr &= backgroundMask + if (n-30)&1 != 0 { + attr |= foregroundRed + } + if (n-30)&2 != 0 { + attr |= foregroundGreen + } + if (n-30)&4 != 0 { + attr |= foregroundBlue + } + case n == 38: // set foreground color. + if i < len(token)-2 && (token[i+1] == "5" || token[i+1] == "05") { + if n256, err := strconv.Atoi(token[i+2]); err == nil { + if n256foreAttr == nil { + n256setup() + } + attr &= backgroundMask + attr |= n256foreAttr[n256] + i += 2 + } + } else if len(token) == 5 && token[i+1] == "2" { + var r, g, b int + r, _ = strconv.Atoi(token[i+2]) + g, _ = strconv.Atoi(token[i+3]) + b, _ = strconv.Atoi(token[i+4]) + i += 4 + if r > 127 { + attr |= foregroundRed + } + if g > 127 { + attr |= foregroundGreen + } + if b > 127 { + attr |= foregroundBlue + } + } else { + attr = attr & (w.oldattr & backgroundMask) + } + case n == 39: // reset foreground color. + attr &= backgroundMask + attr |= w.oldattr & foregroundMask + case 40 <= n && n <= 47: + attr &= foregroundMask + if (n-40)&1 != 0 { + attr |= backgroundRed + } + if (n-40)&2 != 0 { + attr |= backgroundGreen + } + if (n-40)&4 != 0 { + attr |= backgroundBlue + } + case n == 48: // set background color. + if i < len(token)-2 && token[i+1] == "5" { + if n256, err := strconv.Atoi(token[i+2]); err == nil { + if n256backAttr == nil { + n256setup() + } + attr &= foregroundMask + attr |= n256backAttr[n256] + i += 2 + } + } else if len(token) == 5 && token[i+1] == "2" { + var r, g, b int + r, _ = strconv.Atoi(token[i+2]) + g, _ = strconv.Atoi(token[i+3]) + b, _ = strconv.Atoi(token[i+4]) + i += 4 + if r > 127 { + attr |= backgroundRed + } + if g > 127 { + attr |= backgroundGreen + } + if b > 127 { + attr |= backgroundBlue + } + } else { + attr = attr & (w.oldattr & foregroundMask) + } + case n == 49: // reset foreground color. + attr &= foregroundMask + attr |= w.oldattr & backgroundMask + case 90 <= n && n <= 97: + attr = (attr & backgroundMask) + attr |= foregroundIntensity + if (n-90)&1 != 0 { + attr |= foregroundRed + } + if (n-90)&2 != 0 { + attr |= foregroundGreen + } + if (n-90)&4 != 0 { + attr |= foregroundBlue + } + case 100 <= n && n <= 107: + attr = (attr & foregroundMask) + attr |= backgroundIntensity + if (n-100)&1 != 0 { + attr |= backgroundRed + } + if (n-100)&2 != 0 { + attr |= backgroundGreen + } + if (n-100)&4 != 0 { + attr |= backgroundBlue + } + } + procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(attr)) + } + } + case 'h': + var ci consoleCursorInfo + cs := buf.String() + if cs == "5>" { + procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) + ci.visible = 0 + procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) + } else if cs == "?25" { + procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) + ci.visible = 1 + procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) + } else if cs == "?1049" { + if w.althandle == 0 { + h, _, _ := procCreateConsoleScreenBuffer.Call(uintptr(genericRead|genericWrite), 0, 0, uintptr(consoleTextmodeBuffer), 0, 0) + w.althandle = syscall.Handle(h) + if w.althandle != 0 { + handle = w.althandle + } + } + } + case 'l': + var ci consoleCursorInfo + cs := buf.String() + if cs == "5>" { + procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) + ci.visible = 1 + procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) + } else if cs == "?25" { + procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) + ci.visible = 0 + procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) + } else if cs == "?1049" { + if w.althandle != 0 { + syscall.CloseHandle(w.althandle) + w.althandle = 0 + handle = w.handle + } + } + case 's': + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + w.oldpos = csbi.cursorPosition + case 'u': + procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&w.oldpos))) + } + } + + return len(data), nil +} + +type consoleColor struct { + rgb int + red bool + green bool + blue bool + intensity bool +} + +func (c consoleColor) foregroundAttr() (attr word) { + if c.red { + attr |= foregroundRed + } + if c.green { + attr |= foregroundGreen + } + if c.blue { + attr |= foregroundBlue + } + if c.intensity { + attr |= foregroundIntensity + } + return +} + +func (c consoleColor) backgroundAttr() (attr word) { + if c.red { + attr |= backgroundRed + } + if c.green { + attr |= backgroundGreen + } + if c.blue { + attr |= backgroundBlue + } + if c.intensity { + attr |= backgroundIntensity + } + return +} + +var color16 = []consoleColor{ + {0x000000, false, false, false, false}, + {0x000080, false, false, true, false}, + {0x008000, false, true, false, false}, + {0x008080, false, true, true, false}, + {0x800000, true, false, false, false}, + {0x800080, true, false, true, false}, + {0x808000, true, true, false, false}, + {0xc0c0c0, true, true, true, false}, + {0x808080, false, false, false, true}, + {0x0000ff, false, false, true, true}, + {0x00ff00, false, true, false, true}, + {0x00ffff, false, true, true, true}, + {0xff0000, true, false, false, true}, + {0xff00ff, true, false, true, true}, + {0xffff00, true, true, false, true}, + {0xffffff, true, true, true, true}, +} + +type hsv struct { + h, s, v float32 +} + +func (a hsv) dist(b hsv) float32 { + dh := a.h - b.h + switch { + case dh > 0.5: + dh = 1 - dh + case dh < -0.5: + dh = -1 - dh + } + ds := a.s - b.s + dv := a.v - b.v + return float32(math.Sqrt(float64(dh*dh + ds*ds + dv*dv))) +} + +func toHSV(rgb int) hsv { + r, g, b := float32((rgb&0xFF0000)>>16)/256.0, + float32((rgb&0x00FF00)>>8)/256.0, + float32(rgb&0x0000FF)/256.0 + min, max := minmax3f(r, g, b) + h := max - min + if h > 0 { + if max == r { + h = (g - b) / h + if h < 0 { + h += 6 + } + } else if max == g { + h = 2 + (b-r)/h + } else { + h = 4 + (r-g)/h + } + } + h /= 6.0 + s := max - min + if max != 0 { + s /= max + } + v := max + return hsv{h: h, s: s, v: v} +} + +type hsvTable []hsv + +func toHSVTable(rgbTable []consoleColor) hsvTable { + t := make(hsvTable, len(rgbTable)) + for i, c := range rgbTable { + t[i] = toHSV(c.rgb) + } + return t +} + +func (t hsvTable) find(rgb int) consoleColor { + hsv := toHSV(rgb) + n := 7 + l := float32(5.0) + for i, p := range t { + d := hsv.dist(p) + if d < l { + l, n = d, i + } + } + return color16[n] +} + +func minmax3f(a, b, c float32) (min, max float32) { + if a < b { + if b < c { + return a, c + } else if a < c { + return a, b + } else { + return c, b + } + } else { + if a < c { + return b, c + } else if b < c { + return b, a + } else { + return c, a + } + } +} + +var n256foreAttr []word +var n256backAttr []word + +func n256setup() { + n256foreAttr = make([]word, 256) + n256backAttr = make([]word, 256) + t := toHSVTable(color16) + for i, rgb := range color256 { + c := t.find(rgb) + n256foreAttr[i] = c.foregroundAttr() + n256backAttr[i] = c.backgroundAttr() + } +} diff --git a/vendor/github.com/mattn/go-colorable/go.mod b/vendor/github.com/mattn/go-colorable/go.mod new file mode 100644 index 0000000000..ef3ca9d4c3 --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/go.mod @@ -0,0 +1,3 @@ +module github.com/mattn/go-colorable + +require github.com/mattn/go-isatty v0.0.8 diff --git a/vendor/github.com/mattn/go-colorable/go.sum b/vendor/github.com/mattn/go-colorable/go.sum new file mode 100644 index 0000000000..2c12960ec7 --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/go.sum @@ -0,0 +1,4 @@ +github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/vendor/github.com/mattn/go-colorable/noncolorable.go b/vendor/github.com/mattn/go-colorable/noncolorable.go new file mode 100644 index 0000000000..9721e16f4b --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/noncolorable.go @@ -0,0 +1,55 @@ +package colorable + +import ( + "bytes" + "io" +) + +// NonColorable hold writer but remove escape sequence. +type NonColorable struct { + out io.Writer +} + +// NewNonColorable return new instance of Writer which remove escape sequence from Writer. +func NewNonColorable(w io.Writer) io.Writer { + return &NonColorable{out: w} +} + +// Write write data on console +func (w *NonColorable) Write(data []byte) (n int, err error) { + er := bytes.NewReader(data) + var bw [1]byte +loop: + for { + c1, err := er.ReadByte() + if err != nil { + break loop + } + if c1 != 0x1b { + bw[0] = c1 + w.out.Write(bw[:]) + continue + } + c2, err := er.ReadByte() + if err != nil { + break loop + } + if c2 != 0x5b { + continue + } + + var buf bytes.Buffer + for { + c, err := er.ReadByte() + if err != nil { + break loop + } + if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' { + break + } + buf.Write([]byte(string(c))) + } + } + + return len(data), nil +} diff --git a/vendor/github.com/mattn/go-isatty/.travis.yml b/vendor/github.com/mattn/go-isatty/.travis.yml new file mode 100644 index 0000000000..5597e026dd --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/.travis.yml @@ -0,0 +1,13 @@ +language: go +go: + - tip + +os: + - linux + - osx + +before_install: + - go get github.com/mattn/goveralls + - go get golang.org/x/tools/cmd/cover +script: + - $HOME/gopath/bin/goveralls -repotoken 3gHdORO5k5ziZcWMBxnd9LrMZaJs8m9x5 diff --git a/vendor/github.com/mattn/go-isatty/LICENSE b/vendor/github.com/mattn/go-isatty/LICENSE new file mode 100644 index 0000000000..65dc692b6b --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/LICENSE @@ -0,0 +1,9 @@ +Copyright (c) Yasuhiro MATSUMOTO + +MIT License (Expat) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/mattn/go-isatty/README.md b/vendor/github.com/mattn/go-isatty/README.md new file mode 100644 index 0000000000..1e69004bb0 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/README.md @@ -0,0 +1,50 @@ +# go-isatty + +[![Godoc Reference](https://godoc.org/github.com/mattn/go-isatty?status.svg)](http://godoc.org/github.com/mattn/go-isatty) +[![Build Status](https://travis-ci.org/mattn/go-isatty.svg?branch=master)](https://travis-ci.org/mattn/go-isatty) +[![Coverage Status](https://coveralls.io/repos/github/mattn/go-isatty/badge.svg?branch=master)](https://coveralls.io/github/mattn/go-isatty?branch=master) +[![Go Report Card](https://goreportcard.com/badge/mattn/go-isatty)](https://goreportcard.com/report/mattn/go-isatty) + +isatty for golang + +## Usage + +```go +package main + +import ( + "fmt" + "github.com/mattn/go-isatty" + "os" +) + +func main() { + if isatty.IsTerminal(os.Stdout.Fd()) { + fmt.Println("Is Terminal") + } else if isatty.IsCygwinTerminal(os.Stdout.Fd()) { + fmt.Println("Is Cygwin/MSYS2 Terminal") + } else { + fmt.Println("Is Not Terminal") + } +} +``` + +## Installation + +``` +$ go get github.com/mattn/go-isatty +``` + +## License + +MIT + +## Author + +Yasuhiro Matsumoto (a.k.a mattn) + +## Thanks + +* k-takata: base idea for IsCygwinTerminal + + https://github.com/k-takata/go-iscygpty diff --git a/vendor/github.com/mattn/go-isatty/doc.go b/vendor/github.com/mattn/go-isatty/doc.go new file mode 100644 index 0000000000..17d4f90ebc --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/doc.go @@ -0,0 +1,2 @@ +// Package isatty implements interface to isatty +package isatty diff --git a/vendor/github.com/mattn/go-isatty/go.mod b/vendor/github.com/mattn/go-isatty/go.mod new file mode 100644 index 0000000000..f310320c33 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/go.mod @@ -0,0 +1,3 @@ +module github.com/mattn/go-isatty + +require golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 diff --git a/vendor/github.com/mattn/go-isatty/go.sum b/vendor/github.com/mattn/go-isatty/go.sum new file mode 100644 index 0000000000..426c8973c0 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/go.sum @@ -0,0 +1,2 @@ +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/vendor/github.com/mattn/go-isatty/isatty_android.go b/vendor/github.com/mattn/go-isatty/isatty_android.go new file mode 100644 index 0000000000..d3567cb5bf --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_android.go @@ -0,0 +1,23 @@ +// +build android + +package isatty + +import ( + "syscall" + "unsafe" +) + +const ioctlReadTermios = syscall.TCGETS + +// IsTerminal return true if the file descriptor is terminal. +func IsTerminal(fd uintptr) bool { + var termios syscall.Termios + _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) + return err == 0 +} + +// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2 +// terminal. This is also always false on this environment. +func IsCygwinTerminal(fd uintptr) bool { + return false +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_bsd.go b/vendor/github.com/mattn/go-isatty/isatty_bsd.go new file mode 100644 index 0000000000..07e93039db --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_bsd.go @@ -0,0 +1,24 @@ +// +build darwin freebsd openbsd netbsd dragonfly +// +build !appengine + +package isatty + +import ( + "syscall" + "unsafe" +) + +const ioctlReadTermios = syscall.TIOCGETA + +// IsTerminal return true if the file descriptor is terminal. +func IsTerminal(fd uintptr) bool { + var termios syscall.Termios + _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) + return err == 0 +} + +// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2 +// terminal. This is also always false on this environment. +func IsCygwinTerminal(fd uintptr) bool { + return false +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_others.go b/vendor/github.com/mattn/go-isatty/isatty_others.go new file mode 100644 index 0000000000..ff714a3761 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_others.go @@ -0,0 +1,15 @@ +// +build appengine js nacl + +package isatty + +// IsTerminal returns true if the file descriptor is terminal which +// is always false on js and appengine classic which is a sandboxed PaaS. +func IsTerminal(fd uintptr) bool { + return false +} + +// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2 +// terminal. This is also always false on this environment. +func IsCygwinTerminal(fd uintptr) bool { + return false +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_solaris.go b/vendor/github.com/mattn/go-isatty/isatty_solaris.go new file mode 100644 index 0000000000..bdd5c79a07 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_solaris.go @@ -0,0 +1,22 @@ +// +build solaris +// +build !appengine + +package isatty + +import ( + "golang.org/x/sys/unix" +) + +// IsTerminal returns true if the given file descriptor is a terminal. +// see: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libbc/libc/gen/common/isatty.c +func IsTerminal(fd uintptr) bool { + var termio unix.Termio + err := unix.IoctlSetTermio(int(fd), unix.TCGETA, &termio) + return err == nil +} + +// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2 +// terminal. This is also always false on this environment. +func IsCygwinTerminal(fd uintptr) bool { + return false +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_tcgets.go b/vendor/github.com/mattn/go-isatty/isatty_tcgets.go new file mode 100644 index 0000000000..453b025d0d --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_tcgets.go @@ -0,0 +1,19 @@ +// +build linux aix +// +build !appengine +// +build !android + +package isatty + +import "golang.org/x/sys/unix" + +// IsTerminal return true if the file descriptor is terminal. +func IsTerminal(fd uintptr) bool { + _, err := unix.IoctlGetTermios(int(fd), unix.TCGETS) + return err == nil +} + +// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2 +// terminal. This is also always false on this environment. +func IsCygwinTerminal(fd uintptr) bool { + return false +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_windows.go b/vendor/github.com/mattn/go-isatty/isatty_windows.go new file mode 100644 index 0000000000..af51cbcaa4 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_windows.go @@ -0,0 +1,94 @@ +// +build windows +// +build !appengine + +package isatty + +import ( + "strings" + "syscall" + "unicode/utf16" + "unsafe" +) + +const ( + fileNameInfo uintptr = 2 + fileTypePipe = 3 +) + +var ( + kernel32 = syscall.NewLazyDLL("kernel32.dll") + procGetConsoleMode = kernel32.NewProc("GetConsoleMode") + procGetFileInformationByHandleEx = kernel32.NewProc("GetFileInformationByHandleEx") + procGetFileType = kernel32.NewProc("GetFileType") +) + +func init() { + // Check if GetFileInformationByHandleEx is available. + if procGetFileInformationByHandleEx.Find() != nil { + procGetFileInformationByHandleEx = nil + } +} + +// IsTerminal return true if the file descriptor is terminal. +func IsTerminal(fd uintptr) bool { + var st uint32 + r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0) + return r != 0 && e == 0 +} + +// Check pipe name is used for cygwin/msys2 pty. +// Cygwin/MSYS2 PTY has a name like: +// \{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master +func isCygwinPipeName(name string) bool { + token := strings.Split(name, "-") + if len(token) < 5 { + return false + } + + if token[0] != `\msys` && token[0] != `\cygwin` { + return false + } + + if token[1] == "" { + return false + } + + if !strings.HasPrefix(token[2], "pty") { + return false + } + + if token[3] != `from` && token[3] != `to` { + return false + } + + if token[4] != "master" { + return false + } + + return true +} + +// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2 +// terminal. +func IsCygwinTerminal(fd uintptr) bool { + if procGetFileInformationByHandleEx == nil { + return false + } + + // Cygwin/msys's pty is a pipe. + ft, _, e := syscall.Syscall(procGetFileType.Addr(), 1, fd, 0, 0) + if ft != fileTypePipe || e != 0 { + return false + } + + var buf [2 + syscall.MAX_PATH]uint16 + r, _, e := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(), + 4, fd, fileNameInfo, uintptr(unsafe.Pointer(&buf)), + uintptr(len(buf)*2), 0, 0) + if r == 0 || e != 0 { + return false + } + + l := *(*uint32)(unsafe.Pointer(&buf)) + return isCygwinPipeName(string(utf16.Decode(buf[2 : 2+l/2]))) +} diff --git a/vendor/github.com/mikefarah/yaml/v2/.travis.yml b/vendor/github.com/mikefarah/yaml/v2/.travis.yml new file mode 100644 index 0000000000..9f556934d8 --- /dev/null +++ b/vendor/github.com/mikefarah/yaml/v2/.travis.yml @@ -0,0 +1,12 @@ +language: go + +go: + - 1.4 + - 1.5 + - 1.6 + - 1.7 + - 1.8 + - 1.9 + - tip + +go_import_path: gopkg.in/yaml.v2 diff --git a/vendor/github.com/mikefarah/yaml/v2/LICENSE b/vendor/github.com/mikefarah/yaml/v2/LICENSE new file mode 100644 index 0000000000..8dada3edaf --- /dev/null +++ b/vendor/github.com/mikefarah/yaml/v2/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. diff --git a/vendor/github.com/mikefarah/yaml/v2/LICENSE.libyaml b/vendor/github.com/mikefarah/yaml/v2/LICENSE.libyaml new file mode 100644 index 0000000000..8da58fbf6f --- /dev/null +++ b/vendor/github.com/mikefarah/yaml/v2/LICENSE.libyaml @@ -0,0 +1,31 @@ +The following files were ported to Go from C files of libyaml, and thus +are still covered by their original copyright and license: + + apic.go + emitterc.go + parserc.go + readerc.go + scannerc.go + writerc.go + yamlh.go + yamlprivateh.go + +Copyright (c) 2006 Kirill Simonov + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/mikefarah/yaml/v2/NOTICE b/vendor/github.com/mikefarah/yaml/v2/NOTICE new file mode 100644 index 0000000000..866d74a7ad --- /dev/null +++ b/vendor/github.com/mikefarah/yaml/v2/NOTICE @@ -0,0 +1,13 @@ +Copyright 2011-2016 Canonical Ltd. + +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. diff --git a/vendor/github.com/mikefarah/yaml/v2/README.md b/vendor/github.com/mikefarah/yaml/v2/README.md new file mode 100644 index 0000000000..b50c6e8775 --- /dev/null +++ b/vendor/github.com/mikefarah/yaml/v2/README.md @@ -0,0 +1,133 @@ +# YAML support for the Go language + +Introduction +------------ + +The yaml package enables Go programs to comfortably encode and decode YAML +values. It was developed within [Canonical](https://www.canonical.com) as +part of the [juju](https://juju.ubuntu.com) project, and is based on a +pure Go port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML) +C library to parse and generate YAML data quickly and reliably. + +Compatibility +------------- + +The yaml package supports most of YAML 1.1 and 1.2, including support for +anchors, tags, map merging, etc. Multi-document unmarshalling is not yet +implemented, and base-60 floats from YAML 1.1 are purposefully not +supported since they're a poor design and are gone in YAML 1.2. + +Installation and usage +---------------------- + +The import path for the package is *gopkg.in/yaml.v2*. + +To install it, run: + + go get gopkg.in/yaml.v2 + +API documentation +----------------- + +If opened in a browser, the import path itself leads to the API documentation: + + * [https://gopkg.in/yaml.v2](https://gopkg.in/yaml.v2) + +API stability +------------- + +The package API for yaml v2 will remain stable as described in [gopkg.in](https://gopkg.in). + + +License +------- + +The yaml package is licensed under the Apache License 2.0. Please see the LICENSE file for details. + + +Example +------- + +```Go +package main + +import ( + "fmt" + "log" + + "gopkg.in/yaml.v2" +) + +var data = ` +a: Easy! +b: + c: 2 + d: [3, 4] +` + +// Note: struct fields must be public in order for unmarshal to +// correctly populate the data. +type T struct { + A string + B struct { + RenamedC int `yaml:"c"` + D []int `yaml:",flow"` + } +} + +func main() { + t := T{} + + err := yaml.Unmarshal([]byte(data), &t) + if err != nil { + log.Fatalf("error: %v", err) + } + fmt.Printf("--- t:\n%v\n\n", t) + + d, err := yaml.Marshal(&t) + if err != nil { + log.Fatalf("error: %v", err) + } + fmt.Printf("--- t dump:\n%s\n\n", string(d)) + + m := make(map[interface{}]interface{}) + + err = yaml.Unmarshal([]byte(data), &m) + if err != nil { + log.Fatalf("error: %v", err) + } + fmt.Printf("--- m:\n%v\n\n", m) + + d, err = yaml.Marshal(&m) + if err != nil { + log.Fatalf("error: %v", err) + } + fmt.Printf("--- m dump:\n%s\n\n", string(d)) +} +``` + +This example will generate the following output: + +``` +--- t: +{Easy! {2 [3 4]}} + +--- t dump: +a: Easy! +b: + c: 2 + d: [3, 4] + + +--- m: +map[a:Easy! b:map[c:2 d:[3 4]]] + +--- m dump: +a: Easy! +b: + c: 2 + d: + - 3 + - 4 +``` + diff --git a/vendor/github.com/mikefarah/yaml/v2/apic.go b/vendor/github.com/mikefarah/yaml/v2/apic.go new file mode 100644 index 0000000000..1f7e87e672 --- /dev/null +++ b/vendor/github.com/mikefarah/yaml/v2/apic.go @@ -0,0 +1,739 @@ +package yaml + +import ( + "io" +) + +func yaml_insert_token(parser *yaml_parser_t, pos int, token *yaml_token_t) { + //fmt.Println("yaml_insert_token", "pos:", pos, "typ:", token.typ, "head:", parser.tokens_head, "len:", len(parser.tokens)) + + // Check if we can move the queue at the beginning of the buffer. + if parser.tokens_head > 0 && len(parser.tokens) == cap(parser.tokens) { + if parser.tokens_head != len(parser.tokens) { + copy(parser.tokens, parser.tokens[parser.tokens_head:]) + } + parser.tokens = parser.tokens[:len(parser.tokens)-parser.tokens_head] + parser.tokens_head = 0 + } + parser.tokens = append(parser.tokens, *token) + if pos < 0 { + return + } + copy(parser.tokens[parser.tokens_head+pos+1:], parser.tokens[parser.tokens_head+pos:]) + parser.tokens[parser.tokens_head+pos] = *token +} + +// Create a new parser object. +func yaml_parser_initialize(parser *yaml_parser_t) bool { + *parser = yaml_parser_t{ + raw_buffer: make([]byte, 0, input_raw_buffer_size), + buffer: make([]byte, 0, input_buffer_size), + } + return true +} + +// Destroy a parser object. +func yaml_parser_delete(parser *yaml_parser_t) { + *parser = yaml_parser_t{} +} + +// String read handler. +func yaml_string_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) { + if parser.input_pos == len(parser.input) { + return 0, io.EOF + } + n = copy(buffer, parser.input[parser.input_pos:]) + parser.input_pos += n + return n, nil +} + +// Reader read handler. +func yaml_reader_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) { + return parser.input_reader.Read(buffer) +} + +// Set a string input. +func yaml_parser_set_input_string(parser *yaml_parser_t, input []byte) { + if parser.read_handler != nil { + panic("must set the input source only once") + } + parser.read_handler = yaml_string_read_handler + parser.input = input + parser.input_pos = 0 +} + +// Set a file input. +func yaml_parser_set_input_reader(parser *yaml_parser_t, r io.Reader) { + if parser.read_handler != nil { + panic("must set the input source only once") + } + parser.read_handler = yaml_reader_read_handler + parser.input_reader = r +} + +// Set the source encoding. +func yaml_parser_set_encoding(parser *yaml_parser_t, encoding yaml_encoding_t) { + if parser.encoding != yaml_ANY_ENCODING { + panic("must set the encoding only once") + } + parser.encoding = encoding +} + +// Create a new emitter object. +func yaml_emitter_initialize(emitter *yaml_emitter_t) { + *emitter = yaml_emitter_t{ + buffer: make([]byte, output_buffer_size), + raw_buffer: make([]byte, 0, output_raw_buffer_size), + states: make([]yaml_emitter_state_t, 0, initial_stack_size), + events: make([]yaml_event_t, 0, initial_queue_size), + } +} + +// Destroy an emitter object. +func yaml_emitter_delete(emitter *yaml_emitter_t) { + *emitter = yaml_emitter_t{} +} + +// String write handler. +func yaml_string_write_handler(emitter *yaml_emitter_t, buffer []byte) error { + *emitter.output_buffer = append(*emitter.output_buffer, buffer...) + return nil +} + +// yaml_writer_write_handler uses emitter.output_writer to write the +// emitted text. +func yaml_writer_write_handler(emitter *yaml_emitter_t, buffer []byte) error { + _, err := emitter.output_writer.Write(buffer) + return err +} + +// Set a string output. +func yaml_emitter_set_output_string(emitter *yaml_emitter_t, output_buffer *[]byte) { + if emitter.write_handler != nil { + panic("must set the output target only once") + } + emitter.write_handler = yaml_string_write_handler + emitter.output_buffer = output_buffer +} + +// Set a file output. +func yaml_emitter_set_output_writer(emitter *yaml_emitter_t, w io.Writer) { + if emitter.write_handler != nil { + panic("must set the output target only once") + } + emitter.write_handler = yaml_writer_write_handler + emitter.output_writer = w +} + +// Set the output encoding. +func yaml_emitter_set_encoding(emitter *yaml_emitter_t, encoding yaml_encoding_t) { + if emitter.encoding != yaml_ANY_ENCODING { + panic("must set the output encoding only once") + } + emitter.encoding = encoding +} + +// Set the canonical output style. +func yaml_emitter_set_canonical(emitter *yaml_emitter_t, canonical bool) { + emitter.canonical = canonical +} + +//// Set the indentation increment. +func yaml_emitter_set_indent(emitter *yaml_emitter_t, indent int) { + if indent < 2 || indent > 9 { + indent = 2 + } + emitter.best_indent = indent +} + +// Set the preferred line width. +func yaml_emitter_set_width(emitter *yaml_emitter_t, width int) { + if width < 0 { + width = -1 + } + emitter.best_width = width +} + +// Set if unescaped non-ASCII characters are allowed. +func yaml_emitter_set_unicode(emitter *yaml_emitter_t, unicode bool) { + emitter.unicode = unicode +} + +// Set the preferred line break character. +func yaml_emitter_set_break(emitter *yaml_emitter_t, line_break yaml_break_t) { + emitter.line_break = line_break +} + +///* +// * Destroy a token object. +// */ +// +//YAML_DECLARE(void) +//yaml_token_delete(yaml_token_t *token) +//{ +// assert(token); // Non-NULL token object expected. +// +// switch (token.type) +// { +// case YAML_TAG_DIRECTIVE_TOKEN: +// yaml_free(token.data.tag_directive.handle); +// yaml_free(token.data.tag_directive.prefix); +// break; +// +// case YAML_ALIAS_TOKEN: +// yaml_free(token.data.alias.value); +// break; +// +// case YAML_ANCHOR_TOKEN: +// yaml_free(token.data.anchor.value); +// break; +// +// case YAML_TAG_TOKEN: +// yaml_free(token.data.tag.handle); +// yaml_free(token.data.tag.suffix); +// break; +// +// case YAML_SCALAR_TOKEN: +// yaml_free(token.data.scalar.value); +// break; +// +// default: +// break; +// } +// +// memset(token, 0, sizeof(yaml_token_t)); +//} +// +///* +// * Check if a string is a valid UTF-8 sequence. +// * +// * Check 'reader.c' for more details on UTF-8 encoding. +// */ +// +//static int +//yaml_check_utf8(yaml_char_t *start, size_t length) +//{ +// yaml_char_t *end = start+length; +// yaml_char_t *pointer = start; +// +// while (pointer < end) { +// unsigned char octet; +// unsigned int width; +// unsigned int value; +// size_t k; +// +// octet = pointer[0]; +// width = (octet & 0x80) == 0x00 ? 1 : +// (octet & 0xE0) == 0xC0 ? 2 : +// (octet & 0xF0) == 0xE0 ? 3 : +// (octet & 0xF8) == 0xF0 ? 4 : 0; +// value = (octet & 0x80) == 0x00 ? octet & 0x7F : +// (octet & 0xE0) == 0xC0 ? octet & 0x1F : +// (octet & 0xF0) == 0xE0 ? octet & 0x0F : +// (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0; +// if (!width) return 0; +// if (pointer+width > end) return 0; +// for (k = 1; k < width; k ++) { +// octet = pointer[k]; +// if ((octet & 0xC0) != 0x80) return 0; +// value = (value << 6) + (octet & 0x3F); +// } +// if (!((width == 1) || +// (width == 2 && value >= 0x80) || +// (width == 3 && value >= 0x800) || +// (width == 4 && value >= 0x10000))) return 0; +// +// pointer += width; +// } +// +// return 1; +//} +// + +// Create STREAM-START. +func yaml_stream_start_event_initialize(event *yaml_event_t, encoding yaml_encoding_t) { + *event = yaml_event_t{ + typ: yaml_STREAM_START_EVENT, + encoding: encoding, + } +} + +// Create STREAM-END. +func yaml_stream_end_event_initialize(event *yaml_event_t) { + *event = yaml_event_t{ + typ: yaml_STREAM_END_EVENT, + } +} + +// Create DOCUMENT-START. +func yaml_document_start_event_initialize( + event *yaml_event_t, + version_directive *yaml_version_directive_t, + tag_directives []yaml_tag_directive_t, + implicit bool, +) { + *event = yaml_event_t{ + typ: yaml_DOCUMENT_START_EVENT, + version_directive: version_directive, + tag_directives: tag_directives, + implicit: implicit, + } +} + +// Create DOCUMENT-END. +func yaml_document_end_event_initialize(event *yaml_event_t, implicit bool) { + *event = yaml_event_t{ + typ: yaml_DOCUMENT_END_EVENT, + implicit: implicit, + } +} + +///* +// * Create ALIAS. +// */ +// +//YAML_DECLARE(int) +//yaml_alias_event_initialize(event *yaml_event_t, anchor *yaml_char_t) +//{ +// mark yaml_mark_t = { 0, 0, 0 } +// anchor_copy *yaml_char_t = NULL +// +// assert(event) // Non-NULL event object is expected. +// assert(anchor) // Non-NULL anchor is expected. +// +// if (!yaml_check_utf8(anchor, strlen((char *)anchor))) return 0 +// +// anchor_copy = yaml_strdup(anchor) +// if (!anchor_copy) +// return 0 +// +// ALIAS_EVENT_INIT(*event, anchor_copy, mark, mark) +// +// return 1 +//} + +// Create SCALAR. +func yaml_scalar_event_initialize(event *yaml_event_t, anchor, tag, value []byte, plain_implicit, quoted_implicit bool, style yaml_scalar_style_t) bool { + *event = yaml_event_t{ + typ: yaml_SCALAR_EVENT, + anchor: anchor, + tag: tag, + value: value, + implicit: plain_implicit, + quoted_implicit: quoted_implicit, + style: yaml_style_t(style), + } + return true +} + +// Create SEQUENCE-START. +func yaml_sequence_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_sequence_style_t) bool { + *event = yaml_event_t{ + typ: yaml_SEQUENCE_START_EVENT, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(style), + } + return true +} + +// Create SEQUENCE-END. +func yaml_sequence_end_event_initialize(event *yaml_event_t) bool { + *event = yaml_event_t{ + typ: yaml_SEQUENCE_END_EVENT, + } + return true +} + +// Create MAPPING-START. +func yaml_mapping_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_mapping_style_t) { + *event = yaml_event_t{ + typ: yaml_MAPPING_START_EVENT, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(style), + } +} + +// Create MAPPING-END. +func yaml_mapping_end_event_initialize(event *yaml_event_t) { + *event = yaml_event_t{ + typ: yaml_MAPPING_END_EVENT, + } +} + +// Destroy an event object. +func yaml_event_delete(event *yaml_event_t) { + *event = yaml_event_t{} +} + +///* +// * Create a document object. +// */ +// +//YAML_DECLARE(int) +//yaml_document_initialize(document *yaml_document_t, +// version_directive *yaml_version_directive_t, +// tag_directives_start *yaml_tag_directive_t, +// tag_directives_end *yaml_tag_directive_t, +// start_implicit int, end_implicit int) +//{ +// struct { +// error yaml_error_type_t +// } context +// struct { +// start *yaml_node_t +// end *yaml_node_t +// top *yaml_node_t +// } nodes = { NULL, NULL, NULL } +// version_directive_copy *yaml_version_directive_t = NULL +// struct { +// start *yaml_tag_directive_t +// end *yaml_tag_directive_t +// top *yaml_tag_directive_t +// } tag_directives_copy = { NULL, NULL, NULL } +// value yaml_tag_directive_t = { NULL, NULL } +// mark yaml_mark_t = { 0, 0, 0 } +// +// assert(document) // Non-NULL document object is expected. +// assert((tag_directives_start && tag_directives_end) || +// (tag_directives_start == tag_directives_end)) +// // Valid tag directives are expected. +// +// if (!STACK_INIT(&context, nodes, INITIAL_STACK_SIZE)) goto error +// +// if (version_directive) { +// version_directive_copy = yaml_malloc(sizeof(yaml_version_directive_t)) +// if (!version_directive_copy) goto error +// version_directive_copy.major = version_directive.major +// version_directive_copy.minor = version_directive.minor +// } +// +// if (tag_directives_start != tag_directives_end) { +// tag_directive *yaml_tag_directive_t +// if (!STACK_INIT(&context, tag_directives_copy, INITIAL_STACK_SIZE)) +// goto error +// for (tag_directive = tag_directives_start +// tag_directive != tag_directives_end; tag_directive ++) { +// assert(tag_directive.handle) +// assert(tag_directive.prefix) +// if (!yaml_check_utf8(tag_directive.handle, +// strlen((char *)tag_directive.handle))) +// goto error +// if (!yaml_check_utf8(tag_directive.prefix, +// strlen((char *)tag_directive.prefix))) +// goto error +// value.handle = yaml_strdup(tag_directive.handle) +// value.prefix = yaml_strdup(tag_directive.prefix) +// if (!value.handle || !value.prefix) goto error +// if (!PUSH(&context, tag_directives_copy, value)) +// goto error +// value.handle = NULL +// value.prefix = NULL +// } +// } +// +// DOCUMENT_INIT(*document, nodes.start, nodes.end, version_directive_copy, +// tag_directives_copy.start, tag_directives_copy.top, +// start_implicit, end_implicit, mark, mark) +// +// return 1 +// +//error: +// STACK_DEL(&context, nodes) +// yaml_free(version_directive_copy) +// while (!STACK_EMPTY(&context, tag_directives_copy)) { +// value yaml_tag_directive_t = POP(&context, tag_directives_copy) +// yaml_free(value.handle) +// yaml_free(value.prefix) +// } +// STACK_DEL(&context, tag_directives_copy) +// yaml_free(value.handle) +// yaml_free(value.prefix) +// +// return 0 +//} +// +///* +// * Destroy a document object. +// */ +// +//YAML_DECLARE(void) +//yaml_document_delete(document *yaml_document_t) +//{ +// struct { +// error yaml_error_type_t +// } context +// tag_directive *yaml_tag_directive_t +// +// context.error = YAML_NO_ERROR // Eliminate a compiler warning. +// +// assert(document) // Non-NULL document object is expected. +// +// while (!STACK_EMPTY(&context, document.nodes)) { +// node yaml_node_t = POP(&context, document.nodes) +// yaml_free(node.tag) +// switch (node.type) { +// case YAML_SCALAR_NODE: +// yaml_free(node.data.scalar.value) +// break +// case YAML_SEQUENCE_NODE: +// STACK_DEL(&context, node.data.sequence.items) +// break +// case YAML_MAPPING_NODE: +// STACK_DEL(&context, node.data.mapping.pairs) +// break +// default: +// assert(0) // Should not happen. +// } +// } +// STACK_DEL(&context, document.nodes) +// +// yaml_free(document.version_directive) +// for (tag_directive = document.tag_directives.start +// tag_directive != document.tag_directives.end +// tag_directive++) { +// yaml_free(tag_directive.handle) +// yaml_free(tag_directive.prefix) +// } +// yaml_free(document.tag_directives.start) +// +// memset(document, 0, sizeof(yaml_document_t)) +//} +// +///** +// * Get a document node. +// */ +// +//YAML_DECLARE(yaml_node_t *) +//yaml_document_get_node(document *yaml_document_t, index int) +//{ +// assert(document) // Non-NULL document object is expected. +// +// if (index > 0 && document.nodes.start + index <= document.nodes.top) { +// return document.nodes.start + index - 1 +// } +// return NULL +//} +// +///** +// * Get the root object. +// */ +// +//YAML_DECLARE(yaml_node_t *) +//yaml_document_get_root_node(document *yaml_document_t) +//{ +// assert(document) // Non-NULL document object is expected. +// +// if (document.nodes.top != document.nodes.start) { +// return document.nodes.start +// } +// return NULL +//} +// +///* +// * Add a scalar node to a document. +// */ +// +//YAML_DECLARE(int) +//yaml_document_add_scalar(document *yaml_document_t, +// tag *yaml_char_t, value *yaml_char_t, length int, +// style yaml_scalar_style_t) +//{ +// struct { +// error yaml_error_type_t +// } context +// mark yaml_mark_t = { 0, 0, 0 } +// tag_copy *yaml_char_t = NULL +// value_copy *yaml_char_t = NULL +// node yaml_node_t +// +// assert(document) // Non-NULL document object is expected. +// assert(value) // Non-NULL value is expected. +// +// if (!tag) { +// tag = (yaml_char_t *)YAML_DEFAULT_SCALAR_TAG +// } +// +// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error +// tag_copy = yaml_strdup(tag) +// if (!tag_copy) goto error +// +// if (length < 0) { +// length = strlen((char *)value) +// } +// +// if (!yaml_check_utf8(value, length)) goto error +// value_copy = yaml_malloc(length+1) +// if (!value_copy) goto error +// memcpy(value_copy, value, length) +// value_copy[length] = '\0' +// +// SCALAR_NODE_INIT(node, tag_copy, value_copy, length, style, mark, mark) +// if (!PUSH(&context, document.nodes, node)) goto error +// +// return document.nodes.top - document.nodes.start +// +//error: +// yaml_free(tag_copy) +// yaml_free(value_copy) +// +// return 0 +//} +// +///* +// * Add a sequence node to a document. +// */ +// +//YAML_DECLARE(int) +//yaml_document_add_sequence(document *yaml_document_t, +// tag *yaml_char_t, style yaml_sequence_style_t) +//{ +// struct { +// error yaml_error_type_t +// } context +// mark yaml_mark_t = { 0, 0, 0 } +// tag_copy *yaml_char_t = NULL +// struct { +// start *yaml_node_item_t +// end *yaml_node_item_t +// top *yaml_node_item_t +// } items = { NULL, NULL, NULL } +// node yaml_node_t +// +// assert(document) // Non-NULL document object is expected. +// +// if (!tag) { +// tag = (yaml_char_t *)YAML_DEFAULT_SEQUENCE_TAG +// } +// +// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error +// tag_copy = yaml_strdup(tag) +// if (!tag_copy) goto error +// +// if (!STACK_INIT(&context, items, INITIAL_STACK_SIZE)) goto error +// +// SEQUENCE_NODE_INIT(node, tag_copy, items.start, items.end, +// style, mark, mark) +// if (!PUSH(&context, document.nodes, node)) goto error +// +// return document.nodes.top - document.nodes.start +// +//error: +// STACK_DEL(&context, items) +// yaml_free(tag_copy) +// +// return 0 +//} +// +///* +// * Add a mapping node to a document. +// */ +// +//YAML_DECLARE(int) +//yaml_document_add_mapping(document *yaml_document_t, +// tag *yaml_char_t, style yaml_mapping_style_t) +//{ +// struct { +// error yaml_error_type_t +// } context +// mark yaml_mark_t = { 0, 0, 0 } +// tag_copy *yaml_char_t = NULL +// struct { +// start *yaml_node_pair_t +// end *yaml_node_pair_t +// top *yaml_node_pair_t +// } pairs = { NULL, NULL, NULL } +// node yaml_node_t +// +// assert(document) // Non-NULL document object is expected. +// +// if (!tag) { +// tag = (yaml_char_t *)YAML_DEFAULT_MAPPING_TAG +// } +// +// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error +// tag_copy = yaml_strdup(tag) +// if (!tag_copy) goto error +// +// if (!STACK_INIT(&context, pairs, INITIAL_STACK_SIZE)) goto error +// +// MAPPING_NODE_INIT(node, tag_copy, pairs.start, pairs.end, +// style, mark, mark) +// if (!PUSH(&context, document.nodes, node)) goto error +// +// return document.nodes.top - document.nodes.start +// +//error: +// STACK_DEL(&context, pairs) +// yaml_free(tag_copy) +// +// return 0 +//} +// +///* +// * Append an item to a sequence node. +// */ +// +//YAML_DECLARE(int) +//yaml_document_append_sequence_item(document *yaml_document_t, +// sequence int, item int) +//{ +// struct { +// error yaml_error_type_t +// } context +// +// assert(document) // Non-NULL document is required. +// assert(sequence > 0 +// && document.nodes.start + sequence <= document.nodes.top) +// // Valid sequence id is required. +// assert(document.nodes.start[sequence-1].type == YAML_SEQUENCE_NODE) +// // A sequence node is required. +// assert(item > 0 && document.nodes.start + item <= document.nodes.top) +// // Valid item id is required. +// +// if (!PUSH(&context, +// document.nodes.start[sequence-1].data.sequence.items, item)) +// return 0 +// +// return 1 +//} +// +///* +// * Append a pair of a key and a value to a mapping node. +// */ +// +//YAML_DECLARE(int) +//yaml_document_append_mapping_pair(document *yaml_document_t, +// mapping int, key int, value int) +//{ +// struct { +// error yaml_error_type_t +// } context +// +// pair yaml_node_pair_t +// +// assert(document) // Non-NULL document is required. +// assert(mapping > 0 +// && document.nodes.start + mapping <= document.nodes.top) +// // Valid mapping id is required. +// assert(document.nodes.start[mapping-1].type == YAML_MAPPING_NODE) +// // A mapping node is required. +// assert(key > 0 && document.nodes.start + key <= document.nodes.top) +// // Valid key id is required. +// assert(value > 0 && document.nodes.start + value <= document.nodes.top) +// // Valid value id is required. +// +// pair.key = key +// pair.value = value +// +// if (!PUSH(&context, +// document.nodes.start[mapping-1].data.mapping.pairs, pair)) +// return 0 +// +// return 1 +//} +// +// diff --git a/vendor/github.com/mikefarah/yaml/v2/decode.go b/vendor/github.com/mikefarah/yaml/v2/decode.go new file mode 100644 index 0000000000..71c366554e --- /dev/null +++ b/vendor/github.com/mikefarah/yaml/v2/decode.go @@ -0,0 +1,776 @@ +package yaml + +import ( + "encoding" + "encoding/base64" + "fmt" + "io" + "math" + "reflect" + "strconv" + "time" +) + +const ( + documentNode = 1 << iota + mappingNode + sequenceNode + scalarNode + aliasNode +) + +type node struct { + kind int + line, column int + tag string + // For an alias node, alias holds the resolved alias. + alias *node + value string + implicit bool + children []*node + anchors map[string]*node +} + +// ---------------------------------------------------------------------------- +// Parser, produces a node tree out of a libyaml event stream. + +type parser struct { + parser yaml_parser_t + event yaml_event_t + doc *node + doneInit bool +} + +func newParser(b []byte) *parser { + p := parser{} + if !yaml_parser_initialize(&p.parser) { + panic("failed to initialize YAML emitter") + } + if len(b) == 0 { + b = []byte{'\n'} + } + yaml_parser_set_input_string(&p.parser, b) + return &p +} + +func newParserFromReader(r io.Reader) *parser { + p := parser{} + if !yaml_parser_initialize(&p.parser) { + panic("failed to initialize YAML emitter") + } + yaml_parser_set_input_reader(&p.parser, r) + return &p +} + +func (p *parser) init() { + if p.doneInit { + return + } + p.expect(yaml_STREAM_START_EVENT) + p.doneInit = true +} + +func (p *parser) destroy() { + if p.event.typ != yaml_NO_EVENT { + yaml_event_delete(&p.event) + } + yaml_parser_delete(&p.parser) +} + +// expect consumes an event from the event stream and +// checks that it's of the expected type. +func (p *parser) expect(e yaml_event_type_t) { + if p.event.typ == yaml_NO_EVENT { + if !yaml_parser_parse(&p.parser, &p.event) { + p.fail() + } + } + if p.event.typ == yaml_STREAM_END_EVENT { + failf("attempted to go past the end of stream; corrupted value?") + } + if p.event.typ != e { + p.parser.problem = fmt.Sprintf("expected %s event but got %s", e, p.event.typ) + p.fail() + } + yaml_event_delete(&p.event) + p.event.typ = yaml_NO_EVENT +} + +// peek peeks at the next event in the event stream, +// puts the results into p.event and returns the event type. +func (p *parser) peek() yaml_event_type_t { + if p.event.typ != yaml_NO_EVENT { + return p.event.typ + } + if !yaml_parser_parse(&p.parser, &p.event) { + p.fail() + } + return p.event.typ +} + +func (p *parser) fail() { + var where string + var line int + if p.parser.problem_mark.line != 0 { + line = p.parser.problem_mark.line + // Scanner errors don't iterate line before returning error + if p.parser.error == yaml_SCANNER_ERROR { + line++ + } + } else if p.parser.context_mark.line != 0 { + line = p.parser.context_mark.line + } + if line != 0 { + where = "line " + strconv.Itoa(line) + ": " + } + var msg string + if len(p.parser.problem) > 0 { + msg = p.parser.problem + } else { + msg = "unknown problem parsing YAML content" + } + failf("%s%s", where, msg) +} + +func (p *parser) anchor(n *node, anchor []byte) { + if anchor != nil { + p.doc.anchors[string(anchor)] = n + } +} + +func (p *parser) parse() *node { + p.init() + switch p.peek() { + case yaml_SCALAR_EVENT: + return p.scalar() + case yaml_ALIAS_EVENT: + return p.alias() + case yaml_MAPPING_START_EVENT: + return p.mapping() + case yaml_SEQUENCE_START_EVENT: + return p.sequence() + case yaml_DOCUMENT_START_EVENT: + return p.document() + case yaml_STREAM_END_EVENT: + // Happens when attempting to decode an empty buffer. + return nil + default: + panic("attempted to parse unknown event: " + p.event.typ.String()) + } +} + +func (p *parser) node(kind int) *node { + return &node{ + kind: kind, + line: p.event.start_mark.line, + column: p.event.start_mark.column, + } +} + +func (p *parser) document() *node { + n := p.node(documentNode) + n.anchors = make(map[string]*node) + p.doc = n + p.expect(yaml_DOCUMENT_START_EVENT) + n.children = append(n.children, p.parse()) + p.expect(yaml_DOCUMENT_END_EVENT) + return n +} + +func (p *parser) alias() *node { + n := p.node(aliasNode) + n.value = string(p.event.anchor) + n.alias = p.doc.anchors[n.value] + if n.alias == nil { + failf("unknown anchor '%s' referenced", n.value) + } + p.expect(yaml_ALIAS_EVENT) + return n +} + +func (p *parser) scalar() *node { + n := p.node(scalarNode) + n.value = string(p.event.value) + n.tag = string(p.event.tag) + n.implicit = p.event.implicit + p.anchor(n, p.event.anchor) + p.expect(yaml_SCALAR_EVENT) + return n +} + +func (p *parser) sequence() *node { + n := p.node(sequenceNode) + p.anchor(n, p.event.anchor) + p.expect(yaml_SEQUENCE_START_EVENT) + for p.peek() != yaml_SEQUENCE_END_EVENT { + n.children = append(n.children, p.parse()) + } + p.expect(yaml_SEQUENCE_END_EVENT) + return n +} + +func (p *parser) mapping() *node { + n := p.node(mappingNode) + p.anchor(n, p.event.anchor) + p.expect(yaml_MAPPING_START_EVENT) + for p.peek() != yaml_MAPPING_END_EVENT { + n.children = append(n.children, p.parse(), p.parse()) + } + p.expect(yaml_MAPPING_END_EVENT) + return n +} + +// ---------------------------------------------------------------------------- +// Decoder, unmarshals a node into a provided value. + +type decoder struct { + doc *node + aliases map[*node]bool + mapType reflect.Type + terrors []string + strict bool +} + +var ( + mapItemType = reflect.TypeOf(MapItem{}) + durationType = reflect.TypeOf(time.Duration(0)) + // DefaultMapType type to unmarshal maps into, use MapSlice for ordered maps. + DefaultMapType = reflect.TypeOf(map[interface{}]interface{}{}) + ifaceType = DefaultMapType.Elem() + timeType = reflect.TypeOf(time.Time{}) + ptrTimeType = reflect.TypeOf(&time.Time{}) +) + +func newDecoder(strict bool) *decoder { + d := &decoder{mapType: DefaultMapType, strict: strict} + d.aliases = make(map[*node]bool) + return d +} + +func (d *decoder) terror(n *node, tag string, out reflect.Value) { + if n.tag != "" { + tag = n.tag + } + value := n.value + if tag != yaml_SEQ_TAG && tag != yaml_MAP_TAG { + if len(value) > 10 { + value = " `" + value[:7] + "...`" + } else { + value = " `" + value + "`" + } + } + d.terrors = append(d.terrors, fmt.Sprintf("line %d: cannot unmarshal %s%s into %s", n.line+1, shortTag(tag), value, out.Type())) +} + +func (d *decoder) callUnmarshaler(n *node, u Unmarshaler) (good bool) { + terrlen := len(d.terrors) + err := u.UnmarshalYAML(func(v interface{}) (err error) { + defer handleErr(&err) + d.unmarshal(n, reflect.ValueOf(v)) + if len(d.terrors) > terrlen { + issues := d.terrors[terrlen:] + d.terrors = d.terrors[:terrlen] + return &TypeError{issues} + } + return nil + }) + if e, ok := err.(*TypeError); ok { + d.terrors = append(d.terrors, e.Errors...) + return false + } + if err != nil { + fail(err) + } + return true +} + +// d.prepare initializes and dereferences pointers and calls UnmarshalYAML +// if a value is found to implement it. +// It returns the initialized and dereferenced out value, whether +// unmarshalling was already done by UnmarshalYAML, and if so whether +// its types unmarshalled appropriately. +// +// If n holds a null value, prepare returns before doing anything. +func (d *decoder) prepare(n *node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) { + if n.tag == yaml_NULL_TAG || n.kind == scalarNode && n.tag == "" && (n.value == "null" || n.value == "~" || n.value == "" && n.implicit) { + return out, false, false + } + again := true + for again { + again = false + if out.Kind() == reflect.Ptr { + if out.IsNil() { + out.Set(reflect.New(out.Type().Elem())) + } + out = out.Elem() + again = true + } + if out.CanAddr() { + if u, ok := out.Addr().Interface().(Unmarshaler); ok { + good = d.callUnmarshaler(n, u) + return out, true, good + } + } + } + return out, false, false +} + +func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) { + switch n.kind { + case documentNode: + return d.document(n, out) + case aliasNode: + return d.alias(n, out) + } + out, unmarshaled, good := d.prepare(n, out) + if unmarshaled { + return good + } + switch n.kind { + case scalarNode: + good = d.scalar(n, out) + case mappingNode: + good = d.mapping(n, out) + case sequenceNode: + good = d.sequence(n, out) + default: + panic("internal error: unknown node kind: " + strconv.Itoa(n.kind)) + } + return good +} + +func (d *decoder) document(n *node, out reflect.Value) (good bool) { + if len(n.children) == 1 { + d.doc = n + d.unmarshal(n.children[0], out) + return true + } + return false +} + +func (d *decoder) alias(n *node, out reflect.Value) (good bool) { + if d.aliases[n] { + // TODO this could actually be allowed in some circumstances. + failf("anchor '%s' value contains itself", n.value) + } + d.aliases[n] = true + good = d.unmarshal(n.alias, out) + delete(d.aliases, n) + return good +} + +var zeroValue reflect.Value + +func resetMap(out reflect.Value) { + for _, k := range out.MapKeys() { + out.SetMapIndex(k, zeroValue) + } +} + +func (d *decoder) scalar(n *node, out reflect.Value) bool { + var tag string + var resolved interface{} + if n.tag == "" && !n.implicit { + tag = yaml_STR_TAG + resolved = n.value + } else { + tag, resolved = resolve(n.tag, n.value) + if tag == yaml_BINARY_TAG { + data, err := base64.StdEncoding.DecodeString(resolved.(string)) + if err != nil { + failf("!!binary value contains invalid base64 data") + } + resolved = string(data) + } + } + if resolved == nil { + if out.Kind() == reflect.Map && !out.CanAddr() { + resetMap(out) + } else { + out.Set(reflect.Zero(out.Type())) + } + return true + } + if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() { + // We've resolved to exactly the type we want, so use that. + out.Set(resolvedv) + return true + } + // Perhaps we can use the value as a TextUnmarshaler to + // set its value. + if out.CanAddr() { + u, ok := out.Addr().Interface().(encoding.TextUnmarshaler) + if ok { + var text []byte + if tag == yaml_BINARY_TAG { + text = []byte(resolved.(string)) + } else { + // We let any value be unmarshaled into TextUnmarshaler. + // That might be more lax than we'd like, but the + // TextUnmarshaler itself should bowl out any dubious values. + text = []byte(n.value) + } + err := u.UnmarshalText(text) + if err != nil { + fail(err) + } + return true + } + } + switch out.Kind() { + case reflect.String: + if tag == yaml_BINARY_TAG { + out.SetString(resolved.(string)) + return true + } + if resolved != nil { + out.SetString(n.value) + return true + } + case reflect.Interface: + if resolved == nil { + out.Set(reflect.Zero(out.Type())) + } else if tag == yaml_TIMESTAMP_TAG { + // It looks like a timestamp but for backward compatibility + // reasons we set it as a string, so that code that unmarshals + // timestamp-like values into interface{} will continue to + // see a string and not a time.Time. + // TODO(v3) Drop this. + out.Set(reflect.ValueOf(n.value)) + } else { + out.Set(reflect.ValueOf(resolved)) + } + return true + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + switch resolved := resolved.(type) { + case int: + if !out.OverflowInt(int64(resolved)) { + out.SetInt(int64(resolved)) + return true + } + case int64: + if !out.OverflowInt(resolved) { + out.SetInt(resolved) + return true + } + case uint64: + if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { + out.SetInt(int64(resolved)) + return true + } + case float64: + if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { + out.SetInt(int64(resolved)) + return true + } + case string: + if out.Type() == durationType { + d, err := time.ParseDuration(resolved) + if err == nil { + out.SetInt(int64(d)) + return true + } + } + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + switch resolved := resolved.(type) { + case int: + if resolved >= 0 && !out.OverflowUint(uint64(resolved)) { + out.SetUint(uint64(resolved)) + return true + } + case int64: + if resolved >= 0 && !out.OverflowUint(uint64(resolved)) { + out.SetUint(uint64(resolved)) + return true + } + case uint64: + if !out.OverflowUint(uint64(resolved)) { + out.SetUint(uint64(resolved)) + return true + } + case float64: + if resolved <= math.MaxUint64 && !out.OverflowUint(uint64(resolved)) { + out.SetUint(uint64(resolved)) + return true + } + } + case reflect.Bool: + switch resolved := resolved.(type) { + case bool: + out.SetBool(resolved) + return true + } + case reflect.Float32, reflect.Float64: + switch resolved := resolved.(type) { + case int: + out.SetFloat(float64(resolved)) + return true + case int64: + out.SetFloat(float64(resolved)) + return true + case uint64: + out.SetFloat(float64(resolved)) + return true + case float64: + out.SetFloat(resolved) + return true + } + case reflect.Struct: + if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() { + out.Set(resolvedv) + return true + } + case reflect.Ptr: + if out.Type().Elem() == reflect.TypeOf(resolved) { + // TODO DOes this make sense? When is out a Ptr except when decoding a nil value? + elem := reflect.New(out.Type().Elem()) + elem.Elem().Set(reflect.ValueOf(resolved)) + out.Set(elem) + return true + } + } + d.terror(n, tag, out) + return false +} + +func settableValueOf(i interface{}) reflect.Value { + v := reflect.ValueOf(i) + sv := reflect.New(v.Type()).Elem() + sv.Set(v) + return sv +} + +func (d *decoder) sequence(n *node, out reflect.Value) (good bool) { + l := len(n.children) + + var iface reflect.Value + switch out.Kind() { + case reflect.Slice: + out.Set(reflect.MakeSlice(out.Type(), l, l)) + case reflect.Array: + if l != out.Len() { + failf("invalid array: want %d elements but got %d", out.Len(), l) + } + case reflect.Interface: + // No type hints. Will have to use a generic sequence. + iface = out + out = settableValueOf(make([]interface{}, l)) + default: + d.terror(n, yaml_SEQ_TAG, out) + return false + } + et := out.Type().Elem() + + j := 0 + for i := 0; i < l; i++ { + e := reflect.New(et).Elem() + if ok := d.unmarshal(n.children[i], e); ok { + out.Index(j).Set(e) + j++ + } + } + if out.Kind() != reflect.Array { + out.Set(out.Slice(0, j)) + } + if iface.IsValid() { + iface.Set(out) + } + return true +} + +func (d *decoder) mapping(n *node, out reflect.Value) (good bool) { + switch out.Kind() { + case reflect.Struct: + return d.mappingStruct(n, out) + case reflect.Slice: + return d.mappingSlice(n, out) + case reflect.Map: + // okay + case reflect.Interface: + if d.mapType.Kind() == reflect.Map { + iface := out + out = reflect.MakeMap(d.mapType) + iface.Set(out) + } else { + slicev := reflect.New(d.mapType).Elem() + if !d.mappingSlice(n, slicev) { + return false + } + out.Set(slicev) + return true + } + default: + d.terror(n, yaml_MAP_TAG, out) + return false + } + outt := out.Type() + kt := outt.Key() + et := outt.Elem() + + mapType := d.mapType + if outt.Key() == ifaceType && outt.Elem() == ifaceType { + d.mapType = outt + } + + if out.IsNil() { + out.Set(reflect.MakeMap(outt)) + } + l := len(n.children) + for i := 0; i < l; i += 2 { + if isMerge(n.children[i]) { + d.merge(n.children[i+1], out) + continue + } + k := reflect.New(kt).Elem() + if d.unmarshal(n.children[i], k) { + kkind := k.Kind() + if kkind == reflect.Interface { + kkind = k.Elem().Kind() + } + if kkind == reflect.Map || kkind == reflect.Slice { + failf("invalid map key: %#v", k.Interface()) + } + e := reflect.New(et).Elem() + if d.unmarshal(n.children[i+1], e) { + d.setMapIndex(n.children[i+1], out, k, e) + } + } + } + d.mapType = mapType + return true +} + +func (d *decoder) setMapIndex(n *node, out, k, v reflect.Value) { + if d.strict && out.MapIndex(k) != zeroValue { + d.terrors = append(d.terrors, fmt.Sprintf("line %d: key %#v already set in map", n.line+1, k.Interface())) + return + } + out.SetMapIndex(k, v) +} + +func (d *decoder) mappingSlice(n *node, out reflect.Value) (good bool) { + outt := out.Type() + if outt.Elem() != mapItemType { + d.terror(n, yaml_MAP_TAG, out) + return false + } + + mapType := d.mapType + d.mapType = outt + + var slice []MapItem + var l = len(n.children) + for i := 0; i < l; i += 2 { + if isMerge(n.children[i]) { + d.merge(n.children[i+1], out) + continue + } + item := MapItem{} + k := reflect.ValueOf(&item.Key).Elem() + if d.unmarshal(n.children[i], k) { + v := reflect.ValueOf(&item.Value).Elem() + if d.unmarshal(n.children[i+1], v) { + slice = append(slice, item) + } + } + } + out.Set(reflect.ValueOf(slice)) + d.mapType = mapType + return true +} + +func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) { + sinfo, err := getStructInfo(out.Type()) + if err != nil { + panic(err) + } + name := settableValueOf("") + l := len(n.children) + + var inlineMap reflect.Value + var elemType reflect.Type + if sinfo.InlineMap != -1 { + inlineMap = out.Field(sinfo.InlineMap) + inlineMap.Set(reflect.New(inlineMap.Type()).Elem()) + elemType = inlineMap.Type().Elem() + } + + var doneFields []bool + if d.strict { + doneFields = make([]bool, len(sinfo.FieldsList)) + } + for i := 0; i < l; i += 2 { + ni := n.children[i] + if isMerge(ni) { + d.merge(n.children[i+1], out) + continue + } + if !d.unmarshal(ni, name) { + continue + } + if info, ok := sinfo.FieldsMap[name.String()]; ok { + if d.strict { + if doneFields[info.Id] { + d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.line+1, name.String(), out.Type())) + continue + } + doneFields[info.Id] = true + } + var field reflect.Value + if info.Inline == nil { + field = out.Field(info.Num) + } else { + field = out.FieldByIndex(info.Inline) + } + d.unmarshal(n.children[i+1], field) + } else if sinfo.InlineMap != -1 { + if inlineMap.IsNil() { + inlineMap.Set(reflect.MakeMap(inlineMap.Type())) + } + value := reflect.New(elemType).Elem() + d.unmarshal(n.children[i+1], value) + d.setMapIndex(n.children[i+1], inlineMap, name, value) + } else if d.strict { + d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.line+1, name.String(), out.Type())) + } + } + return true +} + +func failWantMap() { + failf("map merge requires map or sequence of maps as the value") +} + +func (d *decoder) merge(n *node, out reflect.Value) { + switch n.kind { + case mappingNode: + d.unmarshal(n, out) + case aliasNode: + an, ok := d.doc.anchors[n.value] + if ok && an.kind != mappingNode { + failWantMap() + } + d.unmarshal(n, out) + case sequenceNode: + // Step backwards as earlier nodes take precedence. + for i := len(n.children) - 1; i >= 0; i-- { + ni := n.children[i] + if ni.kind == aliasNode { + an, ok := d.doc.anchors[ni.value] + if ok && an.kind != mappingNode { + failWantMap() + } + } else if ni.kind != mappingNode { + failWantMap() + } + d.unmarshal(ni, out) + } + default: + failWantMap() + } +} + +func isMerge(n *node) bool { + return n.kind == scalarNode && n.value == "<<" && (n.implicit == true || n.tag == yaml_MERGE_TAG) +} diff --git a/vendor/github.com/mikefarah/yaml/v2/emitterc.go b/vendor/github.com/mikefarah/yaml/v2/emitterc.go new file mode 100644 index 0000000000..a1c2cc5262 --- /dev/null +++ b/vendor/github.com/mikefarah/yaml/v2/emitterc.go @@ -0,0 +1,1685 @@ +package yaml + +import ( + "bytes" + "fmt" +) + +// Flush the buffer if needed. +func flush(emitter *yaml_emitter_t) bool { + if emitter.buffer_pos+5 >= len(emitter.buffer) { + return yaml_emitter_flush(emitter) + } + return true +} + +// Put a character to the output buffer. +func put(emitter *yaml_emitter_t, value byte) bool { + if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) { + return false + } + emitter.buffer[emitter.buffer_pos] = value + emitter.buffer_pos++ + emitter.column++ + return true +} + +// Put a line break to the output buffer. +func put_break(emitter *yaml_emitter_t) bool { + if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) { + return false + } + switch emitter.line_break { + case yaml_CR_BREAK: + emitter.buffer[emitter.buffer_pos] = '\r' + emitter.buffer_pos += 1 + case yaml_LN_BREAK: + emitter.buffer[emitter.buffer_pos] = '\n' + emitter.buffer_pos += 1 + case yaml_CRLN_BREAK: + emitter.buffer[emitter.buffer_pos+0] = '\r' + emitter.buffer[emitter.buffer_pos+1] = '\n' + emitter.buffer_pos += 2 + default: + panic("unknown line break setting") + } + emitter.column = 0 + emitter.line++ + return true +} + +// Copy a character from a string into buffer. +func write(emitter *yaml_emitter_t, s []byte, i *int) bool { + if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) { + return false + } + p := emitter.buffer_pos + w := width(s[*i]) + switch w { + case 4: + emitter.buffer[p+3] = s[*i+3] + fallthrough + case 3: + emitter.buffer[p+2] = s[*i+2] + fallthrough + case 2: + emitter.buffer[p+1] = s[*i+1] + fallthrough + case 1: + emitter.buffer[p+0] = s[*i+0] + default: + panic("unknown character width") + } + emitter.column++ + emitter.buffer_pos += w + *i += w + return true +} + +// Write a whole string into buffer. +func write_all(emitter *yaml_emitter_t, s []byte) bool { + for i := 0; i < len(s); { + if !write(emitter, s, &i) { + return false + } + } + return true +} + +// Copy a line break character from a string into buffer. +func write_break(emitter *yaml_emitter_t, s []byte, i *int) bool { + if s[*i] == '\n' { + if !put_break(emitter) { + return false + } + *i++ + } else { + if !write(emitter, s, i) { + return false + } + emitter.column = 0 + emitter.line++ + } + return true +} + +// Set an emitter error and return false. +func yaml_emitter_set_emitter_error(emitter *yaml_emitter_t, problem string) bool { + emitter.error = yaml_EMITTER_ERROR + emitter.problem = problem + return false +} + +// Emit an event. +func yaml_emitter_emit(emitter *yaml_emitter_t, event *yaml_event_t) bool { + emitter.events = append(emitter.events, *event) + for !yaml_emitter_need_more_events(emitter) { + event := &emitter.events[emitter.events_head] + if !yaml_emitter_analyze_event(emitter, event) { + return false + } + if !yaml_emitter_state_machine(emitter, event) { + return false + } + yaml_event_delete(event) + emitter.events_head++ + } + return true +} + +// Check if we need to accumulate more events before emitting. +// +// We accumulate extra +// - 1 event for DOCUMENT-START +// - 2 events for SEQUENCE-START +// - 3 events for MAPPING-START +// +func yaml_emitter_need_more_events(emitter *yaml_emitter_t) bool { + if emitter.events_head == len(emitter.events) { + return true + } + var accumulate int + switch emitter.events[emitter.events_head].typ { + case yaml_DOCUMENT_START_EVENT: + accumulate = 1 + break + case yaml_SEQUENCE_START_EVENT: + accumulate = 2 + break + case yaml_MAPPING_START_EVENT: + accumulate = 3 + break + default: + return false + } + if len(emitter.events)-emitter.events_head > accumulate { + return false + } + var level int + for i := emitter.events_head; i < len(emitter.events); i++ { + switch emitter.events[i].typ { + case yaml_STREAM_START_EVENT, yaml_DOCUMENT_START_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT: + level++ + case yaml_STREAM_END_EVENT, yaml_DOCUMENT_END_EVENT, yaml_SEQUENCE_END_EVENT, yaml_MAPPING_END_EVENT: + level-- + } + if level == 0 { + return false + } + } + return true +} + +// Append a directive to the directives stack. +func yaml_emitter_append_tag_directive(emitter *yaml_emitter_t, value *yaml_tag_directive_t, allow_duplicates bool) bool { + for i := 0; i < len(emitter.tag_directives); i++ { + if bytes.Equal(value.handle, emitter.tag_directives[i].handle) { + if allow_duplicates { + return true + } + return yaml_emitter_set_emitter_error(emitter, "duplicate %TAG directive") + } + } + + // [Go] Do we actually need to copy this given garbage collection + // and the lack of deallocating destructors? + tag_copy := yaml_tag_directive_t{ + handle: make([]byte, len(value.handle)), + prefix: make([]byte, len(value.prefix)), + } + copy(tag_copy.handle, value.handle) + copy(tag_copy.prefix, value.prefix) + emitter.tag_directives = append(emitter.tag_directives, tag_copy) + return true +} + +// Increase the indentation level. +func yaml_emitter_increase_indent(emitter *yaml_emitter_t, flow, indentless bool) bool { + emitter.indents = append(emitter.indents, emitter.indent) + if emitter.indent < 0 { + if flow { + emitter.indent = emitter.best_indent + } else { + emitter.indent = 0 + } + } else if !indentless { + emitter.indent += emitter.best_indent + } + return true +} + +// State dispatcher. +func yaml_emitter_state_machine(emitter *yaml_emitter_t, event *yaml_event_t) bool { + switch emitter.state { + default: + case yaml_EMIT_STREAM_START_STATE: + return yaml_emitter_emit_stream_start(emitter, event) + + case yaml_EMIT_FIRST_DOCUMENT_START_STATE: + return yaml_emitter_emit_document_start(emitter, event, true) + + case yaml_EMIT_DOCUMENT_START_STATE: + return yaml_emitter_emit_document_start(emitter, event, false) + + case yaml_EMIT_DOCUMENT_CONTENT_STATE: + return yaml_emitter_emit_document_content(emitter, event) + + case yaml_EMIT_DOCUMENT_END_STATE: + return yaml_emitter_emit_document_end(emitter, event) + + case yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE: + return yaml_emitter_emit_flow_sequence_item(emitter, event, true) + + case yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE: + return yaml_emitter_emit_flow_sequence_item(emitter, event, false) + + case yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE: + return yaml_emitter_emit_flow_mapping_key(emitter, event, true) + + case yaml_EMIT_FLOW_MAPPING_KEY_STATE: + return yaml_emitter_emit_flow_mapping_key(emitter, event, false) + + case yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE: + return yaml_emitter_emit_flow_mapping_value(emitter, event, true) + + case yaml_EMIT_FLOW_MAPPING_VALUE_STATE: + return yaml_emitter_emit_flow_mapping_value(emitter, event, false) + + case yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE: + return yaml_emitter_emit_block_sequence_item(emitter, event, true) + + case yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE: + return yaml_emitter_emit_block_sequence_item(emitter, event, false) + + case yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE: + return yaml_emitter_emit_block_mapping_key(emitter, event, true) + + case yaml_EMIT_BLOCK_MAPPING_KEY_STATE: + return yaml_emitter_emit_block_mapping_key(emitter, event, false) + + case yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE: + return yaml_emitter_emit_block_mapping_value(emitter, event, true) + + case yaml_EMIT_BLOCK_MAPPING_VALUE_STATE: + return yaml_emitter_emit_block_mapping_value(emitter, event, false) + + case yaml_EMIT_END_STATE: + return yaml_emitter_set_emitter_error(emitter, "expected nothing after STREAM-END") + } + panic("invalid emitter state") +} + +// Expect STREAM-START. +func yaml_emitter_emit_stream_start(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if event.typ != yaml_STREAM_START_EVENT { + return yaml_emitter_set_emitter_error(emitter, "expected STREAM-START") + } + if emitter.encoding == yaml_ANY_ENCODING { + emitter.encoding = event.encoding + if emitter.encoding == yaml_ANY_ENCODING { + emitter.encoding = yaml_UTF8_ENCODING + } + } + if emitter.best_indent < 2 || emitter.best_indent > 9 { + emitter.best_indent = 2 + } + if emitter.best_width >= 0 && emitter.best_width <= emitter.best_indent*2 { + emitter.best_width = 80 + } + if emitter.best_width < 0 { + emitter.best_width = 1<<31 - 1 + } + if emitter.line_break == yaml_ANY_BREAK { + emitter.line_break = yaml_LN_BREAK + } + + emitter.indent = -1 + emitter.line = 0 + emitter.column = 0 + emitter.whitespace = true + emitter.indention = true + + if emitter.encoding != yaml_UTF8_ENCODING { + if !yaml_emitter_write_bom(emitter) { + return false + } + } + emitter.state = yaml_EMIT_FIRST_DOCUMENT_START_STATE + return true +} + +// Expect DOCUMENT-START or STREAM-END. +func yaml_emitter_emit_document_start(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { + + if event.typ == yaml_DOCUMENT_START_EVENT { + + if event.version_directive != nil { + if !yaml_emitter_analyze_version_directive(emitter, event.version_directive) { + return false + } + } + + for i := 0; i < len(event.tag_directives); i++ { + tag_directive := &event.tag_directives[i] + if !yaml_emitter_analyze_tag_directive(emitter, tag_directive) { + return false + } + if !yaml_emitter_append_tag_directive(emitter, tag_directive, false) { + return false + } + } + + for i := 0; i < len(default_tag_directives); i++ { + tag_directive := &default_tag_directives[i] + if !yaml_emitter_append_tag_directive(emitter, tag_directive, true) { + return false + } + } + + implicit := event.implicit + if !first || emitter.canonical { + implicit = false + } + + if emitter.open_ended && (event.version_directive != nil || len(event.tag_directives) > 0) { + if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + + if event.version_directive != nil { + implicit = false + if !yaml_emitter_write_indicator(emitter, []byte("%YAML"), true, false, false) { + return false + } + if !yaml_emitter_write_indicator(emitter, []byte("1.1"), true, false, false) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + + if len(event.tag_directives) > 0 { + implicit = false + for i := 0; i < len(event.tag_directives); i++ { + tag_directive := &event.tag_directives[i] + if !yaml_emitter_write_indicator(emitter, []byte("%TAG"), true, false, false) { + return false + } + if !yaml_emitter_write_tag_handle(emitter, tag_directive.handle) { + return false + } + if !yaml_emitter_write_tag_content(emitter, tag_directive.prefix, true) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + } + + if yaml_emitter_check_empty_document(emitter) { + implicit = false + } + if !implicit { + if !yaml_emitter_write_indent(emitter) { + return false + } + if !yaml_emitter_write_indicator(emitter, []byte("---"), true, false, false) { + return false + } + if emitter.canonical { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + } + + emitter.state = yaml_EMIT_DOCUMENT_CONTENT_STATE + return true + } + + if event.typ == yaml_STREAM_END_EVENT { + if emitter.open_ended { + if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !yaml_emitter_flush(emitter) { + return false + } + emitter.state = yaml_EMIT_END_STATE + return true + } + + return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-START or STREAM-END") +} + +// Expect the root node. +func yaml_emitter_emit_document_content(emitter *yaml_emitter_t, event *yaml_event_t) bool { + emitter.states = append(emitter.states, yaml_EMIT_DOCUMENT_END_STATE) + return yaml_emitter_emit_node(emitter, event, true, false, false, false) +} + +// Expect DOCUMENT-END. +func yaml_emitter_emit_document_end(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if event.typ != yaml_DOCUMENT_END_EVENT { + return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-END") + } + if !yaml_emitter_write_indent(emitter) { + return false + } + if !event.implicit { + // [Go] Allocate the slice elsewhere. + if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !yaml_emitter_flush(emitter) { + return false + } + emitter.state = yaml_EMIT_DOCUMENT_START_STATE + emitter.tag_directives = emitter.tag_directives[:0] + return true +} + +// Expect a flow item node. +func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { + if first { + if !yaml_emitter_write_indicator(emitter, []byte{'['}, true, true, false) { + return false + } + if !yaml_emitter_increase_indent(emitter, true, false) { + return false + } + emitter.flow_level++ + } + + if event.typ == yaml_SEQUENCE_END_EVENT { + emitter.flow_level-- + emitter.indent = emitter.indents[len(emitter.indents)-1] + emitter.indents = emitter.indents[:len(emitter.indents)-1] + if emitter.canonical && !first { + if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !yaml_emitter_write_indicator(emitter, []byte{']'}, false, false, false) { + return false + } + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + + return true + } + + if !first { + if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { + return false + } + } + + if emitter.canonical || emitter.column > emitter.best_width { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE) + return yaml_emitter_emit_node(emitter, event, false, true, false, false) +} + +// Expect a flow key node. +func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { + if first { + if !yaml_emitter_write_indicator(emitter, []byte{'{'}, true, true, false) { + return false + } + if !yaml_emitter_increase_indent(emitter, true, false) { + return false + } + emitter.flow_level++ + } + + if event.typ == yaml_MAPPING_END_EVENT { + emitter.flow_level-- + emitter.indent = emitter.indents[len(emitter.indents)-1] + emitter.indents = emitter.indents[:len(emitter.indents)-1] + if emitter.canonical && !first { + if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !yaml_emitter_write_indicator(emitter, []byte{'}'}, false, false, false) { + return false + } + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + return true + } + + if !first { + if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { + return false + } + } + if emitter.canonical || emitter.column > emitter.best_width { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + + if !emitter.canonical && yaml_emitter_check_simple_key(emitter) { + emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE) + return yaml_emitter_emit_node(emitter, event, false, false, true, true) + } + if !yaml_emitter_write_indicator(emitter, []byte{'?'}, true, false, false) { + return false + } + emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_VALUE_STATE) + return yaml_emitter_emit_node(emitter, event, false, false, true, false) +} + +// Expect a flow value node. +func yaml_emitter_emit_flow_mapping_value(emitter *yaml_emitter_t, event *yaml_event_t, simple bool) bool { + if simple { + if !yaml_emitter_write_indicator(emitter, []byte{':'}, false, false, false) { + return false + } + } else { + if emitter.canonical || emitter.column > emitter.best_width { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !yaml_emitter_write_indicator(emitter, []byte{':'}, true, false, false) { + return false + } + } + emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_KEY_STATE) + return yaml_emitter_emit_node(emitter, event, false, false, true, false) +} + +// Expect a block item node. +func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { + if first { + if !yaml_emitter_increase_indent(emitter, false, emitter.mapping_context && !emitter.indention) { + return false + } + } + if event.typ == yaml_SEQUENCE_END_EVENT { + emitter.indent = emitter.indents[len(emitter.indents)-1] + emitter.indents = emitter.indents[:len(emitter.indents)-1] + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + return true + } + if !yaml_emitter_write_indent(emitter) { + return false + } + if !yaml_emitter_write_indicator(emitter, []byte{'-'}, true, false, true) { + return false + } + emitter.states = append(emitter.states, yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE) + return yaml_emitter_emit_node(emitter, event, false, true, false, false) +} + +// Expect a block key node. +func yaml_emitter_emit_block_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { + if first { + if !yaml_emitter_increase_indent(emitter, false, false) { + return false + } + } + if event.typ == yaml_MAPPING_END_EVENT { + emitter.indent = emitter.indents[len(emitter.indents)-1] + emitter.indents = emitter.indents[:len(emitter.indents)-1] + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + return true + } + if !yaml_emitter_write_indent(emitter) { + return false + } + if yaml_emitter_check_simple_key(emitter) { + emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE) + return yaml_emitter_emit_node(emitter, event, false, false, true, true) + } + if !yaml_emitter_write_indicator(emitter, []byte{'?'}, true, false, true) { + return false + } + emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_VALUE_STATE) + return yaml_emitter_emit_node(emitter, event, false, false, true, false) +} + +// Expect a block value node. +func yaml_emitter_emit_block_mapping_value(emitter *yaml_emitter_t, event *yaml_event_t, simple bool) bool { + if simple { + if !yaml_emitter_write_indicator(emitter, []byte{':'}, false, false, false) { + return false + } + } else { + if !yaml_emitter_write_indent(emitter) { + return false + } + if !yaml_emitter_write_indicator(emitter, []byte{':'}, true, false, true) { + return false + } + } + emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_KEY_STATE) + return yaml_emitter_emit_node(emitter, event, false, false, true, false) +} + +// Expect a node. +func yaml_emitter_emit_node(emitter *yaml_emitter_t, event *yaml_event_t, + root bool, sequence bool, mapping bool, simple_key bool) bool { + + emitter.root_context = root + emitter.sequence_context = sequence + emitter.mapping_context = mapping + emitter.simple_key_context = simple_key + + switch event.typ { + case yaml_ALIAS_EVENT: + return yaml_emitter_emit_alias(emitter, event) + case yaml_SCALAR_EVENT: + return yaml_emitter_emit_scalar(emitter, event) + case yaml_SEQUENCE_START_EVENT: + return yaml_emitter_emit_sequence_start(emitter, event) + case yaml_MAPPING_START_EVENT: + return yaml_emitter_emit_mapping_start(emitter, event) + default: + return yaml_emitter_set_emitter_error(emitter, + fmt.Sprintf("expected SCALAR, SEQUENCE-START, MAPPING-START, or ALIAS, but got %v", event.typ)) + } +} + +// Expect ALIAS. +func yaml_emitter_emit_alias(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if !yaml_emitter_process_anchor(emitter) { + return false + } + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + return true +} + +// Expect SCALAR. +func yaml_emitter_emit_scalar(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if !yaml_emitter_select_scalar_style(emitter, event) { + return false + } + if !yaml_emitter_process_anchor(emitter) { + return false + } + if !yaml_emitter_process_tag(emitter) { + return false + } + if !yaml_emitter_increase_indent(emitter, true, false) { + return false + } + if !yaml_emitter_process_scalar(emitter) { + return false + } + emitter.indent = emitter.indents[len(emitter.indents)-1] + emitter.indents = emitter.indents[:len(emitter.indents)-1] + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + return true +} + +// Expect SEQUENCE-START. +func yaml_emitter_emit_sequence_start(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if !yaml_emitter_process_anchor(emitter) { + return false + } + if !yaml_emitter_process_tag(emitter) { + return false + } + if emitter.flow_level > 0 || emitter.canonical || event.sequence_style() == yaml_FLOW_SEQUENCE_STYLE || + yaml_emitter_check_empty_sequence(emitter) { + emitter.state = yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE + } else { + emitter.state = yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE + } + return true +} + +// Expect MAPPING-START. +func yaml_emitter_emit_mapping_start(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if !yaml_emitter_process_anchor(emitter) { + return false + } + if !yaml_emitter_process_tag(emitter) { + return false + } + if emitter.flow_level > 0 || emitter.canonical || event.mapping_style() == yaml_FLOW_MAPPING_STYLE || + yaml_emitter_check_empty_mapping(emitter) { + emitter.state = yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE + } else { + emitter.state = yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE + } + return true +} + +// Check if the document content is an empty scalar. +func yaml_emitter_check_empty_document(emitter *yaml_emitter_t) bool { + return false // [Go] Huh? +} + +// Check if the next events represent an empty sequence. +func yaml_emitter_check_empty_sequence(emitter *yaml_emitter_t) bool { + if len(emitter.events)-emitter.events_head < 2 { + return false + } + return emitter.events[emitter.events_head].typ == yaml_SEQUENCE_START_EVENT && + emitter.events[emitter.events_head+1].typ == yaml_SEQUENCE_END_EVENT +} + +// Check if the next events represent an empty mapping. +func yaml_emitter_check_empty_mapping(emitter *yaml_emitter_t) bool { + if len(emitter.events)-emitter.events_head < 2 { + return false + } + return emitter.events[emitter.events_head].typ == yaml_MAPPING_START_EVENT && + emitter.events[emitter.events_head+1].typ == yaml_MAPPING_END_EVENT +} + +// Check if the next node can be expressed as a simple key. +func yaml_emitter_check_simple_key(emitter *yaml_emitter_t) bool { + length := 0 + switch emitter.events[emitter.events_head].typ { + case yaml_ALIAS_EVENT: + length += len(emitter.anchor_data.anchor) + case yaml_SCALAR_EVENT: + if emitter.scalar_data.multiline { + return false + } + length += len(emitter.anchor_data.anchor) + + len(emitter.tag_data.handle) + + len(emitter.tag_data.suffix) + + len(emitter.scalar_data.value) + case yaml_SEQUENCE_START_EVENT: + if !yaml_emitter_check_empty_sequence(emitter) { + return false + } + length += len(emitter.anchor_data.anchor) + + len(emitter.tag_data.handle) + + len(emitter.tag_data.suffix) + case yaml_MAPPING_START_EVENT: + if !yaml_emitter_check_empty_mapping(emitter) { + return false + } + length += len(emitter.anchor_data.anchor) + + len(emitter.tag_data.handle) + + len(emitter.tag_data.suffix) + default: + return false + } + return length <= 128 +} + +// Determine an acceptable scalar style. +func yaml_emitter_select_scalar_style(emitter *yaml_emitter_t, event *yaml_event_t) bool { + + no_tag := len(emitter.tag_data.handle) == 0 && len(emitter.tag_data.suffix) == 0 + if no_tag && !event.implicit && !event.quoted_implicit { + return yaml_emitter_set_emitter_error(emitter, "neither tag nor implicit flags are specified") + } + + style := event.scalar_style() + if style == yaml_ANY_SCALAR_STYLE { + style = yaml_PLAIN_SCALAR_STYLE + } + if emitter.canonical { + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + if emitter.simple_key_context && emitter.scalar_data.multiline { + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + + if style == yaml_PLAIN_SCALAR_STYLE { + if emitter.flow_level > 0 && !emitter.scalar_data.flow_plain_allowed || + emitter.flow_level == 0 && !emitter.scalar_data.block_plain_allowed { + style = yaml_SINGLE_QUOTED_SCALAR_STYLE + } + if len(emitter.scalar_data.value) == 0 && (emitter.flow_level > 0 || emitter.simple_key_context) { + style = yaml_SINGLE_QUOTED_SCALAR_STYLE + } + if no_tag && !event.implicit { + style = yaml_SINGLE_QUOTED_SCALAR_STYLE + } + } + if style == yaml_SINGLE_QUOTED_SCALAR_STYLE { + if !emitter.scalar_data.single_quoted_allowed { + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + } + if style == yaml_LITERAL_SCALAR_STYLE || style == yaml_FOLDED_SCALAR_STYLE { + if !emitter.scalar_data.block_allowed || emitter.flow_level > 0 || emitter.simple_key_context { + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + } + + if no_tag && !event.quoted_implicit && style != yaml_PLAIN_SCALAR_STYLE { + emitter.tag_data.handle = []byte{'!'} + } + emitter.scalar_data.style = style + return true +} + +// Write an anchor. +func yaml_emitter_process_anchor(emitter *yaml_emitter_t) bool { + if emitter.anchor_data.anchor == nil { + return true + } + c := []byte{'&'} + if emitter.anchor_data.alias { + c[0] = '*' + } + if !yaml_emitter_write_indicator(emitter, c, true, false, false) { + return false + } + return yaml_emitter_write_anchor(emitter, emitter.anchor_data.anchor) +} + +// Write a tag. +func yaml_emitter_process_tag(emitter *yaml_emitter_t) bool { + if len(emitter.tag_data.handle) == 0 && len(emitter.tag_data.suffix) == 0 { + return true + } + if len(emitter.tag_data.handle) > 0 { + if !yaml_emitter_write_tag_handle(emitter, emitter.tag_data.handle) { + return false + } + if len(emitter.tag_data.suffix) > 0 { + if !yaml_emitter_write_tag_content(emitter, emitter.tag_data.suffix, false) { + return false + } + } + } else { + // [Go] Allocate these slices elsewhere. + if !yaml_emitter_write_indicator(emitter, []byte("!<"), true, false, false) { + return false + } + if !yaml_emitter_write_tag_content(emitter, emitter.tag_data.suffix, false) { + return false + } + if !yaml_emitter_write_indicator(emitter, []byte{'>'}, false, false, false) { + return false + } + } + return true +} + +// Write a scalar. +func yaml_emitter_process_scalar(emitter *yaml_emitter_t) bool { + switch emitter.scalar_data.style { + case yaml_PLAIN_SCALAR_STYLE: + return yaml_emitter_write_plain_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context) + + case yaml_SINGLE_QUOTED_SCALAR_STYLE: + return yaml_emitter_write_single_quoted_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context) + + case yaml_DOUBLE_QUOTED_SCALAR_STYLE: + return yaml_emitter_write_double_quoted_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context) + + case yaml_LITERAL_SCALAR_STYLE: + return yaml_emitter_write_literal_scalar(emitter, emitter.scalar_data.value) + + case yaml_FOLDED_SCALAR_STYLE: + return yaml_emitter_write_folded_scalar(emitter, emitter.scalar_data.value) + } + panic("unknown scalar style") +} + +// Check if a %YAML directive is valid. +func yaml_emitter_analyze_version_directive(emitter *yaml_emitter_t, version_directive *yaml_version_directive_t) bool { + if version_directive.major != 1 || version_directive.minor != 1 { + return yaml_emitter_set_emitter_error(emitter, "incompatible %YAML directive") + } + return true +} + +// Check if a %TAG directive is valid. +func yaml_emitter_analyze_tag_directive(emitter *yaml_emitter_t, tag_directive *yaml_tag_directive_t) bool { + handle := tag_directive.handle + prefix := tag_directive.prefix + if len(handle) == 0 { + return yaml_emitter_set_emitter_error(emitter, "tag handle must not be empty") + } + if handle[0] != '!' { + return yaml_emitter_set_emitter_error(emitter, "tag handle must start with '!'") + } + if handle[len(handle)-1] != '!' { + return yaml_emitter_set_emitter_error(emitter, "tag handle must end with '!'") + } + for i := 1; i < len(handle)-1; i += width(handle[i]) { + if !is_alpha(handle, i) { + return yaml_emitter_set_emitter_error(emitter, "tag handle must contain alphanumerical characters only") + } + } + if len(prefix) == 0 { + return yaml_emitter_set_emitter_error(emitter, "tag prefix must not be empty") + } + return true +} + +// Check if an anchor is valid. +func yaml_emitter_analyze_anchor(emitter *yaml_emitter_t, anchor []byte, alias bool) bool { + if len(anchor) == 0 { + problem := "anchor value must not be empty" + if alias { + problem = "alias value must not be empty" + } + return yaml_emitter_set_emitter_error(emitter, problem) + } + for i := 0; i < len(anchor); i += width(anchor[i]) { + if !is_alpha(anchor, i) { + problem := "anchor value must contain alphanumerical characters only" + if alias { + problem = "alias value must contain alphanumerical characters only" + } + return yaml_emitter_set_emitter_error(emitter, problem) + } + } + emitter.anchor_data.anchor = anchor + emitter.anchor_data.alias = alias + return true +} + +// Check if a tag is valid. +func yaml_emitter_analyze_tag(emitter *yaml_emitter_t, tag []byte) bool { + if len(tag) == 0 { + return yaml_emitter_set_emitter_error(emitter, "tag value must not be empty") + } + for i := 0; i < len(emitter.tag_directives); i++ { + tag_directive := &emitter.tag_directives[i] + if bytes.HasPrefix(tag, tag_directive.prefix) { + emitter.tag_data.handle = tag_directive.handle + emitter.tag_data.suffix = tag[len(tag_directive.prefix):] + return true + } + } + emitter.tag_data.suffix = tag + return true +} + +// Check if a scalar is valid. +func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool { + var ( + block_indicators = false + flow_indicators = false + line_breaks = false + special_characters = false + + leading_space = false + leading_break = false + trailing_space = false + trailing_break = false + break_space = false + space_break = false + + preceded_by_whitespace = false + followed_by_whitespace = false + previous_space = false + previous_break = false + ) + + emitter.scalar_data.value = value + + if len(value) == 0 { + emitter.scalar_data.multiline = false + emitter.scalar_data.flow_plain_allowed = false + emitter.scalar_data.block_plain_allowed = true + emitter.scalar_data.single_quoted_allowed = true + emitter.scalar_data.block_allowed = false + return true + } + + if len(value) >= 3 && ((value[0] == '-' && value[1] == '-' && value[2] == '-') || (value[0] == '.' && value[1] == '.' && value[2] == '.')) { + block_indicators = true + flow_indicators = true + } + + preceded_by_whitespace = true + for i, w := 0, 0; i < len(value); i += w { + w = width(value[i]) + followed_by_whitespace = i+w >= len(value) || is_blank(value, i+w) + + if i == 0 { + switch value[i] { + case '#', ',', '[', ']', '{', '}', '&', '*', '!', '|', '>', '\'', '"', '%', '@', '`': + flow_indicators = true + block_indicators = true + case '?', ':': + flow_indicators = true + if followed_by_whitespace { + block_indicators = true + } + case '-': + if followed_by_whitespace { + flow_indicators = true + block_indicators = true + } + } + } else { + switch value[i] { + case ',', '?', '[', ']', '{', '}': + flow_indicators = true + case ':': + flow_indicators = true + if followed_by_whitespace { + block_indicators = true + } + case '#': + if preceded_by_whitespace { + flow_indicators = true + block_indicators = true + } + } + } + + if !is_printable(value, i) || !is_ascii(value, i) && !emitter.unicode { + special_characters = true + } + if is_space(value, i) { + if i == 0 { + leading_space = true + } + if i+width(value[i]) == len(value) { + trailing_space = true + } + if previous_break { + break_space = true + } + previous_space = true + previous_break = false + } else if is_break(value, i) { + line_breaks = true + if i == 0 { + leading_break = true + } + if i+width(value[i]) == len(value) { + trailing_break = true + } + if previous_space { + space_break = true + } + previous_space = false + previous_break = true + } else { + previous_space = false + previous_break = false + } + + // [Go]: Why 'z'? Couldn't be the end of the string as that's the loop condition. + preceded_by_whitespace = is_blankz(value, i) + } + + emitter.scalar_data.multiline = line_breaks + emitter.scalar_data.flow_plain_allowed = true + emitter.scalar_data.block_plain_allowed = true + emitter.scalar_data.single_quoted_allowed = true + emitter.scalar_data.block_allowed = true + + if leading_space || leading_break || trailing_space || trailing_break { + emitter.scalar_data.flow_plain_allowed = false + emitter.scalar_data.block_plain_allowed = false + } + if trailing_space { + emitter.scalar_data.block_allowed = false + } + if break_space { + emitter.scalar_data.flow_plain_allowed = false + emitter.scalar_data.block_plain_allowed = false + emitter.scalar_data.single_quoted_allowed = false + } + if space_break || special_characters { + emitter.scalar_data.flow_plain_allowed = false + emitter.scalar_data.block_plain_allowed = false + emitter.scalar_data.single_quoted_allowed = false + emitter.scalar_data.block_allowed = false + } + if line_breaks { + emitter.scalar_data.flow_plain_allowed = false + emitter.scalar_data.block_plain_allowed = false + } + if flow_indicators { + emitter.scalar_data.flow_plain_allowed = false + } + if block_indicators { + emitter.scalar_data.block_plain_allowed = false + } + return true +} + +// Check if the event data is valid. +func yaml_emitter_analyze_event(emitter *yaml_emitter_t, event *yaml_event_t) bool { + + emitter.anchor_data.anchor = nil + emitter.tag_data.handle = nil + emitter.tag_data.suffix = nil + emitter.scalar_data.value = nil + + switch event.typ { + case yaml_ALIAS_EVENT: + if !yaml_emitter_analyze_anchor(emitter, event.anchor, true) { + return false + } + + case yaml_SCALAR_EVENT: + if len(event.anchor) > 0 { + if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) { + return false + } + } + if len(event.tag) > 0 && (emitter.canonical || (!event.implicit && !event.quoted_implicit)) { + if !yaml_emitter_analyze_tag(emitter, event.tag) { + return false + } + } + if !yaml_emitter_analyze_scalar(emitter, event.value) { + return false + } + + case yaml_SEQUENCE_START_EVENT: + if len(event.anchor) > 0 { + if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) { + return false + } + } + if len(event.tag) > 0 && (emitter.canonical || !event.implicit) { + if !yaml_emitter_analyze_tag(emitter, event.tag) { + return false + } + } + + case yaml_MAPPING_START_EVENT: + if len(event.anchor) > 0 { + if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) { + return false + } + } + if len(event.tag) > 0 && (emitter.canonical || !event.implicit) { + if !yaml_emitter_analyze_tag(emitter, event.tag) { + return false + } + } + } + return true +} + +// Write the BOM character. +func yaml_emitter_write_bom(emitter *yaml_emitter_t) bool { + if !flush(emitter) { + return false + } + pos := emitter.buffer_pos + emitter.buffer[pos+0] = '\xEF' + emitter.buffer[pos+1] = '\xBB' + emitter.buffer[pos+2] = '\xBF' + emitter.buffer_pos += 3 + return true +} + +func yaml_emitter_write_indent(emitter *yaml_emitter_t) bool { + indent := emitter.indent + if indent < 0 { + indent = 0 + } + if !emitter.indention || emitter.column > indent || (emitter.column == indent && !emitter.whitespace) { + if !put_break(emitter) { + return false + } + } + for emitter.column < indent { + if !put(emitter, ' ') { + return false + } + } + emitter.whitespace = true + emitter.indention = true + return true +} + +func yaml_emitter_write_indicator(emitter *yaml_emitter_t, indicator []byte, need_whitespace, is_whitespace, is_indention bool) bool { + if need_whitespace && !emitter.whitespace { + if !put(emitter, ' ') { + return false + } + } + if !write_all(emitter, indicator) { + return false + } + emitter.whitespace = is_whitespace + emitter.indention = (emitter.indention && is_indention) + emitter.open_ended = false + return true +} + +func yaml_emitter_write_anchor(emitter *yaml_emitter_t, value []byte) bool { + if !write_all(emitter, value) { + return false + } + emitter.whitespace = false + emitter.indention = false + return true +} + +func yaml_emitter_write_tag_handle(emitter *yaml_emitter_t, value []byte) bool { + if !emitter.whitespace { + if !put(emitter, ' ') { + return false + } + } + if !write_all(emitter, value) { + return false + } + emitter.whitespace = false + emitter.indention = false + return true +} + +func yaml_emitter_write_tag_content(emitter *yaml_emitter_t, value []byte, need_whitespace bool) bool { + if need_whitespace && !emitter.whitespace { + if !put(emitter, ' ') { + return false + } + } + for i := 0; i < len(value); { + var must_write bool + switch value[i] { + case ';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '_', '.', '~', '*', '\'', '(', ')', '[', ']': + must_write = true + default: + must_write = is_alpha(value, i) + } + if must_write { + if !write(emitter, value, &i) { + return false + } + } else { + w := width(value[i]) + for k := 0; k < w; k++ { + octet := value[i] + i++ + if !put(emitter, '%') { + return false + } + + c := octet >> 4 + if c < 10 { + c += '0' + } else { + c += 'A' - 10 + } + if !put(emitter, c) { + return false + } + + c = octet & 0x0f + if c < 10 { + c += '0' + } else { + c += 'A' - 10 + } + if !put(emitter, c) { + return false + } + } + } + } + emitter.whitespace = false + emitter.indention = false + return true +} + +func yaml_emitter_write_plain_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool { + if !emitter.whitespace { + if !put(emitter, ' ') { + return false + } + } + + spaces := false + breaks := false + for i := 0; i < len(value); { + if is_space(value, i) { + if allow_breaks && !spaces && emitter.column > emitter.best_width && !is_space(value, i+1) { + if !yaml_emitter_write_indent(emitter) { + return false + } + i += width(value[i]) + } else { + if !write(emitter, value, &i) { + return false + } + } + spaces = true + } else if is_break(value, i) { + if !breaks && value[i] == '\n' { + if !put_break(emitter) { + return false + } + } + if !write_break(emitter, value, &i) { + return false + } + emitter.indention = true + breaks = true + } else { + if breaks { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !write(emitter, value, &i) { + return false + } + emitter.indention = false + spaces = false + breaks = false + } + } + + emitter.whitespace = false + emitter.indention = false + if emitter.root_context { + emitter.open_ended = true + } + + return true +} + +func yaml_emitter_write_single_quoted_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool { + + if !yaml_emitter_write_indicator(emitter, []byte{'\''}, true, false, false) { + return false + } + + spaces := false + breaks := false + for i := 0; i < len(value); { + if is_space(value, i) { + if allow_breaks && !spaces && emitter.column > emitter.best_width && i > 0 && i < len(value)-1 && !is_space(value, i+1) { + if !yaml_emitter_write_indent(emitter) { + return false + } + i += width(value[i]) + } else { + if !write(emitter, value, &i) { + return false + } + } + spaces = true + } else if is_break(value, i) { + if !breaks && value[i] == '\n' { + if !put_break(emitter) { + return false + } + } + if !write_break(emitter, value, &i) { + return false + } + emitter.indention = true + breaks = true + } else { + if breaks { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if value[i] == '\'' { + if !put(emitter, '\'') { + return false + } + } + if !write(emitter, value, &i) { + return false + } + emitter.indention = false + spaces = false + breaks = false + } + } + if !yaml_emitter_write_indicator(emitter, []byte{'\''}, false, false, false) { + return false + } + emitter.whitespace = false + emitter.indention = false + return true +} + +func yaml_emitter_write_double_quoted_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool { + spaces := false + if !yaml_emitter_write_indicator(emitter, []byte{'"'}, true, false, false) { + return false + } + + for i := 0; i < len(value); { + if !is_printable(value, i) || (!emitter.unicode && !is_ascii(value, i)) || + is_bom(value, i) || is_break(value, i) || + value[i] == '"' || value[i] == '\\' { + + octet := value[i] + + var w int + var v rune + switch { + case octet&0x80 == 0x00: + w, v = 1, rune(octet&0x7F) + case octet&0xE0 == 0xC0: + w, v = 2, rune(octet&0x1F) + case octet&0xF0 == 0xE0: + w, v = 3, rune(octet&0x0F) + case octet&0xF8 == 0xF0: + w, v = 4, rune(octet&0x07) + } + for k := 1; k < w; k++ { + octet = value[i+k] + v = (v << 6) + (rune(octet) & 0x3F) + } + i += w + + if !put(emitter, '\\') { + return false + } + + var ok bool + switch v { + case 0x00: + ok = put(emitter, '0') + case 0x07: + ok = put(emitter, 'a') + case 0x08: + ok = put(emitter, 'b') + case 0x09: + ok = put(emitter, 't') + case 0x0A: + ok = put(emitter, 'n') + case 0x0b: + ok = put(emitter, 'v') + case 0x0c: + ok = put(emitter, 'f') + case 0x0d: + ok = put(emitter, 'r') + case 0x1b: + ok = put(emitter, 'e') + case 0x22: + ok = put(emitter, '"') + case 0x5c: + ok = put(emitter, '\\') + case 0x85: + ok = put(emitter, 'N') + case 0xA0: + ok = put(emitter, '_') + case 0x2028: + ok = put(emitter, 'L') + case 0x2029: + ok = put(emitter, 'P') + default: + if v <= 0xFF { + ok = put(emitter, 'x') + w = 2 + } else if v <= 0xFFFF { + ok = put(emitter, 'u') + w = 4 + } else { + ok = put(emitter, 'U') + w = 8 + } + for k := (w - 1) * 4; ok && k >= 0; k -= 4 { + digit := byte((v >> uint(k)) & 0x0F) + if digit < 10 { + ok = put(emitter, digit+'0') + } else { + ok = put(emitter, digit+'A'-10) + } + } + } + if !ok { + return false + } + spaces = false + } else if is_space(value, i) { + if allow_breaks && !spaces && emitter.column > emitter.best_width && i > 0 && i < len(value)-1 { + if !yaml_emitter_write_indent(emitter) { + return false + } + if is_space(value, i+1) { + if !put(emitter, '\\') { + return false + } + } + i += width(value[i]) + } else if !write(emitter, value, &i) { + return false + } + spaces = true + } else { + if !write(emitter, value, &i) { + return false + } + spaces = false + } + } + if !yaml_emitter_write_indicator(emitter, []byte{'"'}, false, false, false) { + return false + } + emitter.whitespace = false + emitter.indention = false + return true +} + +func yaml_emitter_write_block_scalar_hints(emitter *yaml_emitter_t, value []byte) bool { + if is_space(value, 0) || is_break(value, 0) { + indent_hint := []byte{'0' + byte(emitter.best_indent)} + if !yaml_emitter_write_indicator(emitter, indent_hint, false, false, false) { + return false + } + } + + emitter.open_ended = false + + var chomp_hint [1]byte + if len(value) == 0 { + chomp_hint[0] = '-' + } else { + i := len(value) - 1 + for value[i]&0xC0 == 0x80 { + i-- + } + if !is_break(value, i) { + chomp_hint[0] = '-' + } else if i == 0 { + chomp_hint[0] = '+' + emitter.open_ended = true + } else { + i-- + for value[i]&0xC0 == 0x80 { + i-- + } + if is_break(value, i) { + chomp_hint[0] = '+' + emitter.open_ended = true + } + } + } + if chomp_hint[0] != 0 { + if !yaml_emitter_write_indicator(emitter, chomp_hint[:], false, false, false) { + return false + } + } + return true +} + +func yaml_emitter_write_literal_scalar(emitter *yaml_emitter_t, value []byte) bool { + if !yaml_emitter_write_indicator(emitter, []byte{'|'}, true, false, false) { + return false + } + if !yaml_emitter_write_block_scalar_hints(emitter, value) { + return false + } + if !put_break(emitter) { + return false + } + emitter.indention = true + emitter.whitespace = true + breaks := true + for i := 0; i < len(value); { + if is_break(value, i) { + if !write_break(emitter, value, &i) { + return false + } + emitter.indention = true + breaks = true + } else { + if breaks { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !write(emitter, value, &i) { + return false + } + emitter.indention = false + breaks = false + } + } + + return true +} + +func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) bool { + if !yaml_emitter_write_indicator(emitter, []byte{'>'}, true, false, false) { + return false + } + if !yaml_emitter_write_block_scalar_hints(emitter, value) { + return false + } + + if !put_break(emitter) { + return false + } + emitter.indention = true + emitter.whitespace = true + + breaks := true + leading_spaces := true + for i := 0; i < len(value); { + if is_break(value, i) { + if !breaks && !leading_spaces && value[i] == '\n' { + k := 0 + for is_break(value, k) { + k += width(value[k]) + } + if !is_blankz(value, k) { + if !put_break(emitter) { + return false + } + } + } + if !write_break(emitter, value, &i) { + return false + } + emitter.indention = true + breaks = true + } else { + if breaks { + if !yaml_emitter_write_indent(emitter) { + return false + } + leading_spaces = is_blank(value, i) + } + if !breaks && is_space(value, i) && !is_space(value, i+1) && emitter.column > emitter.best_width { + if !yaml_emitter_write_indent(emitter) { + return false + } + i += width(value[i]) + } else { + if !write(emitter, value, &i) { + return false + } + } + emitter.indention = false + breaks = false + } + } + return true +} diff --git a/vendor/github.com/mikefarah/yaml/v2/encode.go b/vendor/github.com/mikefarah/yaml/v2/encode.go new file mode 100644 index 0000000000..a14435e82f --- /dev/null +++ b/vendor/github.com/mikefarah/yaml/v2/encode.go @@ -0,0 +1,362 @@ +package yaml + +import ( + "encoding" + "fmt" + "io" + "reflect" + "regexp" + "sort" + "strconv" + "strings" + "time" + "unicode/utf8" +) + +type encoder struct { + emitter yaml_emitter_t + event yaml_event_t + out []byte + flow bool + // doneInit holds whether the initial stream_start_event has been + // emitted. + doneInit bool +} + +func newEncoder() *encoder { + e := &encoder{} + yaml_emitter_initialize(&e.emitter) + yaml_emitter_set_output_string(&e.emitter, &e.out) + yaml_emitter_set_unicode(&e.emitter, true) + return e +} + +func newEncoderWithWriter(w io.Writer) *encoder { + e := &encoder{} + yaml_emitter_initialize(&e.emitter) + yaml_emitter_set_output_writer(&e.emitter, w) + yaml_emitter_set_unicode(&e.emitter, true) + return e +} + +func (e *encoder) init() { + if e.doneInit { + return + } + yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING) + e.emit() + e.doneInit = true +} + +func (e *encoder) finish() { + e.emitter.open_ended = false + yaml_stream_end_event_initialize(&e.event) + e.emit() +} + +func (e *encoder) destroy() { + yaml_emitter_delete(&e.emitter) +} + +func (e *encoder) emit() { + // This will internally delete the e.event value. + e.must(yaml_emitter_emit(&e.emitter, &e.event)) +} + +func (e *encoder) must(ok bool) { + if !ok { + msg := e.emitter.problem + if msg == "" { + msg = "unknown problem generating YAML content" + } + failf("%s", msg) + } +} + +func (e *encoder) marshalDoc(tag string, in reflect.Value) { + e.init() + yaml_document_start_event_initialize(&e.event, nil, nil, true) + e.emit() + e.marshal(tag, in) + yaml_document_end_event_initialize(&e.event, true) + e.emit() +} + +func (e *encoder) marshal(tag string, in reflect.Value) { + if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() { + e.nilv() + return + } + iface := in.Interface() + switch m := iface.(type) { + case time.Time, *time.Time: + // Although time.Time implements TextMarshaler, + // we don't want to treat it as a string for YAML + // purposes because YAML has special support for + // timestamps. + case Marshaler: + v, err := m.MarshalYAML() + if err != nil { + fail(err) + } + if v == nil { + e.nilv() + return + } + in = reflect.ValueOf(v) + case encoding.TextMarshaler: + text, err := m.MarshalText() + if err != nil { + fail(err) + } + in = reflect.ValueOf(string(text)) + case nil: + e.nilv() + return + } + switch in.Kind() { + case reflect.Interface: + e.marshal(tag, in.Elem()) + case reflect.Map: + e.mapv(tag, in) + case reflect.Ptr: + if in.Type() == ptrTimeType { + e.timev(tag, in.Elem()) + } else { + e.marshal(tag, in.Elem()) + } + case reflect.Struct: + if in.Type() == timeType { + e.timev(tag, in) + } else { + e.structv(tag, in) + } + case reflect.Slice, reflect.Array: + if in.Type().Elem() == mapItemType { + e.itemsv(tag, in) + } else { + e.slicev(tag, in) + } + case reflect.String: + e.stringv(tag, in) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + if in.Type() == durationType { + e.stringv(tag, reflect.ValueOf(iface.(time.Duration).String())) + } else { + e.intv(tag, in) + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + e.uintv(tag, in) + case reflect.Float32, reflect.Float64: + e.floatv(tag, in) + case reflect.Bool: + e.boolv(tag, in) + default: + panic("cannot marshal type: " + in.Type().String()) + } +} + +func (e *encoder) mapv(tag string, in reflect.Value) { + e.mappingv(tag, func() { + keys := keyList(in.MapKeys()) + sort.Sort(keys) + for _, k := range keys { + e.marshal("", k) + e.marshal("", in.MapIndex(k)) + } + }) +} + +func (e *encoder) itemsv(tag string, in reflect.Value) { + e.mappingv(tag, func() { + slice := in.Convert(reflect.TypeOf([]MapItem{})).Interface().([]MapItem) + for _, item := range slice { + e.marshal("", reflect.ValueOf(item.Key)) + e.marshal("", reflect.ValueOf(item.Value)) + } + }) +} + +func (e *encoder) structv(tag string, in reflect.Value) { + sinfo, err := getStructInfo(in.Type()) + if err != nil { + panic(err) + } + e.mappingv(tag, func() { + for _, info := range sinfo.FieldsList { + var value reflect.Value + if info.Inline == nil { + value = in.Field(info.Num) + } else { + value = in.FieldByIndex(info.Inline) + } + if info.OmitEmpty && isZero(value) { + continue + } + e.marshal("", reflect.ValueOf(info.Key)) + e.flow = info.Flow + e.marshal("", value) + } + if sinfo.InlineMap >= 0 { + m := in.Field(sinfo.InlineMap) + if m.Len() > 0 { + e.flow = false + keys := keyList(m.MapKeys()) + sort.Sort(keys) + for _, k := range keys { + if _, found := sinfo.FieldsMap[k.String()]; found { + panic(fmt.Sprintf("Can't have key %q in inlined map; conflicts with struct field", k.String())) + } + e.marshal("", k) + e.flow = false + e.marshal("", m.MapIndex(k)) + } + } + } + }) +} + +func (e *encoder) mappingv(tag string, f func()) { + implicit := tag == "" + style := yaml_BLOCK_MAPPING_STYLE + if e.flow { + e.flow = false + style = yaml_FLOW_MAPPING_STYLE + } + yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style) + e.emit() + f() + yaml_mapping_end_event_initialize(&e.event) + e.emit() +} + +func (e *encoder) slicev(tag string, in reflect.Value) { + implicit := tag == "" + style := yaml_BLOCK_SEQUENCE_STYLE + if e.flow { + e.flow = false + style = yaml_FLOW_SEQUENCE_STYLE + } + e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) + e.emit() + n := in.Len() + for i := 0; i < n; i++ { + e.marshal("", in.Index(i)) + } + e.must(yaml_sequence_end_event_initialize(&e.event)) + e.emit() +} + +// isBase60 returns whether s is in base 60 notation as defined in YAML 1.1. +// +// The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported +// in YAML 1.2 and by this package, but these should be marshalled quoted for +// the time being for compatibility with other parsers. +func isBase60Float(s string) (result bool) { + // Fast path. + if s == "" { + return false + } + c := s[0] + if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 { + return false + } + // Do the full match. + return base60float.MatchString(s) +} + +// From http://yaml.org/type/float.html, except the regular expression there +// is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix. +var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`) + +func (e *encoder) stringv(tag string, in reflect.Value) { + var style yaml_scalar_style_t + s := in.String() + canUsePlain := true + switch { + case !utf8.ValidString(s): + if tag == yaml_BINARY_TAG { + failf("explicitly tagged !!binary data must be base64-encoded") + } + if tag != "" { + failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag)) + } + // It can't be encoded directly as YAML so use a binary tag + // and encode it as base64. + tag = yaml_BINARY_TAG + s = encodeBase64(s) + case tag == "": + // Check to see if it would resolve to a specific + // tag when encoded unquoted. If it doesn't, + // there's no need to quote it. + rtag, _ := resolve("", s) + canUsePlain = rtag == yaml_STR_TAG && !isBase60Float(s) + } + // Note: it's possible for user code to emit invalid YAML + // if they explicitly specify a tag and a string containing + // text that's incompatible with that tag. + switch { + case strings.Contains(s, "\n"): + style = yaml_LITERAL_SCALAR_STYLE + case canUsePlain: + style = yaml_PLAIN_SCALAR_STYLE + default: + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + e.emitScalar(s, "", tag, style) +} + +func (e *encoder) boolv(tag string, in reflect.Value) { + var s string + if in.Bool() { + s = "true" + } else { + s = "false" + } + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) +} + +func (e *encoder) intv(tag string, in reflect.Value) { + s := strconv.FormatInt(in.Int(), 10) + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) +} + +func (e *encoder) uintv(tag string, in reflect.Value) { + s := strconv.FormatUint(in.Uint(), 10) + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) +} + +func (e *encoder) timev(tag string, in reflect.Value) { + t := in.Interface().(time.Time) + s := t.Format(time.RFC3339Nano) + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) +} + +func (e *encoder) floatv(tag string, in reflect.Value) { + // Issue #352: When formatting, use the precision of the underlying value + precision := 64 + if in.Kind() == reflect.Float32 { + precision = 32 + } + + s := strconv.FormatFloat(in.Float(), 'g', -1, precision) + switch s { + case "+Inf": + s = ".inf" + case "-Inf": + s = "-.inf" + case "NaN": + s = ".nan" + } + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) +} + +func (e *encoder) nilv() { + e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE) +} + +func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t) { + implicit := tag == "" + e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style)) + e.emit() +} diff --git a/vendor/github.com/mikefarah/yaml/v2/go.mod b/vendor/github.com/mikefarah/yaml/v2/go.mod new file mode 100644 index 0000000000..a0d5f48b30 --- /dev/null +++ b/vendor/github.com/mikefarah/yaml/v2/go.mod @@ -0,0 +1,5 @@ +module "github.com/mikefarah/yaml/v2" + +require ( + "gopkg.in/check.v1" v0.0.0-20161208181325-20d25e280405 +) diff --git a/vendor/github.com/mikefarah/yaml/v2/parserc.go b/vendor/github.com/mikefarah/yaml/v2/parserc.go new file mode 100644 index 0000000000..81d05dfe57 --- /dev/null +++ b/vendor/github.com/mikefarah/yaml/v2/parserc.go @@ -0,0 +1,1095 @@ +package yaml + +import ( + "bytes" +) + +// The parser implements the following grammar: +// +// stream ::= STREAM-START implicit_document? explicit_document* STREAM-END +// implicit_document ::= block_node DOCUMENT-END* +// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +// block_node_or_indentless_sequence ::= +// ALIAS +// | properties (block_content | indentless_block_sequence)? +// | block_content +// | indentless_block_sequence +// block_node ::= ALIAS +// | properties block_content? +// | block_content +// flow_node ::= ALIAS +// | properties flow_content? +// | flow_content +// properties ::= TAG ANCHOR? | ANCHOR TAG? +// block_content ::= block_collection | flow_collection | SCALAR +// flow_content ::= flow_collection | SCALAR +// block_collection ::= block_sequence | block_mapping +// flow_collection ::= flow_sequence | flow_mapping +// block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END +// indentless_sequence ::= (BLOCK-ENTRY block_node?)+ +// block_mapping ::= BLOCK-MAPPING_START +// ((KEY block_node_or_indentless_sequence?)? +// (VALUE block_node_or_indentless_sequence?)?)* +// BLOCK-END +// flow_sequence ::= FLOW-SEQUENCE-START +// (flow_sequence_entry FLOW-ENTRY)* +// flow_sequence_entry? +// FLOW-SEQUENCE-END +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// flow_mapping ::= FLOW-MAPPING-START +// (flow_mapping_entry FLOW-ENTRY)* +// flow_mapping_entry? +// FLOW-MAPPING-END +// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? + +// Peek the next token in the token queue. +func peek_token(parser *yaml_parser_t) *yaml_token_t { + if parser.token_available || yaml_parser_fetch_more_tokens(parser) { + return &parser.tokens[parser.tokens_head] + } + return nil +} + +// Remove the next token from the queue (must be called after peek_token). +func skip_token(parser *yaml_parser_t) { + parser.token_available = false + parser.tokens_parsed++ + parser.stream_end_produced = parser.tokens[parser.tokens_head].typ == yaml_STREAM_END_TOKEN + parser.tokens_head++ +} + +// Get the next event. +func yaml_parser_parse(parser *yaml_parser_t, event *yaml_event_t) bool { + // Erase the event object. + *event = yaml_event_t{} + + // No events after the end of the stream or error. + if parser.stream_end_produced || parser.error != yaml_NO_ERROR || parser.state == yaml_PARSE_END_STATE { + return true + } + + // Generate the next event. + return yaml_parser_state_machine(parser, event) +} + +// Set parser error. +func yaml_parser_set_parser_error(parser *yaml_parser_t, problem string, problem_mark yaml_mark_t) bool { + parser.error = yaml_PARSER_ERROR + parser.problem = problem + parser.problem_mark = problem_mark + return false +} + +func yaml_parser_set_parser_error_context(parser *yaml_parser_t, context string, context_mark yaml_mark_t, problem string, problem_mark yaml_mark_t) bool { + parser.error = yaml_PARSER_ERROR + parser.context = context + parser.context_mark = context_mark + parser.problem = problem + parser.problem_mark = problem_mark + return false +} + +// State dispatcher. +func yaml_parser_state_machine(parser *yaml_parser_t, event *yaml_event_t) bool { + //trace("yaml_parser_state_machine", "state:", parser.state.String()) + + switch parser.state { + case yaml_PARSE_STREAM_START_STATE: + return yaml_parser_parse_stream_start(parser, event) + + case yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE: + return yaml_parser_parse_document_start(parser, event, true) + + case yaml_PARSE_DOCUMENT_START_STATE: + return yaml_parser_parse_document_start(parser, event, false) + + case yaml_PARSE_DOCUMENT_CONTENT_STATE: + return yaml_parser_parse_document_content(parser, event) + + case yaml_PARSE_DOCUMENT_END_STATE: + return yaml_parser_parse_document_end(parser, event) + + case yaml_PARSE_BLOCK_NODE_STATE: + return yaml_parser_parse_node(parser, event, true, false) + + case yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE: + return yaml_parser_parse_node(parser, event, true, true) + + case yaml_PARSE_FLOW_NODE_STATE: + return yaml_parser_parse_node(parser, event, false, false) + + case yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE: + return yaml_parser_parse_block_sequence_entry(parser, event, true) + + case yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE: + return yaml_parser_parse_block_sequence_entry(parser, event, false) + + case yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE: + return yaml_parser_parse_indentless_sequence_entry(parser, event) + + case yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE: + return yaml_parser_parse_block_mapping_key(parser, event, true) + + case yaml_PARSE_BLOCK_MAPPING_KEY_STATE: + return yaml_parser_parse_block_mapping_key(parser, event, false) + + case yaml_PARSE_BLOCK_MAPPING_VALUE_STATE: + return yaml_parser_parse_block_mapping_value(parser, event) + + case yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE: + return yaml_parser_parse_flow_sequence_entry(parser, event, true) + + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE: + return yaml_parser_parse_flow_sequence_entry(parser, event, false) + + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE: + return yaml_parser_parse_flow_sequence_entry_mapping_key(parser, event) + + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE: + return yaml_parser_parse_flow_sequence_entry_mapping_value(parser, event) + + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE: + return yaml_parser_parse_flow_sequence_entry_mapping_end(parser, event) + + case yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE: + return yaml_parser_parse_flow_mapping_key(parser, event, true) + + case yaml_PARSE_FLOW_MAPPING_KEY_STATE: + return yaml_parser_parse_flow_mapping_key(parser, event, false) + + case yaml_PARSE_FLOW_MAPPING_VALUE_STATE: + return yaml_parser_parse_flow_mapping_value(parser, event, false) + + case yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE: + return yaml_parser_parse_flow_mapping_value(parser, event, true) + + default: + panic("invalid parser state") + } +} + +// Parse the production: +// stream ::= STREAM-START implicit_document? explicit_document* STREAM-END +// ************ +func yaml_parser_parse_stream_start(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_STREAM_START_TOKEN { + return yaml_parser_set_parser_error(parser, "did not find expected ", token.start_mark) + } + parser.state = yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE + *event = yaml_event_t{ + typ: yaml_STREAM_START_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + encoding: token.encoding, + } + skip_token(parser) + return true +} + +// Parse the productions: +// implicit_document ::= block_node DOCUMENT-END* +// * +// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +// ************************* +func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t, implicit bool) bool { + + token := peek_token(parser) + if token == nil { + return false + } + + // Parse extra document end indicators. + if !implicit { + for token.typ == yaml_DOCUMENT_END_TOKEN { + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } + } + + if implicit && token.typ != yaml_VERSION_DIRECTIVE_TOKEN && + token.typ != yaml_TAG_DIRECTIVE_TOKEN && + token.typ != yaml_DOCUMENT_START_TOKEN && + token.typ != yaml_STREAM_END_TOKEN { + // Parse an implicit document. + if !yaml_parser_process_directives(parser, nil, nil) { + return false + } + parser.states = append(parser.states, yaml_PARSE_DOCUMENT_END_STATE) + parser.state = yaml_PARSE_BLOCK_NODE_STATE + + *event = yaml_event_t{ + typ: yaml_DOCUMENT_START_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + + } else if token.typ != yaml_STREAM_END_TOKEN { + // Parse an explicit document. + var version_directive *yaml_version_directive_t + var tag_directives []yaml_tag_directive_t + start_mark := token.start_mark + if !yaml_parser_process_directives(parser, &version_directive, &tag_directives) { + return false + } + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_DOCUMENT_START_TOKEN { + yaml_parser_set_parser_error(parser, + "did not find expected ", token.start_mark) + return false + } + parser.states = append(parser.states, yaml_PARSE_DOCUMENT_END_STATE) + parser.state = yaml_PARSE_DOCUMENT_CONTENT_STATE + end_mark := token.end_mark + + *event = yaml_event_t{ + typ: yaml_DOCUMENT_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + version_directive: version_directive, + tag_directives: tag_directives, + implicit: false, + } + skip_token(parser) + + } else { + // Parse the stream end. + parser.state = yaml_PARSE_END_STATE + *event = yaml_event_t{ + typ: yaml_STREAM_END_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + skip_token(parser) + } + + return true +} + +// Parse the productions: +// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +// *********** +// +func yaml_parser_parse_document_content(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + if token.typ == yaml_VERSION_DIRECTIVE_TOKEN || + token.typ == yaml_TAG_DIRECTIVE_TOKEN || + token.typ == yaml_DOCUMENT_START_TOKEN || + token.typ == yaml_DOCUMENT_END_TOKEN || + token.typ == yaml_STREAM_END_TOKEN { + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + return yaml_parser_process_empty_scalar(parser, event, + token.start_mark) + } + return yaml_parser_parse_node(parser, event, true, false) +} + +// Parse the productions: +// implicit_document ::= block_node DOCUMENT-END* +// ************* +// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +// +func yaml_parser_parse_document_end(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + + start_mark := token.start_mark + end_mark := token.start_mark + + implicit := true + if token.typ == yaml_DOCUMENT_END_TOKEN { + end_mark = token.end_mark + skip_token(parser) + implicit = false + } + + parser.tag_directives = parser.tag_directives[:0] + + parser.state = yaml_PARSE_DOCUMENT_START_STATE + *event = yaml_event_t{ + typ: yaml_DOCUMENT_END_EVENT, + start_mark: start_mark, + end_mark: end_mark, + implicit: implicit, + } + return true +} + +// Parse the productions: +// block_node_or_indentless_sequence ::= +// ALIAS +// ***** +// | properties (block_content | indentless_block_sequence)? +// ********** * +// | block_content | indentless_block_sequence +// * +// block_node ::= ALIAS +// ***** +// | properties block_content? +// ********** * +// | block_content +// * +// flow_node ::= ALIAS +// ***** +// | properties flow_content? +// ********** * +// | flow_content +// * +// properties ::= TAG ANCHOR? | ANCHOR TAG? +// ************************* +// block_content ::= block_collection | flow_collection | SCALAR +// ****** +// flow_content ::= flow_collection | SCALAR +// ****** +func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, indentless_sequence bool) bool { + //defer trace("yaml_parser_parse_node", "block:", block, "indentless_sequence:", indentless_sequence)() + + token := peek_token(parser) + if token == nil { + return false + } + + if token.typ == yaml_ALIAS_TOKEN { + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + *event = yaml_event_t{ + typ: yaml_ALIAS_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + anchor: token.value, + } + skip_token(parser) + return true + } + + start_mark := token.start_mark + end_mark := token.start_mark + + var tag_token bool + var tag_handle, tag_suffix, anchor []byte + var tag_mark yaml_mark_t + if token.typ == yaml_ANCHOR_TOKEN { + anchor = token.value + start_mark = token.start_mark + end_mark = token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ == yaml_TAG_TOKEN { + tag_token = true + tag_handle = token.value + tag_suffix = token.suffix + tag_mark = token.start_mark + end_mark = token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } + } else if token.typ == yaml_TAG_TOKEN { + tag_token = true + tag_handle = token.value + tag_suffix = token.suffix + start_mark = token.start_mark + tag_mark = token.start_mark + end_mark = token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ == yaml_ANCHOR_TOKEN { + anchor = token.value + end_mark = token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } + } + + var tag []byte + if tag_token { + if len(tag_handle) == 0 { + tag = tag_suffix + tag_suffix = nil + } else { + for i := range parser.tag_directives { + if bytes.Equal(parser.tag_directives[i].handle, tag_handle) { + tag = append([]byte(nil), parser.tag_directives[i].prefix...) + tag = append(tag, tag_suffix...) + break + } + } + if len(tag) == 0 { + yaml_parser_set_parser_error_context(parser, + "while parsing a node", start_mark, + "found undefined tag handle", tag_mark) + return false + } + } + } + + implicit := len(tag) == 0 + if indentless_sequence && token.typ == yaml_BLOCK_ENTRY_TOKEN { + end_mark = token.end_mark + parser.state = yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE + *event = yaml_event_t{ + typ: yaml_SEQUENCE_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(yaml_BLOCK_SEQUENCE_STYLE), + } + return true + } + if token.typ == yaml_SCALAR_TOKEN { + var plain_implicit, quoted_implicit bool + end_mark = token.end_mark + if (len(tag) == 0 && token.style == yaml_PLAIN_SCALAR_STYLE) || (len(tag) == 1 && tag[0] == '!') { + plain_implicit = true + } else if len(tag) == 0 { + quoted_implicit = true + } + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + + *event = yaml_event_t{ + typ: yaml_SCALAR_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + value: token.value, + implicit: plain_implicit, + quoted_implicit: quoted_implicit, + style: yaml_style_t(token.style), + } + skip_token(parser) + return true + } + if token.typ == yaml_FLOW_SEQUENCE_START_TOKEN { + // [Go] Some of the events below can be merged as they differ only on style. + end_mark = token.end_mark + parser.state = yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE + *event = yaml_event_t{ + typ: yaml_SEQUENCE_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(yaml_FLOW_SEQUENCE_STYLE), + } + return true + } + if token.typ == yaml_FLOW_MAPPING_START_TOKEN { + end_mark = token.end_mark + parser.state = yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE + *event = yaml_event_t{ + typ: yaml_MAPPING_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(yaml_FLOW_MAPPING_STYLE), + } + return true + } + if block && token.typ == yaml_BLOCK_SEQUENCE_START_TOKEN { + end_mark = token.end_mark + parser.state = yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE + *event = yaml_event_t{ + typ: yaml_SEQUENCE_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(yaml_BLOCK_SEQUENCE_STYLE), + } + return true + } + if block && token.typ == yaml_BLOCK_MAPPING_START_TOKEN { + end_mark = token.end_mark + parser.state = yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE + *event = yaml_event_t{ + typ: yaml_MAPPING_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(yaml_BLOCK_MAPPING_STYLE), + } + return true + } + if len(anchor) > 0 || len(tag) > 0 { + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + + *event = yaml_event_t{ + typ: yaml_SCALAR_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + quoted_implicit: false, + style: yaml_style_t(yaml_PLAIN_SCALAR_STYLE), + } + return true + } + + context := "while parsing a flow node" + if block { + context = "while parsing a block node" + } + yaml_parser_set_parser_error_context(parser, context, start_mark, + "did not find expected node content", token.start_mark) + return false +} + +// Parse the productions: +// block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END +// ******************** *********** * ********* +// +func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { + if first { + token := peek_token(parser) + parser.marks = append(parser.marks, token.start_mark) + skip_token(parser) + } + + token := peek_token(parser) + if token == nil { + return false + } + + if token.typ == yaml_BLOCK_ENTRY_TOKEN { + mark := token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_BLOCK_ENTRY_TOKEN && token.typ != yaml_BLOCK_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE) + return yaml_parser_parse_node(parser, event, true, false) + } else { + parser.state = yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE + return yaml_parser_process_empty_scalar(parser, event, mark) + } + } + if token.typ == yaml_BLOCK_END_TOKEN { + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + + *event = yaml_event_t{ + typ: yaml_SEQUENCE_END_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + + skip_token(parser) + return true + } + + context_mark := parser.marks[len(parser.marks)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + return yaml_parser_set_parser_error_context(parser, + "while parsing a block collection", context_mark, + "did not find expected '-' indicator", token.start_mark) +} + +// Parse the productions: +// indentless_sequence ::= (BLOCK-ENTRY block_node?)+ +// *********** * +func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + + if token.typ == yaml_BLOCK_ENTRY_TOKEN { + mark := token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_BLOCK_ENTRY_TOKEN && + token.typ != yaml_KEY_TOKEN && + token.typ != yaml_VALUE_TOKEN && + token.typ != yaml_BLOCK_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE) + return yaml_parser_parse_node(parser, event, true, false) + } + parser.state = yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE + return yaml_parser_process_empty_scalar(parser, event, mark) + } + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + + *event = yaml_event_t{ + typ: yaml_SEQUENCE_END_EVENT, + start_mark: token.start_mark, + end_mark: token.start_mark, // [Go] Shouldn't this be token.end_mark? + } + return true +} + +// Parse the productions: +// block_mapping ::= BLOCK-MAPPING_START +// ******************* +// ((KEY block_node_or_indentless_sequence?)? +// *** * +// (VALUE block_node_or_indentless_sequence?)?)* +// +// BLOCK-END +// ********* +// +func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { + if first { + token := peek_token(parser) + parser.marks = append(parser.marks, token.start_mark) + skip_token(parser) + } + + token := peek_token(parser) + if token == nil { + return false + } + + if token.typ == yaml_KEY_TOKEN { + mark := token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_KEY_TOKEN && + token.typ != yaml_VALUE_TOKEN && + token.typ != yaml_BLOCK_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_BLOCK_MAPPING_VALUE_STATE) + return yaml_parser_parse_node(parser, event, true, true) + } else { + parser.state = yaml_PARSE_BLOCK_MAPPING_VALUE_STATE + return yaml_parser_process_empty_scalar(parser, event, mark) + } + } else if token.typ == yaml_BLOCK_END_TOKEN { + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + *event = yaml_event_t{ + typ: yaml_MAPPING_END_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + skip_token(parser) + return true + } + + context_mark := parser.marks[len(parser.marks)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + return yaml_parser_set_parser_error_context(parser, + "while parsing a block mapping", context_mark, + "did not find expected key", token.start_mark) +} + +// Parse the productions: +// block_mapping ::= BLOCK-MAPPING_START +// +// ((KEY block_node_or_indentless_sequence?)? +// +// (VALUE block_node_or_indentless_sequence?)?)* +// ***** * +// BLOCK-END +// +// +func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + if token.typ == yaml_VALUE_TOKEN { + mark := token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_KEY_TOKEN && + token.typ != yaml_VALUE_TOKEN && + token.typ != yaml_BLOCK_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_BLOCK_MAPPING_KEY_STATE) + return yaml_parser_parse_node(parser, event, true, true) + } + parser.state = yaml_PARSE_BLOCK_MAPPING_KEY_STATE + return yaml_parser_process_empty_scalar(parser, event, mark) + } + parser.state = yaml_PARSE_BLOCK_MAPPING_KEY_STATE + return yaml_parser_process_empty_scalar(parser, event, token.start_mark) +} + +// Parse the productions: +// flow_sequence ::= FLOW-SEQUENCE-START +// ******************* +// (flow_sequence_entry FLOW-ENTRY)* +// * ********** +// flow_sequence_entry? +// * +// FLOW-SEQUENCE-END +// ***************** +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * +// +func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { + if first { + token := peek_token(parser) + parser.marks = append(parser.marks, token.start_mark) + skip_token(parser) + } + token := peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { + if !first { + if token.typ == yaml_FLOW_ENTRY_TOKEN { + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } else { + context_mark := parser.marks[len(parser.marks)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + return yaml_parser_set_parser_error_context(parser, + "while parsing a flow sequence", context_mark, + "did not find expected ',' or ']'", token.start_mark) + } + } + + if token.typ == yaml_KEY_TOKEN { + parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE + *event = yaml_event_t{ + typ: yaml_MAPPING_START_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + implicit: true, + style: yaml_style_t(yaml_FLOW_MAPPING_STYLE), + } + skip_token(parser) + return true + } else if token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } + } + + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + + *event = yaml_event_t{ + typ: yaml_SEQUENCE_END_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + + skip_token(parser) + return true +} + +// +// Parse the productions: +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// *** * +// +func yaml_parser_parse_flow_sequence_entry_mapping_key(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_VALUE_TOKEN && + token.typ != yaml_FLOW_ENTRY_TOKEN && + token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } + mark := token.end_mark + skip_token(parser) + parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE + return yaml_parser_process_empty_scalar(parser, event, mark) +} + +// Parse the productions: +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// ***** * +// +func yaml_parser_parse_flow_sequence_entry_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + if token.typ == yaml_VALUE_TOKEN { + skip_token(parser) + token := peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_FLOW_ENTRY_TOKEN && token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } + } + parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE + return yaml_parser_process_empty_scalar(parser, event, token.start_mark) +} + +// Parse the productions: +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * +// +func yaml_parser_parse_flow_sequence_entry_mapping_end(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE + *event = yaml_event_t{ + typ: yaml_MAPPING_END_EVENT, + start_mark: token.start_mark, + end_mark: token.start_mark, // [Go] Shouldn't this be end_mark? + } + return true +} + +// Parse the productions: +// flow_mapping ::= FLOW-MAPPING-START +// ****************** +// (flow_mapping_entry FLOW-ENTRY)* +// * ********** +// flow_mapping_entry? +// ****************** +// FLOW-MAPPING-END +// **************** +// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * *** * +// +func yaml_parser_parse_flow_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { + if first { + token := peek_token(parser) + parser.marks = append(parser.marks, token.start_mark) + skip_token(parser) + } + + token := peek_token(parser) + if token == nil { + return false + } + + if token.typ != yaml_FLOW_MAPPING_END_TOKEN { + if !first { + if token.typ == yaml_FLOW_ENTRY_TOKEN { + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } else { + context_mark := parser.marks[len(parser.marks)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + return yaml_parser_set_parser_error_context(parser, + "while parsing a flow mapping", context_mark, + "did not find expected ',' or '}'", token.start_mark) + } + } + + if token.typ == yaml_KEY_TOKEN { + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_VALUE_TOKEN && + token.typ != yaml_FLOW_ENTRY_TOKEN && + token.typ != yaml_FLOW_MAPPING_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_VALUE_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } else { + parser.state = yaml_PARSE_FLOW_MAPPING_VALUE_STATE + return yaml_parser_process_empty_scalar(parser, event, token.start_mark) + } + } else if token.typ != yaml_FLOW_MAPPING_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } + } + + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + *event = yaml_event_t{ + typ: yaml_MAPPING_END_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + skip_token(parser) + return true +} + +// Parse the productions: +// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * ***** * +// +func yaml_parser_parse_flow_mapping_value(parser *yaml_parser_t, event *yaml_event_t, empty bool) bool { + token := peek_token(parser) + if token == nil { + return false + } + if empty { + parser.state = yaml_PARSE_FLOW_MAPPING_KEY_STATE + return yaml_parser_process_empty_scalar(parser, event, token.start_mark) + } + if token.typ == yaml_VALUE_TOKEN { + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_FLOW_ENTRY_TOKEN && token.typ != yaml_FLOW_MAPPING_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_KEY_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } + } + parser.state = yaml_PARSE_FLOW_MAPPING_KEY_STATE + return yaml_parser_process_empty_scalar(parser, event, token.start_mark) +} + +// Generate an empty scalar event. +func yaml_parser_process_empty_scalar(parser *yaml_parser_t, event *yaml_event_t, mark yaml_mark_t) bool { + *event = yaml_event_t{ + typ: yaml_SCALAR_EVENT, + start_mark: mark, + end_mark: mark, + value: nil, // Empty + implicit: true, + style: yaml_style_t(yaml_PLAIN_SCALAR_STYLE), + } + return true +} + +var default_tag_directives = []yaml_tag_directive_t{ + {[]byte("!"), []byte("!")}, + {[]byte("!!"), []byte("tag:yaml.org,2002:")}, +} + +// Parse directives. +func yaml_parser_process_directives(parser *yaml_parser_t, + version_directive_ref **yaml_version_directive_t, + tag_directives_ref *[]yaml_tag_directive_t) bool { + + var version_directive *yaml_version_directive_t + var tag_directives []yaml_tag_directive_t + + token := peek_token(parser) + if token == nil { + return false + } + + for token.typ == yaml_VERSION_DIRECTIVE_TOKEN || token.typ == yaml_TAG_DIRECTIVE_TOKEN { + if token.typ == yaml_VERSION_DIRECTIVE_TOKEN { + if version_directive != nil { + yaml_parser_set_parser_error(parser, + "found duplicate %YAML directive", token.start_mark) + return false + } + if token.major != 1 || token.minor != 1 { + yaml_parser_set_parser_error(parser, + "found incompatible YAML document", token.start_mark) + return false + } + version_directive = &yaml_version_directive_t{ + major: token.major, + minor: token.minor, + } + } else if token.typ == yaml_TAG_DIRECTIVE_TOKEN { + value := yaml_tag_directive_t{ + handle: token.value, + prefix: token.prefix, + } + if !yaml_parser_append_tag_directive(parser, value, false, token.start_mark) { + return false + } + tag_directives = append(tag_directives, value) + } + + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } + + for i := range default_tag_directives { + if !yaml_parser_append_tag_directive(parser, default_tag_directives[i], true, token.start_mark) { + return false + } + } + + if version_directive_ref != nil { + *version_directive_ref = version_directive + } + if tag_directives_ref != nil { + *tag_directives_ref = tag_directives + } + return true +} + +// Append a tag directive to the directives stack. +func yaml_parser_append_tag_directive(parser *yaml_parser_t, value yaml_tag_directive_t, allow_duplicates bool, mark yaml_mark_t) bool { + for i := range parser.tag_directives { + if bytes.Equal(value.handle, parser.tag_directives[i].handle) { + if allow_duplicates { + return true + } + return yaml_parser_set_parser_error(parser, "found duplicate %TAG directive", mark) + } + } + + // [Go] I suspect the copy is unnecessary. This was likely done + // because there was no way to track ownership of the data. + value_copy := yaml_tag_directive_t{ + handle: make([]byte, len(value.handle)), + prefix: make([]byte, len(value.prefix)), + } + copy(value_copy.handle, value.handle) + copy(value_copy.prefix, value.prefix) + parser.tag_directives = append(parser.tag_directives, value_copy) + return true +} diff --git a/vendor/github.com/mikefarah/yaml/v2/readerc.go b/vendor/github.com/mikefarah/yaml/v2/readerc.go new file mode 100644 index 0000000000..7c1f5fac3d --- /dev/null +++ b/vendor/github.com/mikefarah/yaml/v2/readerc.go @@ -0,0 +1,412 @@ +package yaml + +import ( + "io" +) + +// Set the reader error and return 0. +func yaml_parser_set_reader_error(parser *yaml_parser_t, problem string, offset int, value int) bool { + parser.error = yaml_READER_ERROR + parser.problem = problem + parser.problem_offset = offset + parser.problem_value = value + return false +} + +// Byte order marks. +const ( + bom_UTF8 = "\xef\xbb\xbf" + bom_UTF16LE = "\xff\xfe" + bom_UTF16BE = "\xfe\xff" +) + +// Determine the input stream encoding by checking the BOM symbol. If no BOM is +// found, the UTF-8 encoding is assumed. Return 1 on success, 0 on failure. +func yaml_parser_determine_encoding(parser *yaml_parser_t) bool { + // Ensure that we had enough bytes in the raw buffer. + for !parser.eof && len(parser.raw_buffer)-parser.raw_buffer_pos < 3 { + if !yaml_parser_update_raw_buffer(parser) { + return false + } + } + + // Determine the encoding. + buf := parser.raw_buffer + pos := parser.raw_buffer_pos + avail := len(buf) - pos + if avail >= 2 && buf[pos] == bom_UTF16LE[0] && buf[pos+1] == bom_UTF16LE[1] { + parser.encoding = yaml_UTF16LE_ENCODING + parser.raw_buffer_pos += 2 + parser.offset += 2 + } else if avail >= 2 && buf[pos] == bom_UTF16BE[0] && buf[pos+1] == bom_UTF16BE[1] { + parser.encoding = yaml_UTF16BE_ENCODING + parser.raw_buffer_pos += 2 + parser.offset += 2 + } else if avail >= 3 && buf[pos] == bom_UTF8[0] && buf[pos+1] == bom_UTF8[1] && buf[pos+2] == bom_UTF8[2] { + parser.encoding = yaml_UTF8_ENCODING + parser.raw_buffer_pos += 3 + parser.offset += 3 + } else { + parser.encoding = yaml_UTF8_ENCODING + } + return true +} + +// Update the raw buffer. +func yaml_parser_update_raw_buffer(parser *yaml_parser_t) bool { + size_read := 0 + + // Return if the raw buffer is full. + if parser.raw_buffer_pos == 0 && len(parser.raw_buffer) == cap(parser.raw_buffer) { + return true + } + + // Return on EOF. + if parser.eof { + return true + } + + // Move the remaining bytes in the raw buffer to the beginning. + if parser.raw_buffer_pos > 0 && parser.raw_buffer_pos < len(parser.raw_buffer) { + copy(parser.raw_buffer, parser.raw_buffer[parser.raw_buffer_pos:]) + } + parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)-parser.raw_buffer_pos] + parser.raw_buffer_pos = 0 + + // Call the read handler to fill the buffer. + size_read, err := parser.read_handler(parser, parser.raw_buffer[len(parser.raw_buffer):cap(parser.raw_buffer)]) + parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)+size_read] + if err == io.EOF { + parser.eof = true + } else if err != nil { + return yaml_parser_set_reader_error(parser, "input error: "+err.Error(), parser.offset, -1) + } + return true +} + +// Ensure that the buffer contains at least `length` characters. +// Return true on success, false on failure. +// +// The length is supposed to be significantly less that the buffer size. +func yaml_parser_update_buffer(parser *yaml_parser_t, length int) bool { + if parser.read_handler == nil { + panic("read handler must be set") + } + + // [Go] This function was changed to guarantee the requested length size at EOF. + // The fact we need to do this is pretty awful, but the description above implies + // for that to be the case, and there are tests + + // If the EOF flag is set and the raw buffer is empty, do nothing. + if parser.eof && parser.raw_buffer_pos == len(parser.raw_buffer) { + // [Go] ACTUALLY! Read the documentation of this function above. + // This is just broken. To return true, we need to have the + // given length in the buffer. Not doing that means every single + // check that calls this function to make sure the buffer has a + // given length is Go) panicking; or C) accessing invalid memory. + //return true + } + + // Return if the buffer contains enough characters. + if parser.unread >= length { + return true + } + + // Determine the input encoding if it is not known yet. + if parser.encoding == yaml_ANY_ENCODING { + if !yaml_parser_determine_encoding(parser) { + return false + } + } + + // Move the unread characters to the beginning of the buffer. + buffer_len := len(parser.buffer) + if parser.buffer_pos > 0 && parser.buffer_pos < buffer_len { + copy(parser.buffer, parser.buffer[parser.buffer_pos:]) + buffer_len -= parser.buffer_pos + parser.buffer_pos = 0 + } else if parser.buffer_pos == buffer_len { + buffer_len = 0 + parser.buffer_pos = 0 + } + + // Open the whole buffer for writing, and cut it before returning. + parser.buffer = parser.buffer[:cap(parser.buffer)] + + // Fill the buffer until it has enough characters. + first := true + for parser.unread < length { + + // Fill the raw buffer if necessary. + if !first || parser.raw_buffer_pos == len(parser.raw_buffer) { + if !yaml_parser_update_raw_buffer(parser) { + parser.buffer = parser.buffer[:buffer_len] + return false + } + } + first = false + + // Decode the raw buffer. + inner: + for parser.raw_buffer_pos != len(parser.raw_buffer) { + var value rune + var width int + + raw_unread := len(parser.raw_buffer) - parser.raw_buffer_pos + + // Decode the next character. + switch parser.encoding { + case yaml_UTF8_ENCODING: + // Decode a UTF-8 character. Check RFC 3629 + // (http://www.ietf.org/rfc/rfc3629.txt) for more details. + // + // The following table (taken from the RFC) is used for + // decoding. + // + // Char. number range | UTF-8 octet sequence + // (hexadecimal) | (binary) + // --------------------+------------------------------------ + // 0000 0000-0000 007F | 0xxxxxxx + // 0000 0080-0000 07FF | 110xxxxx 10xxxxxx + // 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx + // 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + // + // Additionally, the characters in the range 0xD800-0xDFFF + // are prohibited as they are reserved for use with UTF-16 + // surrogate pairs. + + // Determine the length of the UTF-8 sequence. + octet := parser.raw_buffer[parser.raw_buffer_pos] + switch { + case octet&0x80 == 0x00: + width = 1 + case octet&0xE0 == 0xC0: + width = 2 + case octet&0xF0 == 0xE0: + width = 3 + case octet&0xF8 == 0xF0: + width = 4 + default: + // The leading octet is invalid. + return yaml_parser_set_reader_error(parser, + "invalid leading UTF-8 octet", + parser.offset, int(octet)) + } + + // Check if the raw buffer contains an incomplete character. + if width > raw_unread { + if parser.eof { + return yaml_parser_set_reader_error(parser, + "incomplete UTF-8 octet sequence", + parser.offset, -1) + } + break inner + } + + // Decode the leading octet. + switch { + case octet&0x80 == 0x00: + value = rune(octet & 0x7F) + case octet&0xE0 == 0xC0: + value = rune(octet & 0x1F) + case octet&0xF0 == 0xE0: + value = rune(octet & 0x0F) + case octet&0xF8 == 0xF0: + value = rune(octet & 0x07) + default: + value = 0 + } + + // Check and decode the trailing octets. + for k := 1; k < width; k++ { + octet = parser.raw_buffer[parser.raw_buffer_pos+k] + + // Check if the octet is valid. + if (octet & 0xC0) != 0x80 { + return yaml_parser_set_reader_error(parser, + "invalid trailing UTF-8 octet", + parser.offset+k, int(octet)) + } + + // Decode the octet. + value = (value << 6) + rune(octet&0x3F) + } + + // Check the length of the sequence against the value. + switch { + case width == 1: + case width == 2 && value >= 0x80: + case width == 3 && value >= 0x800: + case width == 4 && value >= 0x10000: + default: + return yaml_parser_set_reader_error(parser, + "invalid length of a UTF-8 sequence", + parser.offset, -1) + } + + // Check the range of the value. + if value >= 0xD800 && value <= 0xDFFF || value > 0x10FFFF { + return yaml_parser_set_reader_error(parser, + "invalid Unicode character", + parser.offset, int(value)) + } + + case yaml_UTF16LE_ENCODING, yaml_UTF16BE_ENCODING: + var low, high int + if parser.encoding == yaml_UTF16LE_ENCODING { + low, high = 0, 1 + } else { + low, high = 1, 0 + } + + // The UTF-16 encoding is not as simple as one might + // naively think. Check RFC 2781 + // (http://www.ietf.org/rfc/rfc2781.txt). + // + // Normally, two subsequent bytes describe a Unicode + // character. However a special technique (called a + // surrogate pair) is used for specifying character + // values larger than 0xFFFF. + // + // A surrogate pair consists of two pseudo-characters: + // high surrogate area (0xD800-0xDBFF) + // low surrogate area (0xDC00-0xDFFF) + // + // The following formulas are used for decoding + // and encoding characters using surrogate pairs: + // + // U = U' + 0x10000 (0x01 00 00 <= U <= 0x10 FF FF) + // U' = yyyyyyyyyyxxxxxxxxxx (0 <= U' <= 0x0F FF FF) + // W1 = 110110yyyyyyyyyy + // W2 = 110111xxxxxxxxxx + // + // where U is the character value, W1 is the high surrogate + // area, W2 is the low surrogate area. + + // Check for incomplete UTF-16 character. + if raw_unread < 2 { + if parser.eof { + return yaml_parser_set_reader_error(parser, + "incomplete UTF-16 character", + parser.offset, -1) + } + break inner + } + + // Get the character. + value = rune(parser.raw_buffer[parser.raw_buffer_pos+low]) + + (rune(parser.raw_buffer[parser.raw_buffer_pos+high]) << 8) + + // Check for unexpected low surrogate area. + if value&0xFC00 == 0xDC00 { + return yaml_parser_set_reader_error(parser, + "unexpected low surrogate area", + parser.offset, int(value)) + } + + // Check for a high surrogate area. + if value&0xFC00 == 0xD800 { + width = 4 + + // Check for incomplete surrogate pair. + if raw_unread < 4 { + if parser.eof { + return yaml_parser_set_reader_error(parser, + "incomplete UTF-16 surrogate pair", + parser.offset, -1) + } + break inner + } + + // Get the next character. + value2 := rune(parser.raw_buffer[parser.raw_buffer_pos+low+2]) + + (rune(parser.raw_buffer[parser.raw_buffer_pos+high+2]) << 8) + + // Check for a low surrogate area. + if value2&0xFC00 != 0xDC00 { + return yaml_parser_set_reader_error(parser, + "expected low surrogate area", + parser.offset+2, int(value2)) + } + + // Generate the value of the surrogate pair. + value = 0x10000 + ((value & 0x3FF) << 10) + (value2 & 0x3FF) + } else { + width = 2 + } + + default: + panic("impossible") + } + + // Check if the character is in the allowed range: + // #x9 | #xA | #xD | [#x20-#x7E] (8 bit) + // | #x85 | [#xA0-#xD7FF] | [#xE000-#xFFFD] (16 bit) + // | [#x10000-#x10FFFF] (32 bit) + switch { + case value == 0x09: + case value == 0x0A: + case value == 0x0D: + case value >= 0x20 && value <= 0x7E: + case value == 0x85: + case value >= 0xA0 && value <= 0xD7FF: + case value >= 0xE000 && value <= 0xFFFD: + case value >= 0x10000 && value <= 0x10FFFF: + default: + return yaml_parser_set_reader_error(parser, + "control characters are not allowed", + parser.offset, int(value)) + } + + // Move the raw pointers. + parser.raw_buffer_pos += width + parser.offset += width + + // Finally put the character into the buffer. + if value <= 0x7F { + // 0000 0000-0000 007F . 0xxxxxxx + parser.buffer[buffer_len+0] = byte(value) + buffer_len += 1 + } else if value <= 0x7FF { + // 0000 0080-0000 07FF . 110xxxxx 10xxxxxx + parser.buffer[buffer_len+0] = byte(0xC0 + (value >> 6)) + parser.buffer[buffer_len+1] = byte(0x80 + (value & 0x3F)) + buffer_len += 2 + } else if value <= 0xFFFF { + // 0000 0800-0000 FFFF . 1110xxxx 10xxxxxx 10xxxxxx + parser.buffer[buffer_len+0] = byte(0xE0 + (value >> 12)) + parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 6) & 0x3F)) + parser.buffer[buffer_len+2] = byte(0x80 + (value & 0x3F)) + buffer_len += 3 + } else { + // 0001 0000-0010 FFFF . 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + parser.buffer[buffer_len+0] = byte(0xF0 + (value >> 18)) + parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 12) & 0x3F)) + parser.buffer[buffer_len+2] = byte(0x80 + ((value >> 6) & 0x3F)) + parser.buffer[buffer_len+3] = byte(0x80 + (value & 0x3F)) + buffer_len += 4 + } + + parser.unread++ + } + + // On EOF, put NUL into the buffer and return. + if parser.eof { + parser.buffer[buffer_len] = 0 + buffer_len++ + parser.unread++ + break + } + } + // [Go] Read the documentation of this function above. To return true, + // we need to have the given length in the buffer. Not doing that means + // every single check that calls this function to make sure the buffer + // has a given length is Go) panicking; or C) accessing invalid memory. + // This happens here due to the EOF above breaking early. + for buffer_len < length { + parser.buffer[buffer_len] = 0 + buffer_len++ + } + parser.buffer = parser.buffer[:buffer_len] + return true +} diff --git a/vendor/github.com/mikefarah/yaml/v2/resolve.go b/vendor/github.com/mikefarah/yaml/v2/resolve.go new file mode 100644 index 0000000000..6c151db6fb --- /dev/null +++ b/vendor/github.com/mikefarah/yaml/v2/resolve.go @@ -0,0 +1,258 @@ +package yaml + +import ( + "encoding/base64" + "math" + "regexp" + "strconv" + "strings" + "time" +) + +type resolveMapItem struct { + value interface{} + tag string +} + +var resolveTable = make([]byte, 256) +var resolveMap = make(map[string]resolveMapItem) + +func init() { + t := resolveTable + t[int('+')] = 'S' // Sign + t[int('-')] = 'S' + for _, c := range "0123456789" { + t[int(c)] = 'D' // Digit + } + for _, c := range "yYnNtTfFoO~" { + t[int(c)] = 'M' // In map + } + t[int('.')] = '.' // Float (potentially in map) + + var resolveMapList = []struct { + v interface{} + tag string + l []string + }{ + {true, yaml_BOOL_TAG, []string{"y", "Y", "yes", "Yes", "YES"}}, + {true, yaml_BOOL_TAG, []string{"true", "True", "TRUE"}}, + {true, yaml_BOOL_TAG, []string{"on", "On", "ON"}}, + {false, yaml_BOOL_TAG, []string{"n", "N", "no", "No", "NO"}}, + {false, yaml_BOOL_TAG, []string{"false", "False", "FALSE"}}, + {false, yaml_BOOL_TAG, []string{"off", "Off", "OFF"}}, + {nil, yaml_NULL_TAG, []string{"", "~", "null", "Null", "NULL"}}, + {math.NaN(), yaml_FLOAT_TAG, []string{".nan", ".NaN", ".NAN"}}, + {math.Inf(+1), yaml_FLOAT_TAG, []string{".inf", ".Inf", ".INF"}}, + {math.Inf(+1), yaml_FLOAT_TAG, []string{"+.inf", "+.Inf", "+.INF"}}, + {math.Inf(-1), yaml_FLOAT_TAG, []string{"-.inf", "-.Inf", "-.INF"}}, + {"<<", yaml_MERGE_TAG, []string{"<<"}}, + } + + m := resolveMap + for _, item := range resolveMapList { + for _, s := range item.l { + m[s] = resolveMapItem{item.v, item.tag} + } + } +} + +const longTagPrefix = "tag:yaml.org,2002:" + +func shortTag(tag string) string { + // TODO This can easily be made faster and produce less garbage. + if strings.HasPrefix(tag, longTagPrefix) { + return "!!" + tag[len(longTagPrefix):] + } + return tag +} + +func longTag(tag string) string { + if strings.HasPrefix(tag, "!!") { + return longTagPrefix + tag[2:] + } + return tag +} + +func resolvableTag(tag string) bool { + switch tag { + case "", yaml_STR_TAG, yaml_BOOL_TAG, yaml_INT_TAG, yaml_FLOAT_TAG, yaml_NULL_TAG, yaml_TIMESTAMP_TAG: + return true + } + return false +} + +var yamlStyleFloat = regexp.MustCompile(`^[-+]?[0-9]*\.?[0-9]+([eE][-+][0-9]+)?$`) + +func resolve(tag string, in string) (rtag string, out interface{}) { + if !resolvableTag(tag) { + return tag, in + } + + defer func() { + switch tag { + case "", rtag, yaml_STR_TAG, yaml_BINARY_TAG: + return + case yaml_FLOAT_TAG: + if rtag == yaml_INT_TAG { + switch v := out.(type) { + case int64: + rtag = yaml_FLOAT_TAG + out = float64(v) + return + case int: + rtag = yaml_FLOAT_TAG + out = float64(v) + return + } + } + } + failf("cannot decode %s `%s` as a %s", shortTag(rtag), in, shortTag(tag)) + }() + + // Any data is accepted as a !!str or !!binary. + // Otherwise, the prefix is enough of a hint about what it might be. + hint := byte('N') + if in != "" { + hint = resolveTable[in[0]] + } + if hint != 0 && tag != yaml_STR_TAG && tag != yaml_BINARY_TAG { + // Handle things we can lookup in a map. + if item, ok := resolveMap[in]; ok { + return item.tag, item.value + } + + // Base 60 floats are a bad idea, were dropped in YAML 1.2, and + // are purposefully unsupported here. They're still quoted on + // the way out for compatibility with other parser, though. + + switch hint { + case 'M': + // We've already checked the map above. + + case '.': + // Not in the map, so maybe a normal float. + floatv, err := strconv.ParseFloat(in, 64) + if err == nil { + return yaml_FLOAT_TAG, floatv + } + + case 'D', 'S': + // Int, float, or timestamp. + // Only try values as a timestamp if the value is unquoted or there's an explicit + // !!timestamp tag. + if tag == "" || tag == yaml_TIMESTAMP_TAG { + t, ok := parseTimestamp(in) + if ok { + return yaml_TIMESTAMP_TAG, t + } + } + + plain := strings.Replace(in, "_", "", -1) + intv, err := strconv.ParseInt(plain, 0, 64) + if err == nil { + if intv == int64(int(intv)) { + return yaml_INT_TAG, int(intv) + } else { + return yaml_INT_TAG, intv + } + } + uintv, err := strconv.ParseUint(plain, 0, 64) + if err == nil { + return yaml_INT_TAG, uintv + } + if yamlStyleFloat.MatchString(plain) { + floatv, err := strconv.ParseFloat(plain, 64) + if err == nil { + return yaml_FLOAT_TAG, floatv + } + } + if strings.HasPrefix(plain, "0b") { + intv, err := strconv.ParseInt(plain[2:], 2, 64) + if err == nil { + if intv == int64(int(intv)) { + return yaml_INT_TAG, int(intv) + } else { + return yaml_INT_TAG, intv + } + } + uintv, err := strconv.ParseUint(plain[2:], 2, 64) + if err == nil { + return yaml_INT_TAG, uintv + } + } else if strings.HasPrefix(plain, "-0b") { + intv, err := strconv.ParseInt("-" + plain[3:], 2, 64) + if err == nil { + if true || intv == int64(int(intv)) { + return yaml_INT_TAG, int(intv) + } else { + return yaml_INT_TAG, intv + } + } + } + default: + panic("resolveTable item not yet handled: " + string(rune(hint)) + " (with " + in + ")") + } + } + return yaml_STR_TAG, in +} + +// encodeBase64 encodes s as base64 that is broken up into multiple lines +// as appropriate for the resulting length. +func encodeBase64(s string) string { + const lineLen = 70 + encLen := base64.StdEncoding.EncodedLen(len(s)) + lines := encLen/lineLen + 1 + buf := make([]byte, encLen*2+lines) + in := buf[0:encLen] + out := buf[encLen:] + base64.StdEncoding.Encode(in, []byte(s)) + k := 0 + for i := 0; i < len(in); i += lineLen { + j := i + lineLen + if j > len(in) { + j = len(in) + } + k += copy(out[k:], in[i:j]) + if lines > 1 { + out[k] = '\n' + k++ + } + } + return string(out[:k]) +} + +// This is a subset of the formats allowed by the regular expression +// defined at http://yaml.org/type/timestamp.html. +var allowedTimestampFormats = []string{ + "2006-1-2T15:4:5.999999999Z07:00", // RCF3339Nano with short date fields. + "2006-1-2t15:4:5.999999999Z07:00", // RFC3339Nano with short date fields and lower-case "t". + "2006-1-2 15:4:5.999999999", // space separated with no time zone + "2006-1-2", // date only + // Notable exception: time.Parse cannot handle: "2001-12-14 21:59:43.10 -5" + // from the set of examples. +} + +// parseTimestamp parses s as a timestamp string and +// returns the timestamp and reports whether it succeeded. +// Timestamp formats are defined at http://yaml.org/type/timestamp.html +func parseTimestamp(s string) (time.Time, bool) { + // TODO write code to check all the formats supported by + // http://yaml.org/type/timestamp.html instead of using time.Parse. + + // Quick check: all date formats start with YYYY-. + i := 0 + for ; i < len(s); i++ { + if c := s[i]; c < '0' || c > '9' { + break + } + } + if i != 4 || i == len(s) || s[i] != '-' { + return time.Time{}, false + } + for _, format := range allowedTimestampFormats { + if t, err := time.Parse(format, s); err == nil { + return t, true + } + } + return time.Time{}, false +} diff --git a/vendor/github.com/mikefarah/yaml/v2/scannerc.go b/vendor/github.com/mikefarah/yaml/v2/scannerc.go new file mode 100644 index 0000000000..077fd1dd2d --- /dev/null +++ b/vendor/github.com/mikefarah/yaml/v2/scannerc.go @@ -0,0 +1,2696 @@ +package yaml + +import ( + "bytes" + "fmt" +) + +// Introduction +// ************ +// +// The following notes assume that you are familiar with the YAML specification +// (http://yaml.org/spec/1.2/spec.html). We mostly follow it, although in +// some cases we are less restrictive that it requires. +// +// The process of transforming a YAML stream into a sequence of events is +// divided on two steps: Scanning and Parsing. +// +// The Scanner transforms the input stream into a sequence of tokens, while the +// parser transform the sequence of tokens produced by the Scanner into a +// sequence of parsing events. +// +// The Scanner is rather clever and complicated. The Parser, on the contrary, +// is a straightforward implementation of a recursive-descendant parser (or, +// LL(1) parser, as it is usually called). +// +// Actually there are two issues of Scanning that might be called "clever", the +// rest is quite straightforward. The issues are "block collection start" and +// "simple keys". Both issues are explained below in details. +// +// Here the Scanning step is explained and implemented. We start with the list +// of all the tokens produced by the Scanner together with short descriptions. +// +// Now, tokens: +// +// STREAM-START(encoding) # The stream start. +// STREAM-END # The stream end. +// VERSION-DIRECTIVE(major,minor) # The '%YAML' directive. +// TAG-DIRECTIVE(handle,prefix) # The '%TAG' directive. +// DOCUMENT-START # '---' +// DOCUMENT-END # '...' +// BLOCK-SEQUENCE-START # Indentation increase denoting a block +// BLOCK-MAPPING-START # sequence or a block mapping. +// BLOCK-END # Indentation decrease. +// FLOW-SEQUENCE-START # '[' +// FLOW-SEQUENCE-END # ']' +// BLOCK-SEQUENCE-START # '{' +// BLOCK-SEQUENCE-END # '}' +// BLOCK-ENTRY # '-' +// FLOW-ENTRY # ',' +// KEY # '?' or nothing (simple keys). +// VALUE # ':' +// ALIAS(anchor) # '*anchor' +// ANCHOR(anchor) # '&anchor' +// TAG(handle,suffix) # '!handle!suffix' +// SCALAR(value,style) # A scalar. +// +// The following two tokens are "virtual" tokens denoting the beginning and the +// end of the stream: +// +// STREAM-START(encoding) +// STREAM-END +// +// We pass the information about the input stream encoding with the +// STREAM-START token. +// +// The next two tokens are responsible for tags: +// +// VERSION-DIRECTIVE(major,minor) +// TAG-DIRECTIVE(handle,prefix) +// +// Example: +// +// %YAML 1.1 +// %TAG ! !foo +// %TAG !yaml! tag:yaml.org,2002: +// --- +// +// The correspoding sequence of tokens: +// +// STREAM-START(utf-8) +// VERSION-DIRECTIVE(1,1) +// TAG-DIRECTIVE("!","!foo") +// TAG-DIRECTIVE("!yaml","tag:yaml.org,2002:") +// DOCUMENT-START +// STREAM-END +// +// Note that the VERSION-DIRECTIVE and TAG-DIRECTIVE tokens occupy a whole +// line. +// +// The document start and end indicators are represented by: +// +// DOCUMENT-START +// DOCUMENT-END +// +// Note that if a YAML stream contains an implicit document (without '---' +// and '...' indicators), no DOCUMENT-START and DOCUMENT-END tokens will be +// produced. +// +// In the following examples, we present whole documents together with the +// produced tokens. +// +// 1. An implicit document: +// +// 'a scalar' +// +// Tokens: +// +// STREAM-START(utf-8) +// SCALAR("a scalar",single-quoted) +// STREAM-END +// +// 2. An explicit document: +// +// --- +// 'a scalar' +// ... +// +// Tokens: +// +// STREAM-START(utf-8) +// DOCUMENT-START +// SCALAR("a scalar",single-quoted) +// DOCUMENT-END +// STREAM-END +// +// 3. Several documents in a stream: +// +// 'a scalar' +// --- +// 'another scalar' +// --- +// 'yet another scalar' +// +// Tokens: +// +// STREAM-START(utf-8) +// SCALAR("a scalar",single-quoted) +// DOCUMENT-START +// SCALAR("another scalar",single-quoted) +// DOCUMENT-START +// SCALAR("yet another scalar",single-quoted) +// STREAM-END +// +// We have already introduced the SCALAR token above. The following tokens are +// used to describe aliases, anchors, tag, and scalars: +// +// ALIAS(anchor) +// ANCHOR(anchor) +// TAG(handle,suffix) +// SCALAR(value,style) +// +// The following series of examples illustrate the usage of these tokens: +// +// 1. A recursive sequence: +// +// &A [ *A ] +// +// Tokens: +// +// STREAM-START(utf-8) +// ANCHOR("A") +// FLOW-SEQUENCE-START +// ALIAS("A") +// FLOW-SEQUENCE-END +// STREAM-END +// +// 2. A tagged scalar: +// +// !!float "3.14" # A good approximation. +// +// Tokens: +// +// STREAM-START(utf-8) +// TAG("!!","float") +// SCALAR("3.14",double-quoted) +// STREAM-END +// +// 3. Various scalar styles: +// +// --- # Implicit empty plain scalars do not produce tokens. +// --- a plain scalar +// --- 'a single-quoted scalar' +// --- "a double-quoted scalar" +// --- |- +// a literal scalar +// --- >- +// a folded +// scalar +// +// Tokens: +// +// STREAM-START(utf-8) +// DOCUMENT-START +// DOCUMENT-START +// SCALAR("a plain scalar",plain) +// DOCUMENT-START +// SCALAR("a single-quoted scalar",single-quoted) +// DOCUMENT-START +// SCALAR("a double-quoted scalar",double-quoted) +// DOCUMENT-START +// SCALAR("a literal scalar",literal) +// DOCUMENT-START +// SCALAR("a folded scalar",folded) +// STREAM-END +// +// Now it's time to review collection-related tokens. We will start with +// flow collections: +// +// FLOW-SEQUENCE-START +// FLOW-SEQUENCE-END +// FLOW-MAPPING-START +// FLOW-MAPPING-END +// FLOW-ENTRY +// KEY +// VALUE +// +// The tokens FLOW-SEQUENCE-START, FLOW-SEQUENCE-END, FLOW-MAPPING-START, and +// FLOW-MAPPING-END represent the indicators '[', ']', '{', and '}' +// correspondingly. FLOW-ENTRY represent the ',' indicator. Finally the +// indicators '?' and ':', which are used for denoting mapping keys and values, +// are represented by the KEY and VALUE tokens. +// +// The following examples show flow collections: +// +// 1. A flow sequence: +// +// [item 1, item 2, item 3] +// +// Tokens: +// +// STREAM-START(utf-8) +// FLOW-SEQUENCE-START +// SCALAR("item 1",plain) +// FLOW-ENTRY +// SCALAR("item 2",plain) +// FLOW-ENTRY +// SCALAR("item 3",plain) +// FLOW-SEQUENCE-END +// STREAM-END +// +// 2. A flow mapping: +// +// { +// a simple key: a value, # Note that the KEY token is produced. +// ? a complex key: another value, +// } +// +// Tokens: +// +// STREAM-START(utf-8) +// FLOW-MAPPING-START +// KEY +// SCALAR("a simple key",plain) +// VALUE +// SCALAR("a value",plain) +// FLOW-ENTRY +// KEY +// SCALAR("a complex key",plain) +// VALUE +// SCALAR("another value",plain) +// FLOW-ENTRY +// FLOW-MAPPING-END +// STREAM-END +// +// A simple key is a key which is not denoted by the '?' indicator. Note that +// the Scanner still produce the KEY token whenever it encounters a simple key. +// +// For scanning block collections, the following tokens are used (note that we +// repeat KEY and VALUE here): +// +// BLOCK-SEQUENCE-START +// BLOCK-MAPPING-START +// BLOCK-END +// BLOCK-ENTRY +// KEY +// VALUE +// +// The tokens BLOCK-SEQUENCE-START and BLOCK-MAPPING-START denote indentation +// increase that precedes a block collection (cf. the INDENT token in Python). +// The token BLOCK-END denote indentation decrease that ends a block collection +// (cf. the DEDENT token in Python). However YAML has some syntax pecularities +// that makes detections of these tokens more complex. +// +// The tokens BLOCK-ENTRY, KEY, and VALUE are used to represent the indicators +// '-', '?', and ':' correspondingly. +// +// The following examples show how the tokens BLOCK-SEQUENCE-START, +// BLOCK-MAPPING-START, and BLOCK-END are emitted by the Scanner: +// +// 1. Block sequences: +// +// - item 1 +// - item 2 +// - +// - item 3.1 +// - item 3.2 +// - +// key 1: value 1 +// key 2: value 2 +// +// Tokens: +// +// STREAM-START(utf-8) +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// SCALAR("item 1",plain) +// BLOCK-ENTRY +// SCALAR("item 2",plain) +// BLOCK-ENTRY +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// SCALAR("item 3.1",plain) +// BLOCK-ENTRY +// SCALAR("item 3.2",plain) +// BLOCK-END +// BLOCK-ENTRY +// BLOCK-MAPPING-START +// KEY +// SCALAR("key 1",plain) +// VALUE +// SCALAR("value 1",plain) +// KEY +// SCALAR("key 2",plain) +// VALUE +// SCALAR("value 2",plain) +// BLOCK-END +// BLOCK-END +// STREAM-END +// +// 2. Block mappings: +// +// a simple key: a value # The KEY token is produced here. +// ? a complex key +// : another value +// a mapping: +// key 1: value 1 +// key 2: value 2 +// a sequence: +// - item 1 +// - item 2 +// +// Tokens: +// +// STREAM-START(utf-8) +// BLOCK-MAPPING-START +// KEY +// SCALAR("a simple key",plain) +// VALUE +// SCALAR("a value",plain) +// KEY +// SCALAR("a complex key",plain) +// VALUE +// SCALAR("another value",plain) +// KEY +// SCALAR("a mapping",plain) +// BLOCK-MAPPING-START +// KEY +// SCALAR("key 1",plain) +// VALUE +// SCALAR("value 1",plain) +// KEY +// SCALAR("key 2",plain) +// VALUE +// SCALAR("value 2",plain) +// BLOCK-END +// KEY +// SCALAR("a sequence",plain) +// VALUE +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// SCALAR("item 1",plain) +// BLOCK-ENTRY +// SCALAR("item 2",plain) +// BLOCK-END +// BLOCK-END +// STREAM-END +// +// YAML does not always require to start a new block collection from a new +// line. If the current line contains only '-', '?', and ':' indicators, a new +// block collection may start at the current line. The following examples +// illustrate this case: +// +// 1. Collections in a sequence: +// +// - - item 1 +// - item 2 +// - key 1: value 1 +// key 2: value 2 +// - ? complex key +// : complex value +// +// Tokens: +// +// STREAM-START(utf-8) +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// SCALAR("item 1",plain) +// BLOCK-ENTRY +// SCALAR("item 2",plain) +// BLOCK-END +// BLOCK-ENTRY +// BLOCK-MAPPING-START +// KEY +// SCALAR("key 1",plain) +// VALUE +// SCALAR("value 1",plain) +// KEY +// SCALAR("key 2",plain) +// VALUE +// SCALAR("value 2",plain) +// BLOCK-END +// BLOCK-ENTRY +// BLOCK-MAPPING-START +// KEY +// SCALAR("complex key") +// VALUE +// SCALAR("complex value") +// BLOCK-END +// BLOCK-END +// STREAM-END +// +// 2. Collections in a mapping: +// +// ? a sequence +// : - item 1 +// - item 2 +// ? a mapping +// : key 1: value 1 +// key 2: value 2 +// +// Tokens: +// +// STREAM-START(utf-8) +// BLOCK-MAPPING-START +// KEY +// SCALAR("a sequence",plain) +// VALUE +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// SCALAR("item 1",plain) +// BLOCK-ENTRY +// SCALAR("item 2",plain) +// BLOCK-END +// KEY +// SCALAR("a mapping",plain) +// VALUE +// BLOCK-MAPPING-START +// KEY +// SCALAR("key 1",plain) +// VALUE +// SCALAR("value 1",plain) +// KEY +// SCALAR("key 2",plain) +// VALUE +// SCALAR("value 2",plain) +// BLOCK-END +// BLOCK-END +// STREAM-END +// +// YAML also permits non-indented sequences if they are included into a block +// mapping. In this case, the token BLOCK-SEQUENCE-START is not produced: +// +// key: +// - item 1 # BLOCK-SEQUENCE-START is NOT produced here. +// - item 2 +// +// Tokens: +// +// STREAM-START(utf-8) +// BLOCK-MAPPING-START +// KEY +// SCALAR("key",plain) +// VALUE +// BLOCK-ENTRY +// SCALAR("item 1",plain) +// BLOCK-ENTRY +// SCALAR("item 2",plain) +// BLOCK-END +// + +// Ensure that the buffer contains the required number of characters. +// Return true on success, false on failure (reader error or memory error). +func cache(parser *yaml_parser_t, length int) bool { + // [Go] This was inlined: !cache(A, B) -> unread < B && !update(A, B) + return parser.unread >= length || yaml_parser_update_buffer(parser, length) +} + +// Advance the buffer pointer. +func skip(parser *yaml_parser_t) { + parser.mark.index++ + parser.mark.column++ + parser.unread-- + parser.buffer_pos += width(parser.buffer[parser.buffer_pos]) +} + +func skip_line(parser *yaml_parser_t) { + if is_crlf(parser.buffer, parser.buffer_pos) { + parser.mark.index += 2 + parser.mark.column = 0 + parser.mark.line++ + parser.unread -= 2 + parser.buffer_pos += 2 + } else if is_break(parser.buffer, parser.buffer_pos) { + parser.mark.index++ + parser.mark.column = 0 + parser.mark.line++ + parser.unread-- + parser.buffer_pos += width(parser.buffer[parser.buffer_pos]) + } +} + +// Copy a character to a string buffer and advance pointers. +func read(parser *yaml_parser_t, s []byte) []byte { + w := width(parser.buffer[parser.buffer_pos]) + if w == 0 { + panic("invalid character sequence") + } + if len(s) == 0 { + s = make([]byte, 0, 32) + } + if w == 1 && len(s)+w <= cap(s) { + s = s[:len(s)+1] + s[len(s)-1] = parser.buffer[parser.buffer_pos] + parser.buffer_pos++ + } else { + s = append(s, parser.buffer[parser.buffer_pos:parser.buffer_pos+w]...) + parser.buffer_pos += w + } + parser.mark.index++ + parser.mark.column++ + parser.unread-- + return s +} + +// Copy a line break character to a string buffer and advance pointers. +func read_line(parser *yaml_parser_t, s []byte) []byte { + buf := parser.buffer + pos := parser.buffer_pos + switch { + case buf[pos] == '\r' && buf[pos+1] == '\n': + // CR LF . LF + s = append(s, '\n') + parser.buffer_pos += 2 + parser.mark.index++ + parser.unread-- + case buf[pos] == '\r' || buf[pos] == '\n': + // CR|LF . LF + s = append(s, '\n') + parser.buffer_pos += 1 + case buf[pos] == '\xC2' && buf[pos+1] == '\x85': + // NEL . LF + s = append(s, '\n') + parser.buffer_pos += 2 + case buf[pos] == '\xE2' && buf[pos+1] == '\x80' && (buf[pos+2] == '\xA8' || buf[pos+2] == '\xA9'): + // LS|PS . LS|PS + s = append(s, buf[parser.buffer_pos:pos+3]...) + parser.buffer_pos += 3 + default: + return s + } + parser.mark.index++ + parser.mark.column = 0 + parser.mark.line++ + parser.unread-- + return s +} + +// Get the next token. +func yaml_parser_scan(parser *yaml_parser_t, token *yaml_token_t) bool { + // Erase the token object. + *token = yaml_token_t{} // [Go] Is this necessary? + + // No tokens after STREAM-END or error. + if parser.stream_end_produced || parser.error != yaml_NO_ERROR { + return true + } + + // Ensure that the tokens queue contains enough tokens. + if !parser.token_available { + if !yaml_parser_fetch_more_tokens(parser) { + return false + } + } + + // Fetch the next token from the queue. + *token = parser.tokens[parser.tokens_head] + parser.tokens_head++ + parser.tokens_parsed++ + parser.token_available = false + + if token.typ == yaml_STREAM_END_TOKEN { + parser.stream_end_produced = true + } + return true +} + +// Set the scanner error and return false. +func yaml_parser_set_scanner_error(parser *yaml_parser_t, context string, context_mark yaml_mark_t, problem string) bool { + parser.error = yaml_SCANNER_ERROR + parser.context = context + parser.context_mark = context_mark + parser.problem = problem + parser.problem_mark = parser.mark + return false +} + +func yaml_parser_set_scanner_tag_error(parser *yaml_parser_t, directive bool, context_mark yaml_mark_t, problem string) bool { + context := "while parsing a tag" + if directive { + context = "while parsing a %TAG directive" + } + return yaml_parser_set_scanner_error(parser, context, context_mark, problem) +} + +func trace(args ...interface{}) func() { + pargs := append([]interface{}{"+++"}, args...) + fmt.Println(pargs...) + pargs = append([]interface{}{"---"}, args...) + return func() { fmt.Println(pargs...) } +} + +// Ensure that the tokens queue contains at least one token which can be +// returned to the Parser. +func yaml_parser_fetch_more_tokens(parser *yaml_parser_t) bool { + // While we need more tokens to fetch, do it. + for { + // Check if we really need to fetch more tokens. + need_more_tokens := false + + if parser.tokens_head == len(parser.tokens) { + // Queue is empty. + need_more_tokens = true + } else { + // Check if any potential simple key may occupy the head position. + if !yaml_parser_stale_simple_keys(parser) { + return false + } + + for i := range parser.simple_keys { + simple_key := &parser.simple_keys[i] + if simple_key.possible && simple_key.token_number == parser.tokens_parsed { + need_more_tokens = true + break + } + } + } + + // We are finished. + if !need_more_tokens { + break + } + // Fetch the next token. + if !yaml_parser_fetch_next_token(parser) { + return false + } + } + + parser.token_available = true + return true +} + +// The dispatcher for token fetchers. +func yaml_parser_fetch_next_token(parser *yaml_parser_t) bool { + // Ensure that the buffer is initialized. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + // Check if we just started scanning. Fetch STREAM-START then. + if !parser.stream_start_produced { + return yaml_parser_fetch_stream_start(parser) + } + + // Eat whitespaces and comments until we reach the next token. + if !yaml_parser_scan_to_next_token(parser) { + return false + } + + // Remove obsolete potential simple keys. + if !yaml_parser_stale_simple_keys(parser) { + return false + } + + // Check the indentation level against the current column. + if !yaml_parser_unroll_indent(parser, parser.mark.column) { + return false + } + + // Ensure that the buffer contains at least 4 characters. 4 is the length + // of the longest indicators ('--- ' and '... '). + if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) { + return false + } + + // Is it the end of the stream? + if is_z(parser.buffer, parser.buffer_pos) { + return yaml_parser_fetch_stream_end(parser) + } + + // Is it a directive? + if parser.mark.column == 0 && parser.buffer[parser.buffer_pos] == '%' { + return yaml_parser_fetch_directive(parser) + } + + buf := parser.buffer + pos := parser.buffer_pos + + // Is it the document start indicator? + if parser.mark.column == 0 && buf[pos] == '-' && buf[pos+1] == '-' && buf[pos+2] == '-' && is_blankz(buf, pos+3) { + return yaml_parser_fetch_document_indicator(parser, yaml_DOCUMENT_START_TOKEN) + } + + // Is it the document end indicator? + if parser.mark.column == 0 && buf[pos] == '.' && buf[pos+1] == '.' && buf[pos+2] == '.' && is_blankz(buf, pos+3) { + return yaml_parser_fetch_document_indicator(parser, yaml_DOCUMENT_END_TOKEN) + } + + // Is it the flow sequence start indicator? + if buf[pos] == '[' { + return yaml_parser_fetch_flow_collection_start(parser, yaml_FLOW_SEQUENCE_START_TOKEN) + } + + // Is it the flow mapping start indicator? + if parser.buffer[parser.buffer_pos] == '{' { + return yaml_parser_fetch_flow_collection_start(parser, yaml_FLOW_MAPPING_START_TOKEN) + } + + // Is it the flow sequence end indicator? + if parser.buffer[parser.buffer_pos] == ']' { + return yaml_parser_fetch_flow_collection_end(parser, + yaml_FLOW_SEQUENCE_END_TOKEN) + } + + // Is it the flow mapping end indicator? + if parser.buffer[parser.buffer_pos] == '}' { + return yaml_parser_fetch_flow_collection_end(parser, + yaml_FLOW_MAPPING_END_TOKEN) + } + + // Is it the flow entry indicator? + if parser.buffer[parser.buffer_pos] == ',' { + return yaml_parser_fetch_flow_entry(parser) + } + + // Is it the block entry indicator? + if parser.buffer[parser.buffer_pos] == '-' && is_blankz(parser.buffer, parser.buffer_pos+1) { + return yaml_parser_fetch_block_entry(parser) + } + + // Is it the key indicator? + if parser.buffer[parser.buffer_pos] == '?' && (parser.flow_level > 0 || is_blankz(parser.buffer, parser.buffer_pos+1)) { + return yaml_parser_fetch_key(parser) + } + + // Is it the value indicator? + if parser.buffer[parser.buffer_pos] == ':' && (parser.flow_level > 0 || is_blankz(parser.buffer, parser.buffer_pos+1)) { + return yaml_parser_fetch_value(parser) + } + + // Is it an alias? + if parser.buffer[parser.buffer_pos] == '*' { + return yaml_parser_fetch_anchor(parser, yaml_ALIAS_TOKEN) + } + + // Is it an anchor? + if parser.buffer[parser.buffer_pos] == '&' { + return yaml_parser_fetch_anchor(parser, yaml_ANCHOR_TOKEN) + } + + // Is it a tag? + if parser.buffer[parser.buffer_pos] == '!' { + return yaml_parser_fetch_tag(parser) + } + + // Is it a literal scalar? + if parser.buffer[parser.buffer_pos] == '|' && parser.flow_level == 0 { + return yaml_parser_fetch_block_scalar(parser, true) + } + + // Is it a folded scalar? + if parser.buffer[parser.buffer_pos] == '>' && parser.flow_level == 0 { + return yaml_parser_fetch_block_scalar(parser, false) + } + + // Is it a single-quoted scalar? + if parser.buffer[parser.buffer_pos] == '\'' { + return yaml_parser_fetch_flow_scalar(parser, true) + } + + // Is it a double-quoted scalar? + if parser.buffer[parser.buffer_pos] == '"' { + return yaml_parser_fetch_flow_scalar(parser, false) + } + + // Is it a plain scalar? + // + // A plain scalar may start with any non-blank characters except + // + // '-', '?', ':', ',', '[', ']', '{', '}', + // '#', '&', '*', '!', '|', '>', '\'', '\"', + // '%', '@', '`'. + // + // In the block context (and, for the '-' indicator, in the flow context + // too), it may also start with the characters + // + // '-', '?', ':' + // + // if it is followed by a non-space character. + // + // The last rule is more restrictive than the specification requires. + // [Go] Make this logic more reasonable. + //switch parser.buffer[parser.buffer_pos] { + //case '-', '?', ':', ',', '?', '-', ',', ':', ']', '[', '}', '{', '&', '#', '!', '*', '>', '|', '"', '\'', '@', '%', '-', '`': + //} + if !(is_blankz(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == '-' || + parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == ':' || + parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == '[' || + parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '{' || + parser.buffer[parser.buffer_pos] == '}' || parser.buffer[parser.buffer_pos] == '#' || + parser.buffer[parser.buffer_pos] == '&' || parser.buffer[parser.buffer_pos] == '*' || + parser.buffer[parser.buffer_pos] == '!' || parser.buffer[parser.buffer_pos] == '|' || + parser.buffer[parser.buffer_pos] == '>' || parser.buffer[parser.buffer_pos] == '\'' || + parser.buffer[parser.buffer_pos] == '"' || parser.buffer[parser.buffer_pos] == '%' || + parser.buffer[parser.buffer_pos] == '@' || parser.buffer[parser.buffer_pos] == '`') || + (parser.buffer[parser.buffer_pos] == '-' && !is_blank(parser.buffer, parser.buffer_pos+1)) || + (parser.flow_level == 0 && + (parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == ':') && + !is_blankz(parser.buffer, parser.buffer_pos+1)) { + return yaml_parser_fetch_plain_scalar(parser) + } + + // If we don't determine the token type so far, it is an error. + return yaml_parser_set_scanner_error(parser, + "while scanning for the next token", parser.mark, + "found character that cannot start any token") +} + +// Check the list of potential simple keys and remove the positions that +// cannot contain simple keys anymore. +func yaml_parser_stale_simple_keys(parser *yaml_parser_t) bool { + // Check for a potential simple key for each flow level. + for i := range parser.simple_keys { + simple_key := &parser.simple_keys[i] + + // The specification requires that a simple key + // + // - is limited to a single line, + // - is shorter than 1024 characters. + if simple_key.possible && (simple_key.mark.line < parser.mark.line || simple_key.mark.index+1024 < parser.mark.index) { + + // Check if the potential simple key to be removed is required. + if simple_key.required { + return yaml_parser_set_scanner_error(parser, + "while scanning a simple key", simple_key.mark, + "could not find expected ':'") + } + simple_key.possible = false + } + } + return true +} + +// Check if a simple key may start at the current position and add it if +// needed. +func yaml_parser_save_simple_key(parser *yaml_parser_t) bool { + // A simple key is required at the current position if the scanner is in + // the block context and the current column coincides with the indentation + // level. + + required := parser.flow_level == 0 && parser.indent == parser.mark.column + + // + // If the current position may start a simple key, save it. + // + if parser.simple_key_allowed { + simple_key := yaml_simple_key_t{ + possible: true, + required: required, + token_number: parser.tokens_parsed + (len(parser.tokens) - parser.tokens_head), + } + simple_key.mark = parser.mark + + if !yaml_parser_remove_simple_key(parser) { + return false + } + parser.simple_keys[len(parser.simple_keys)-1] = simple_key + } + return true +} + +// Remove a potential simple key at the current flow level. +func yaml_parser_remove_simple_key(parser *yaml_parser_t) bool { + i := len(parser.simple_keys) - 1 + if parser.simple_keys[i].possible { + // If the key is required, it is an error. + if parser.simple_keys[i].required { + return yaml_parser_set_scanner_error(parser, + "while scanning a simple key", parser.simple_keys[i].mark, + "could not find expected ':'") + } + } + // Remove the key from the stack. + parser.simple_keys[i].possible = false + return true +} + +// Increase the flow level and resize the simple key list if needed. +func yaml_parser_increase_flow_level(parser *yaml_parser_t) bool { + // Reset the simple key on the next level. + parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{}) + + // Increase the flow level. + parser.flow_level++ + return true +} + +// Decrease the flow level. +func yaml_parser_decrease_flow_level(parser *yaml_parser_t) bool { + if parser.flow_level > 0 { + parser.flow_level-- + parser.simple_keys = parser.simple_keys[:len(parser.simple_keys)-1] + } + return true +} + +// Push the current indentation level to the stack and set the new level +// the current column is greater than the indentation level. In this case, +// append or insert the specified token into the token queue. +func yaml_parser_roll_indent(parser *yaml_parser_t, column, number int, typ yaml_token_type_t, mark yaml_mark_t) bool { + // In the flow context, do nothing. + if parser.flow_level > 0 { + return true + } + + if parser.indent < column { + // Push the current indentation level to the stack and set the new + // indentation level. + parser.indents = append(parser.indents, parser.indent) + parser.indent = column + + // Create a token and insert it into the queue. + token := yaml_token_t{ + typ: typ, + start_mark: mark, + end_mark: mark, + } + if number > -1 { + number -= parser.tokens_parsed + } + yaml_insert_token(parser, number, &token) + } + return true +} + +// Pop indentation levels from the indents stack until the current level +// becomes less or equal to the column. For each indentation level, append +// the BLOCK-END token. +func yaml_parser_unroll_indent(parser *yaml_parser_t, column int) bool { + // In the flow context, do nothing. + if parser.flow_level > 0 { + return true + } + + // Loop through the indentation levels in the stack. + for parser.indent > column { + // Create a token and append it to the queue. + token := yaml_token_t{ + typ: yaml_BLOCK_END_TOKEN, + start_mark: parser.mark, + end_mark: parser.mark, + } + yaml_insert_token(parser, -1, &token) + + // Pop the indentation level. + parser.indent = parser.indents[len(parser.indents)-1] + parser.indents = parser.indents[:len(parser.indents)-1] + } + return true +} + +// Initialize the scanner and produce the STREAM-START token. +func yaml_parser_fetch_stream_start(parser *yaml_parser_t) bool { + + // Set the initial indentation. + parser.indent = -1 + + // Initialize the simple key stack. + parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{}) + + // A simple key is allowed at the beginning of the stream. + parser.simple_key_allowed = true + + // We have started. + parser.stream_start_produced = true + + // Create the STREAM-START token and append it to the queue. + token := yaml_token_t{ + typ: yaml_STREAM_START_TOKEN, + start_mark: parser.mark, + end_mark: parser.mark, + encoding: parser.encoding, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the STREAM-END token and shut down the scanner. +func yaml_parser_fetch_stream_end(parser *yaml_parser_t) bool { + + // Force new line. + if parser.mark.column != 0 { + parser.mark.column = 0 + parser.mark.line++ + } + + // Reset the indentation level. + if !yaml_parser_unroll_indent(parser, -1) { + return false + } + + // Reset simple keys. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + parser.simple_key_allowed = false + + // Create the STREAM-END token and append it to the queue. + token := yaml_token_t{ + typ: yaml_STREAM_END_TOKEN, + start_mark: parser.mark, + end_mark: parser.mark, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce a VERSION-DIRECTIVE or TAG-DIRECTIVE token. +func yaml_parser_fetch_directive(parser *yaml_parser_t) bool { + // Reset the indentation level. + if !yaml_parser_unroll_indent(parser, -1) { + return false + } + + // Reset simple keys. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + parser.simple_key_allowed = false + + // Create the YAML-DIRECTIVE or TAG-DIRECTIVE token. + token := yaml_token_t{} + if !yaml_parser_scan_directive(parser, &token) { + return false + } + // Append the token to the queue. + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the DOCUMENT-START or DOCUMENT-END token. +func yaml_parser_fetch_document_indicator(parser *yaml_parser_t, typ yaml_token_type_t) bool { + // Reset the indentation level. + if !yaml_parser_unroll_indent(parser, -1) { + return false + } + + // Reset simple keys. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + parser.simple_key_allowed = false + + // Consume the token. + start_mark := parser.mark + + skip(parser) + skip(parser) + skip(parser) + + end_mark := parser.mark + + // Create the DOCUMENT-START or DOCUMENT-END token. + token := yaml_token_t{ + typ: typ, + start_mark: start_mark, + end_mark: end_mark, + } + // Append the token to the queue. + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the FLOW-SEQUENCE-START or FLOW-MAPPING-START token. +func yaml_parser_fetch_flow_collection_start(parser *yaml_parser_t, typ yaml_token_type_t) bool { + // The indicators '[' and '{' may start a simple key. + if !yaml_parser_save_simple_key(parser) { + return false + } + + // Increase the flow level. + if !yaml_parser_increase_flow_level(parser) { + return false + } + + // A simple key may follow the indicators '[' and '{'. + parser.simple_key_allowed = true + + // Consume the token. + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the FLOW-SEQUENCE-START of FLOW-MAPPING-START token. + token := yaml_token_t{ + typ: typ, + start_mark: start_mark, + end_mark: end_mark, + } + // Append the token to the queue. + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the FLOW-SEQUENCE-END or FLOW-MAPPING-END token. +func yaml_parser_fetch_flow_collection_end(parser *yaml_parser_t, typ yaml_token_type_t) bool { + // Reset any potential simple key on the current flow level. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + // Decrease the flow level. + if !yaml_parser_decrease_flow_level(parser) { + return false + } + + // No simple keys after the indicators ']' and '}'. + parser.simple_key_allowed = false + + // Consume the token. + + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the FLOW-SEQUENCE-END of FLOW-MAPPING-END token. + token := yaml_token_t{ + typ: typ, + start_mark: start_mark, + end_mark: end_mark, + } + // Append the token to the queue. + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the FLOW-ENTRY token. +func yaml_parser_fetch_flow_entry(parser *yaml_parser_t) bool { + // Reset any potential simple keys on the current flow level. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + // Simple keys are allowed after ','. + parser.simple_key_allowed = true + + // Consume the token. + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the FLOW-ENTRY token and append it to the queue. + token := yaml_token_t{ + typ: yaml_FLOW_ENTRY_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the BLOCK-ENTRY token. +func yaml_parser_fetch_block_entry(parser *yaml_parser_t) bool { + // Check if the scanner is in the block context. + if parser.flow_level == 0 { + // Check if we are allowed to start a new entry. + if !parser.simple_key_allowed { + return yaml_parser_set_scanner_error(parser, "", parser.mark, + "block sequence entries are not allowed in this context") + } + // Add the BLOCK-SEQUENCE-START token if needed. + if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_SEQUENCE_START_TOKEN, parser.mark) { + return false + } + } else { + // It is an error for the '-' indicator to occur in the flow context, + // but we let the Parser detect and report about it because the Parser + // is able to point to the context. + } + + // Reset any potential simple keys on the current flow level. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + // Simple keys are allowed after '-'. + parser.simple_key_allowed = true + + // Consume the token. + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the BLOCK-ENTRY token and append it to the queue. + token := yaml_token_t{ + typ: yaml_BLOCK_ENTRY_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the KEY token. +func yaml_parser_fetch_key(parser *yaml_parser_t) bool { + + // In the block context, additional checks are required. + if parser.flow_level == 0 { + // Check if we are allowed to start a new key (not nessesary simple). + if !parser.simple_key_allowed { + return yaml_parser_set_scanner_error(parser, "", parser.mark, + "mapping keys are not allowed in this context") + } + // Add the BLOCK-MAPPING-START token if needed. + if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_MAPPING_START_TOKEN, parser.mark) { + return false + } + } + + // Reset any potential simple keys on the current flow level. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + // Simple keys are allowed after '?' in the block context. + parser.simple_key_allowed = parser.flow_level == 0 + + // Consume the token. + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the KEY token and append it to the queue. + token := yaml_token_t{ + typ: yaml_KEY_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the VALUE token. +func yaml_parser_fetch_value(parser *yaml_parser_t) bool { + + simple_key := &parser.simple_keys[len(parser.simple_keys)-1] + + // Have we found a simple key? + if simple_key.possible { + // Create the KEY token and insert it into the queue. + token := yaml_token_t{ + typ: yaml_KEY_TOKEN, + start_mark: simple_key.mark, + end_mark: simple_key.mark, + } + yaml_insert_token(parser, simple_key.token_number-parser.tokens_parsed, &token) + + // In the block context, we may need to add the BLOCK-MAPPING-START token. + if !yaml_parser_roll_indent(parser, simple_key.mark.column, + simple_key.token_number, + yaml_BLOCK_MAPPING_START_TOKEN, simple_key.mark) { + return false + } + + // Remove the simple key. + simple_key.possible = false + + // A simple key cannot follow another simple key. + parser.simple_key_allowed = false + + } else { + // The ':' indicator follows a complex key. + + // In the block context, extra checks are required. + if parser.flow_level == 0 { + + // Check if we are allowed to start a complex value. + if !parser.simple_key_allowed { + return yaml_parser_set_scanner_error(parser, "", parser.mark, + "mapping values are not allowed in this context") + } + + // Add the BLOCK-MAPPING-START token if needed. + if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_MAPPING_START_TOKEN, parser.mark) { + return false + } + } + + // Simple keys after ':' are allowed in the block context. + parser.simple_key_allowed = parser.flow_level == 0 + } + + // Consume the token. + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the VALUE token and append it to the queue. + token := yaml_token_t{ + typ: yaml_VALUE_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the ALIAS or ANCHOR token. +func yaml_parser_fetch_anchor(parser *yaml_parser_t, typ yaml_token_type_t) bool { + // An anchor or an alias could be a simple key. + if !yaml_parser_save_simple_key(parser) { + return false + } + + // A simple key cannot follow an anchor or an alias. + parser.simple_key_allowed = false + + // Create the ALIAS or ANCHOR token and append it to the queue. + var token yaml_token_t + if !yaml_parser_scan_anchor(parser, &token, typ) { + return false + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the TAG token. +func yaml_parser_fetch_tag(parser *yaml_parser_t) bool { + // A tag could be a simple key. + if !yaml_parser_save_simple_key(parser) { + return false + } + + // A simple key cannot follow a tag. + parser.simple_key_allowed = false + + // Create the TAG token and append it to the queue. + var token yaml_token_t + if !yaml_parser_scan_tag(parser, &token) { + return false + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the SCALAR(...,literal) or SCALAR(...,folded) tokens. +func yaml_parser_fetch_block_scalar(parser *yaml_parser_t, literal bool) bool { + // Remove any potential simple keys. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + // A simple key may follow a block scalar. + parser.simple_key_allowed = true + + // Create the SCALAR token and append it to the queue. + var token yaml_token_t + if !yaml_parser_scan_block_scalar(parser, &token, literal) { + return false + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the SCALAR(...,single-quoted) or SCALAR(...,double-quoted) tokens. +func yaml_parser_fetch_flow_scalar(parser *yaml_parser_t, single bool) bool { + // A plain scalar could be a simple key. + if !yaml_parser_save_simple_key(parser) { + return false + } + + // A simple key cannot follow a flow scalar. + parser.simple_key_allowed = false + + // Create the SCALAR token and append it to the queue. + var token yaml_token_t + if !yaml_parser_scan_flow_scalar(parser, &token, single) { + return false + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the SCALAR(...,plain) token. +func yaml_parser_fetch_plain_scalar(parser *yaml_parser_t) bool { + // A plain scalar could be a simple key. + if !yaml_parser_save_simple_key(parser) { + return false + } + + // A simple key cannot follow a flow scalar. + parser.simple_key_allowed = false + + // Create the SCALAR token and append it to the queue. + var token yaml_token_t + if !yaml_parser_scan_plain_scalar(parser, &token) { + return false + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Eat whitespaces and comments until the next token is found. +func yaml_parser_scan_to_next_token(parser *yaml_parser_t) bool { + + // Until the next token is not found. + for { + // Allow the BOM mark to start a line. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if parser.mark.column == 0 && is_bom(parser.buffer, parser.buffer_pos) { + skip(parser) + } + + // Eat whitespaces. + // Tabs are allowed: + // - in the flow context + // - in the block context, but not at the beginning of the line or + // after '-', '?', or ':' (complex value). + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + for parser.buffer[parser.buffer_pos] == ' ' || ((parser.flow_level > 0 || !parser.simple_key_allowed) && parser.buffer[parser.buffer_pos] == '\t') { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Eat a comment until a line break. + if parser.buffer[parser.buffer_pos] == '#' { + for !is_breakz(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + } + + // If it is a line break, eat it. + if is_break(parser.buffer, parser.buffer_pos) { + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + skip_line(parser) + + // In the block context, a new line may start a simple key. + if parser.flow_level == 0 { + parser.simple_key_allowed = true + } + } else { + break // We have found a token. + } + } + + return true +} + +// Scan a YAML-DIRECTIVE or TAG-DIRECTIVE token. +// +// Scope: +// %YAML 1.1 # a comment \n +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// %TAG !yaml! tag:yaml.org,2002: \n +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// +func yaml_parser_scan_directive(parser *yaml_parser_t, token *yaml_token_t) bool { + // Eat '%'. + start_mark := parser.mark + skip(parser) + + // Scan the directive name. + var name []byte + if !yaml_parser_scan_directive_name(parser, start_mark, &name) { + return false + } + + // Is it a YAML directive? + if bytes.Equal(name, []byte("YAML")) { + // Scan the VERSION directive value. + var major, minor int8 + if !yaml_parser_scan_version_directive_value(parser, start_mark, &major, &minor) { + return false + } + end_mark := parser.mark + + // Create a VERSION-DIRECTIVE token. + *token = yaml_token_t{ + typ: yaml_VERSION_DIRECTIVE_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + major: major, + minor: minor, + } + + // Is it a TAG directive? + } else if bytes.Equal(name, []byte("TAG")) { + // Scan the TAG directive value. + var handle, prefix []byte + if !yaml_parser_scan_tag_directive_value(parser, start_mark, &handle, &prefix) { + return false + } + end_mark := parser.mark + + // Create a TAG-DIRECTIVE token. + *token = yaml_token_t{ + typ: yaml_TAG_DIRECTIVE_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + value: handle, + prefix: prefix, + } + + // Unknown directive. + } else { + yaml_parser_set_scanner_error(parser, "while scanning a directive", + start_mark, "found unknown directive name") + return false + } + + // Eat the rest of the line including any comments. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + for is_blank(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + if parser.buffer[parser.buffer_pos] == '#' { + for !is_breakz(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + } + + // Check if we are at the end of the line. + if !is_breakz(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a directive", + start_mark, "did not find expected comment or line break") + return false + } + + // Eat a line break. + if is_break(parser.buffer, parser.buffer_pos) { + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + skip_line(parser) + } + + return true +} + +// Scan the directive name. +// +// Scope: +// %YAML 1.1 # a comment \n +// ^^^^ +// %TAG !yaml! tag:yaml.org,2002: \n +// ^^^ +// +func yaml_parser_scan_directive_name(parser *yaml_parser_t, start_mark yaml_mark_t, name *[]byte) bool { + // Consume the directive name. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + var s []byte + for is_alpha(parser.buffer, parser.buffer_pos) { + s = read(parser, s) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Check if the name is empty. + if len(s) == 0 { + yaml_parser_set_scanner_error(parser, "while scanning a directive", + start_mark, "could not find expected directive name") + return false + } + + // Check for an blank character after the name. + if !is_blankz(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a directive", + start_mark, "found unexpected non-alphabetical character") + return false + } + *name = s + return true +} + +// Scan the value of VERSION-DIRECTIVE. +// +// Scope: +// %YAML 1.1 # a comment \n +// ^^^^^^ +func yaml_parser_scan_version_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, major, minor *int8) bool { + // Eat whitespaces. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + for is_blank(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Consume the major version number. + if !yaml_parser_scan_version_directive_number(parser, start_mark, major) { + return false + } + + // Eat '.'. + if parser.buffer[parser.buffer_pos] != '.' { + return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive", + start_mark, "did not find expected digit or '.' character") + } + + skip(parser) + + // Consume the minor version number. + if !yaml_parser_scan_version_directive_number(parser, start_mark, minor) { + return false + } + return true +} + +const max_number_length = 2 + +// Scan the version number of VERSION-DIRECTIVE. +// +// Scope: +// %YAML 1.1 # a comment \n +// ^ +// %YAML 1.1 # a comment \n +// ^ +func yaml_parser_scan_version_directive_number(parser *yaml_parser_t, start_mark yaml_mark_t, number *int8) bool { + + // Repeat while the next character is digit. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + var value, length int8 + for is_digit(parser.buffer, parser.buffer_pos) { + // Check if the number is too long. + length++ + if length > max_number_length { + return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive", + start_mark, "found extremely long version number") + } + value = value*10 + int8(as_digit(parser.buffer, parser.buffer_pos)) + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Check if the number was present. + if length == 0 { + return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive", + start_mark, "did not find expected version number") + } + *number = value + return true +} + +// Scan the value of a TAG-DIRECTIVE token. +// +// Scope: +// %TAG !yaml! tag:yaml.org,2002: \n +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// +func yaml_parser_scan_tag_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, handle, prefix *[]byte) bool { + var handle_value, prefix_value []byte + + // Eat whitespaces. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + for is_blank(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Scan a handle. + if !yaml_parser_scan_tag_handle(parser, true, start_mark, &handle_value) { + return false + } + + // Expect a whitespace. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if !is_blank(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a %TAG directive", + start_mark, "did not find expected whitespace") + return false + } + + // Eat whitespaces. + for is_blank(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Scan a prefix. + if !yaml_parser_scan_tag_uri(parser, true, nil, start_mark, &prefix_value) { + return false + } + + // Expect a whitespace or line break. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if !is_blankz(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a %TAG directive", + start_mark, "did not find expected whitespace or line break") + return false + } + + *handle = handle_value + *prefix = prefix_value + return true +} + +func yaml_parser_scan_anchor(parser *yaml_parser_t, token *yaml_token_t, typ yaml_token_type_t) bool { + var s []byte + + // Eat the indicator character. + start_mark := parser.mark + skip(parser) + + // Consume the value. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + for is_alpha(parser.buffer, parser.buffer_pos) { + s = read(parser, s) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + end_mark := parser.mark + + /* + * Check if length of the anchor is greater than 0 and it is followed by + * a whitespace character or one of the indicators: + * + * '?', ':', ',', ']', '}', '%', '@', '`'. + */ + + if len(s) == 0 || + !(is_blankz(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == '?' || + parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == ',' || + parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '}' || + parser.buffer[parser.buffer_pos] == '%' || parser.buffer[parser.buffer_pos] == '@' || + parser.buffer[parser.buffer_pos] == '`') { + context := "while scanning an alias" + if typ == yaml_ANCHOR_TOKEN { + context = "while scanning an anchor" + } + yaml_parser_set_scanner_error(parser, context, start_mark, + "did not find expected alphabetic or numeric character") + return false + } + + // Create a token. + *token = yaml_token_t{ + typ: typ, + start_mark: start_mark, + end_mark: end_mark, + value: s, + } + + return true +} + +/* + * Scan a TAG token. + */ + +func yaml_parser_scan_tag(parser *yaml_parser_t, token *yaml_token_t) bool { + var handle, suffix []byte + + start_mark := parser.mark + + // Check if the tag is in the canonical form. + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + + if parser.buffer[parser.buffer_pos+1] == '<' { + // Keep the handle as '' + + // Eat '!<' + skip(parser) + skip(parser) + + // Consume the tag value. + if !yaml_parser_scan_tag_uri(parser, false, nil, start_mark, &suffix) { + return false + } + + // Check for '>' and eat it. + if parser.buffer[parser.buffer_pos] != '>' { + yaml_parser_set_scanner_error(parser, "while scanning a tag", + start_mark, "did not find the expected '>'") + return false + } + + skip(parser) + } else { + // The tag has either the '!suffix' or the '!handle!suffix' form. + + // First, try to scan a handle. + if !yaml_parser_scan_tag_handle(parser, false, start_mark, &handle) { + return false + } + + // Check if it is, indeed, handle. + if handle[0] == '!' && len(handle) > 1 && handle[len(handle)-1] == '!' { + // Scan the suffix now. + if !yaml_parser_scan_tag_uri(parser, false, nil, start_mark, &suffix) { + return false + } + } else { + // It wasn't a handle after all. Scan the rest of the tag. + if !yaml_parser_scan_tag_uri(parser, false, handle, start_mark, &suffix) { + return false + } + + // Set the handle to '!'. + handle = []byte{'!'} + + // A special case: the '!' tag. Set the handle to '' and the + // suffix to '!'. + if len(suffix) == 0 { + handle, suffix = suffix, handle + } + } + } + + // Check the character which ends the tag. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if !is_blankz(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a tag", + start_mark, "did not find expected whitespace or line break") + return false + } + + end_mark := parser.mark + + // Create a token. + *token = yaml_token_t{ + typ: yaml_TAG_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + value: handle, + suffix: suffix, + } + return true +} + +// Scan a tag handle. +func yaml_parser_scan_tag_handle(parser *yaml_parser_t, directive bool, start_mark yaml_mark_t, handle *[]byte) bool { + // Check the initial '!' character. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if parser.buffer[parser.buffer_pos] != '!' { + yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "did not find expected '!'") + return false + } + + var s []byte + + // Copy the '!' character. + s = read(parser, s) + + // Copy all subsequent alphabetical and numerical characters. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + for is_alpha(parser.buffer, parser.buffer_pos) { + s = read(parser, s) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Check if the trailing character is '!' and copy it. + if parser.buffer[parser.buffer_pos] == '!' { + s = read(parser, s) + } else { + // It's either the '!' tag or not really a tag handle. If it's a %TAG + // directive, it's an error. If it's a tag token, it must be a part of URI. + if directive && string(s) != "!" { + yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "did not find expected '!'") + return false + } + } + + *handle = s + return true +} + +// Scan a tag. +func yaml_parser_scan_tag_uri(parser *yaml_parser_t, directive bool, head []byte, start_mark yaml_mark_t, uri *[]byte) bool { + //size_t length = head ? strlen((char *)head) : 0 + var s []byte + hasTag := len(head) > 0 + + // Copy the head if needed. + // + // Note that we don't copy the leading '!' character. + if len(head) > 1 { + s = append(s, head[1:]...) + } + + // Scan the tag. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + // The set of characters that may appear in URI is as follows: + // + // '0'-'9', 'A'-'Z', 'a'-'z', '_', '-', ';', '/', '?', ':', '@', '&', + // '=', '+', '$', ',', '.', '!', '~', '*', '\'', '(', ')', '[', ']', + // '%'. + // [Go] Convert this into more reasonable logic. + for is_alpha(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == ';' || + parser.buffer[parser.buffer_pos] == '/' || parser.buffer[parser.buffer_pos] == '?' || + parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == '@' || + parser.buffer[parser.buffer_pos] == '&' || parser.buffer[parser.buffer_pos] == '=' || + parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '$' || + parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == '.' || + parser.buffer[parser.buffer_pos] == '!' || parser.buffer[parser.buffer_pos] == '~' || + parser.buffer[parser.buffer_pos] == '*' || parser.buffer[parser.buffer_pos] == '\'' || + parser.buffer[parser.buffer_pos] == '(' || parser.buffer[parser.buffer_pos] == ')' || + parser.buffer[parser.buffer_pos] == '[' || parser.buffer[parser.buffer_pos] == ']' || + parser.buffer[parser.buffer_pos] == '%' { + // Check if it is a URI-escape sequence. + if parser.buffer[parser.buffer_pos] == '%' { + if !yaml_parser_scan_uri_escapes(parser, directive, start_mark, &s) { + return false + } + } else { + s = read(parser, s) + } + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + hasTag = true + } + + if !hasTag { + yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "did not find expected tag URI") + return false + } + *uri = s + return true +} + +// Decode an URI-escape sequence corresponding to a single UTF-8 character. +func yaml_parser_scan_uri_escapes(parser *yaml_parser_t, directive bool, start_mark yaml_mark_t, s *[]byte) bool { + + // Decode the required number of characters. + w := 1024 + for w > 0 { + // Check for a URI-escaped octet. + if parser.unread < 3 && !yaml_parser_update_buffer(parser, 3) { + return false + } + + if !(parser.buffer[parser.buffer_pos] == '%' && + is_hex(parser.buffer, parser.buffer_pos+1) && + is_hex(parser.buffer, parser.buffer_pos+2)) { + return yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "did not find URI escaped octet") + } + + // Get the octet. + octet := byte((as_hex(parser.buffer, parser.buffer_pos+1) << 4) + as_hex(parser.buffer, parser.buffer_pos+2)) + + // If it is the leading octet, determine the length of the UTF-8 sequence. + if w == 1024 { + w = width(octet) + if w == 0 { + return yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "found an incorrect leading UTF-8 octet") + } + } else { + // Check if the trailing octet is correct. + if octet&0xC0 != 0x80 { + return yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "found an incorrect trailing UTF-8 octet") + } + } + + // Copy the octet and move the pointers. + *s = append(*s, octet) + skip(parser) + skip(parser) + skip(parser) + w-- + } + return true +} + +// Scan a block scalar. +func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, literal bool) bool { + // Eat the indicator '|' or '>'. + start_mark := parser.mark + skip(parser) + + // Scan the additional block scalar indicators. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + // Check for a chomping indicator. + var chomping, increment int + if parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '-' { + // Set the chomping method and eat the indicator. + if parser.buffer[parser.buffer_pos] == '+' { + chomping = +1 + } else { + chomping = -1 + } + skip(parser) + + // Check for an indentation indicator. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if is_digit(parser.buffer, parser.buffer_pos) { + // Check that the indentation is greater than 0. + if parser.buffer[parser.buffer_pos] == '0' { + yaml_parser_set_scanner_error(parser, "while scanning a block scalar", + start_mark, "found an indentation indicator equal to 0") + return false + } + + // Get the indentation level and eat the indicator. + increment = as_digit(parser.buffer, parser.buffer_pos) + skip(parser) + } + + } else if is_digit(parser.buffer, parser.buffer_pos) { + // Do the same as above, but in the opposite order. + + if parser.buffer[parser.buffer_pos] == '0' { + yaml_parser_set_scanner_error(parser, "while scanning a block scalar", + start_mark, "found an indentation indicator equal to 0") + return false + } + increment = as_digit(parser.buffer, parser.buffer_pos) + skip(parser) + + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '-' { + if parser.buffer[parser.buffer_pos] == '+' { + chomping = +1 + } else { + chomping = -1 + } + skip(parser) + } + } + + // Eat whitespaces and comments to the end of the line. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + for is_blank(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + if parser.buffer[parser.buffer_pos] == '#' { + for !is_breakz(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + } + + // Check if we are at the end of the line. + if !is_breakz(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a block scalar", + start_mark, "did not find expected comment or line break") + return false + } + + // Eat a line break. + if is_break(parser.buffer, parser.buffer_pos) { + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + skip_line(parser) + } + + end_mark := parser.mark + + // Set the indentation level if it was specified. + var indent int + if increment > 0 { + if parser.indent >= 0 { + indent = parser.indent + increment + } else { + indent = increment + } + } + + // Scan the leading line breaks and determine the indentation level if needed. + var s, leading_break, trailing_breaks []byte + if !yaml_parser_scan_block_scalar_breaks(parser, &indent, &trailing_breaks, start_mark, &end_mark) { + return false + } + + // Scan the block scalar content. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + var leading_blank, trailing_blank bool + for parser.mark.column == indent && !is_z(parser.buffer, parser.buffer_pos) { + // We are at the beginning of a non-empty line. + + // Is it a trailing whitespace? + trailing_blank = is_blank(parser.buffer, parser.buffer_pos) + + // Check if we need to fold the leading line break. + if !literal && !leading_blank && !trailing_blank && len(leading_break) > 0 && leading_break[0] == '\n' { + // Do we need to join the lines by space? + if len(trailing_breaks) == 0 { + s = append(s, ' ') + } + } else { + s = append(s, leading_break...) + } + leading_break = leading_break[:0] + + // Append the remaining line breaks. + s = append(s, trailing_breaks...) + trailing_breaks = trailing_breaks[:0] + + // Is it a leading whitespace? + leading_blank = is_blank(parser.buffer, parser.buffer_pos) + + // Consume the current line. + for !is_breakz(parser.buffer, parser.buffer_pos) { + s = read(parser, s) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Consume the line break. + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + + leading_break = read_line(parser, leading_break) + + // Eat the following indentation spaces and line breaks. + if !yaml_parser_scan_block_scalar_breaks(parser, &indent, &trailing_breaks, start_mark, &end_mark) { + return false + } + } + + // Chomp the tail. + if chomping != -1 { + s = append(s, leading_break...) + } + if chomping == 1 { + s = append(s, trailing_breaks...) + } + + // Create a token. + *token = yaml_token_t{ + typ: yaml_SCALAR_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + value: s, + style: yaml_LITERAL_SCALAR_STYLE, + } + if !literal { + token.style = yaml_FOLDED_SCALAR_STYLE + } + return true +} + +// Scan indentation spaces and line breaks for a block scalar. Determine the +// indentation level if needed. +func yaml_parser_scan_block_scalar_breaks(parser *yaml_parser_t, indent *int, breaks *[]byte, start_mark yaml_mark_t, end_mark *yaml_mark_t) bool { + *end_mark = parser.mark + + // Eat the indentation spaces and line breaks. + max_indent := 0 + for { + // Eat the indentation spaces. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + for (*indent == 0 || parser.mark.column < *indent) && is_space(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + if parser.mark.column > max_indent { + max_indent = parser.mark.column + } + + // Check for a tab character messing the indentation. + if (*indent == 0 || parser.mark.column < *indent) && is_tab(parser.buffer, parser.buffer_pos) { + return yaml_parser_set_scanner_error(parser, "while scanning a block scalar", + start_mark, "found a tab character where an indentation space is expected") + } + + // Have we found a non-empty line? + if !is_break(parser.buffer, parser.buffer_pos) { + break + } + + // Consume the line break. + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + // [Go] Should really be returning breaks instead. + *breaks = read_line(parser, *breaks) + *end_mark = parser.mark + } + + // Determine the indentation level if needed. + if *indent == 0 { + *indent = max_indent + if *indent < parser.indent+1 { + *indent = parser.indent + 1 + } + if *indent < 1 { + *indent = 1 + } + } + return true +} + +// Scan a quoted scalar. +func yaml_parser_scan_flow_scalar(parser *yaml_parser_t, token *yaml_token_t, single bool) bool { + // Eat the left quote. + start_mark := parser.mark + skip(parser) + + // Consume the content of the quoted scalar. + var s, leading_break, trailing_breaks, whitespaces []byte + for { + // Check that there are no document indicators at the beginning of the line. + if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) { + return false + } + + if parser.mark.column == 0 && + ((parser.buffer[parser.buffer_pos+0] == '-' && + parser.buffer[parser.buffer_pos+1] == '-' && + parser.buffer[parser.buffer_pos+2] == '-') || + (parser.buffer[parser.buffer_pos+0] == '.' && + parser.buffer[parser.buffer_pos+1] == '.' && + parser.buffer[parser.buffer_pos+2] == '.')) && + is_blankz(parser.buffer, parser.buffer_pos+3) { + yaml_parser_set_scanner_error(parser, "while scanning a quoted scalar", + start_mark, "found unexpected document indicator") + return false + } + + // Check for EOF. + if is_z(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a quoted scalar", + start_mark, "found unexpected end of stream") + return false + } + + // Consume non-blank characters. + leading_blanks := false + for !is_blankz(parser.buffer, parser.buffer_pos) { + if single && parser.buffer[parser.buffer_pos] == '\'' && parser.buffer[parser.buffer_pos+1] == '\'' { + // Is is an escaped single quote. + s = append(s, '\'') + skip(parser) + skip(parser) + + } else if single && parser.buffer[parser.buffer_pos] == '\'' { + // It is a right single quote. + break + } else if !single && parser.buffer[parser.buffer_pos] == '"' { + // It is a right double quote. + break + + } else if !single && parser.buffer[parser.buffer_pos] == '\\' && is_break(parser.buffer, parser.buffer_pos+1) { + // It is an escaped line break. + if parser.unread < 3 && !yaml_parser_update_buffer(parser, 3) { + return false + } + skip(parser) + skip_line(parser) + leading_blanks = true + break + + } else if !single && parser.buffer[parser.buffer_pos] == '\\' { + // It is an escape sequence. + code_length := 0 + + // Check the escape character. + switch parser.buffer[parser.buffer_pos+1] { + case '0': + s = append(s, 0) + case 'a': + s = append(s, '\x07') + case 'b': + s = append(s, '\x08') + case 't', '\t': + s = append(s, '\x09') + case 'n': + s = append(s, '\x0A') + case 'v': + s = append(s, '\x0B') + case 'f': + s = append(s, '\x0C') + case 'r': + s = append(s, '\x0D') + case 'e': + s = append(s, '\x1B') + case ' ': + s = append(s, '\x20') + case '"': + s = append(s, '"') + case '\'': + s = append(s, '\'') + case '\\': + s = append(s, '\\') + case 'N': // NEL (#x85) + s = append(s, '\xC2') + s = append(s, '\x85') + case '_': // #xA0 + s = append(s, '\xC2') + s = append(s, '\xA0') + case 'L': // LS (#x2028) + s = append(s, '\xE2') + s = append(s, '\x80') + s = append(s, '\xA8') + case 'P': // PS (#x2029) + s = append(s, '\xE2') + s = append(s, '\x80') + s = append(s, '\xA9') + case 'x': + code_length = 2 + case 'u': + code_length = 4 + case 'U': + code_length = 8 + default: + yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar", + start_mark, "found unknown escape character") + return false + } + + skip(parser) + skip(parser) + + // Consume an arbitrary escape code. + if code_length > 0 { + var value int + + // Scan the character value. + if parser.unread < code_length && !yaml_parser_update_buffer(parser, code_length) { + return false + } + for k := 0; k < code_length; k++ { + if !is_hex(parser.buffer, parser.buffer_pos+k) { + yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar", + start_mark, "did not find expected hexdecimal number") + return false + } + value = (value << 4) + as_hex(parser.buffer, parser.buffer_pos+k) + } + + // Check the value and write the character. + if (value >= 0xD800 && value <= 0xDFFF) || value > 0x10FFFF { + yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar", + start_mark, "found invalid Unicode character escape code") + return false + } + if value <= 0x7F { + s = append(s, byte(value)) + } else if value <= 0x7FF { + s = append(s, byte(0xC0+(value>>6))) + s = append(s, byte(0x80+(value&0x3F))) + } else if value <= 0xFFFF { + s = append(s, byte(0xE0+(value>>12))) + s = append(s, byte(0x80+((value>>6)&0x3F))) + s = append(s, byte(0x80+(value&0x3F))) + } else { + s = append(s, byte(0xF0+(value>>18))) + s = append(s, byte(0x80+((value>>12)&0x3F))) + s = append(s, byte(0x80+((value>>6)&0x3F))) + s = append(s, byte(0x80+(value&0x3F))) + } + + // Advance the pointer. + for k := 0; k < code_length; k++ { + skip(parser) + } + } + } else { + // It is a non-escaped non-blank character. + s = read(parser, s) + } + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + } + + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + // Check if we are at the end of the scalar. + if single { + if parser.buffer[parser.buffer_pos] == '\'' { + break + } + } else { + if parser.buffer[parser.buffer_pos] == '"' { + break + } + } + + // Consume blank characters. + for is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos) { + if is_blank(parser.buffer, parser.buffer_pos) { + // Consume a space or a tab character. + if !leading_blanks { + whitespaces = read(parser, whitespaces) + } else { + skip(parser) + } + } else { + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + + // Check if it is a first line break. + if !leading_blanks { + whitespaces = whitespaces[:0] + leading_break = read_line(parser, leading_break) + leading_blanks = true + } else { + trailing_breaks = read_line(parser, trailing_breaks) + } + } + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Join the whitespaces or fold line breaks. + if leading_blanks { + // Do we need to fold line breaks? + if len(leading_break) > 0 && leading_break[0] == '\n' { + if len(trailing_breaks) == 0 { + s = append(s, ' ') + } else { + s = append(s, trailing_breaks...) + } + } else { + s = append(s, leading_break...) + s = append(s, trailing_breaks...) + } + trailing_breaks = trailing_breaks[:0] + leading_break = leading_break[:0] + } else { + s = append(s, whitespaces...) + whitespaces = whitespaces[:0] + } + } + + // Eat the right quote. + skip(parser) + end_mark := parser.mark + + // Create a token. + *token = yaml_token_t{ + typ: yaml_SCALAR_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + value: s, + style: yaml_SINGLE_QUOTED_SCALAR_STYLE, + } + if !single { + token.style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + return true +} + +// Scan a plain scalar. +func yaml_parser_scan_plain_scalar(parser *yaml_parser_t, token *yaml_token_t) bool { + + var s, leading_break, trailing_breaks, whitespaces []byte + var leading_blanks bool + var indent = parser.indent + 1 + + start_mark := parser.mark + end_mark := parser.mark + + // Consume the content of the plain scalar. + for { + // Check for a document indicator. + if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) { + return false + } + if parser.mark.column == 0 && + ((parser.buffer[parser.buffer_pos+0] == '-' && + parser.buffer[parser.buffer_pos+1] == '-' && + parser.buffer[parser.buffer_pos+2] == '-') || + (parser.buffer[parser.buffer_pos+0] == '.' && + parser.buffer[parser.buffer_pos+1] == '.' && + parser.buffer[parser.buffer_pos+2] == '.')) && + is_blankz(parser.buffer, parser.buffer_pos+3) { + break + } + + // Check for a comment. + if parser.buffer[parser.buffer_pos] == '#' { + break + } + + // Consume non-blank characters. + for !is_blankz(parser.buffer, parser.buffer_pos) { + + // Check for indicators that may end a plain scalar. + if (parser.buffer[parser.buffer_pos] == ':' && is_blankz(parser.buffer, parser.buffer_pos+1)) || + (parser.flow_level > 0 && + (parser.buffer[parser.buffer_pos] == ',' || + parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == '[' || + parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '{' || + parser.buffer[parser.buffer_pos] == '}')) { + break + } + + // Check if we need to join whitespaces and breaks. + if leading_blanks || len(whitespaces) > 0 { + if leading_blanks { + // Do we need to fold line breaks? + if leading_break[0] == '\n' { + if len(trailing_breaks) == 0 { + s = append(s, ' ') + } else { + s = append(s, trailing_breaks...) + } + } else { + s = append(s, leading_break...) + s = append(s, trailing_breaks...) + } + trailing_breaks = trailing_breaks[:0] + leading_break = leading_break[:0] + leading_blanks = false + } else { + s = append(s, whitespaces...) + whitespaces = whitespaces[:0] + } + } + + // Copy the character. + s = read(parser, s) + + end_mark = parser.mark + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + } + + // Is it the end? + if !(is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos)) { + break + } + + // Consume blank characters. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + for is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos) { + if is_blank(parser.buffer, parser.buffer_pos) { + + // Check for tab characters that abuse indentation. + if leading_blanks && parser.mark.column < indent && is_tab(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a plain scalar", + start_mark, "found a tab character that violates indentation") + return false + } + + // Consume a space or a tab character. + if !leading_blanks { + whitespaces = read(parser, whitespaces) + } else { + skip(parser) + } + } else { + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + + // Check if it is a first line break. + if !leading_blanks { + whitespaces = whitespaces[:0] + leading_break = read_line(parser, leading_break) + leading_blanks = true + } else { + trailing_breaks = read_line(parser, trailing_breaks) + } + } + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Check indentation level. + if parser.flow_level == 0 && parser.mark.column < indent { + break + } + } + + // Create a token. + *token = yaml_token_t{ + typ: yaml_SCALAR_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + value: s, + style: yaml_PLAIN_SCALAR_STYLE, + } + + // Note that we change the 'simple_key_allowed' flag. + if leading_blanks { + parser.simple_key_allowed = true + } + return true +} diff --git a/vendor/github.com/mikefarah/yaml/v2/sorter.go b/vendor/github.com/mikefarah/yaml/v2/sorter.go new file mode 100644 index 0000000000..4c45e660a8 --- /dev/null +++ b/vendor/github.com/mikefarah/yaml/v2/sorter.go @@ -0,0 +1,113 @@ +package yaml + +import ( + "reflect" + "unicode" +) + +type keyList []reflect.Value + +func (l keyList) Len() int { return len(l) } +func (l keyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } +func (l keyList) Less(i, j int) bool { + a := l[i] + b := l[j] + ak := a.Kind() + bk := b.Kind() + for (ak == reflect.Interface || ak == reflect.Ptr) && !a.IsNil() { + a = a.Elem() + ak = a.Kind() + } + for (bk == reflect.Interface || bk == reflect.Ptr) && !b.IsNil() { + b = b.Elem() + bk = b.Kind() + } + af, aok := keyFloat(a) + bf, bok := keyFloat(b) + if aok && bok { + if af != bf { + return af < bf + } + if ak != bk { + return ak < bk + } + return numLess(a, b) + } + if ak != reflect.String || bk != reflect.String { + return ak < bk + } + ar, br := []rune(a.String()), []rune(b.String()) + for i := 0; i < len(ar) && i < len(br); i++ { + if ar[i] == br[i] { + continue + } + al := unicode.IsLetter(ar[i]) + bl := unicode.IsLetter(br[i]) + if al && bl { + return ar[i] < br[i] + } + if al || bl { + return bl + } + var ai, bi int + var an, bn int64 + if ar[i] == '0' || br[i] == '0' { + for j := i-1; j >= 0 && unicode.IsDigit(ar[j]); j-- { + if ar[j] != '0' { + an = 1 + bn = 1 + break + } + } + } + for ai = i; ai < len(ar) && unicode.IsDigit(ar[ai]); ai++ { + an = an*10 + int64(ar[ai]-'0') + } + for bi = i; bi < len(br) && unicode.IsDigit(br[bi]); bi++ { + bn = bn*10 + int64(br[bi]-'0') + } + if an != bn { + return an < bn + } + if ai != bi { + return ai < bi + } + return ar[i] < br[i] + } + return len(ar) < len(br) +} + +// keyFloat returns a float value for v if it is a number/bool +// and whether it is a number/bool or not. +func keyFloat(v reflect.Value) (f float64, ok bool) { + switch v.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return float64(v.Int()), true + case reflect.Float32, reflect.Float64: + return v.Float(), true + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return float64(v.Uint()), true + case reflect.Bool: + if v.Bool() { + return 1, true + } + return 0, true + } + return 0, false +} + +// numLess returns whether a < b. +// a and b must necessarily have the same kind. +func numLess(a, b reflect.Value) bool { + switch a.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return a.Int() < b.Int() + case reflect.Float32, reflect.Float64: + return a.Float() < b.Float() + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return a.Uint() < b.Uint() + case reflect.Bool: + return !a.Bool() && b.Bool() + } + panic("not a number") +} diff --git a/vendor/github.com/mikefarah/yaml/v2/writerc.go b/vendor/github.com/mikefarah/yaml/v2/writerc.go new file mode 100644 index 0000000000..a2dde608cb --- /dev/null +++ b/vendor/github.com/mikefarah/yaml/v2/writerc.go @@ -0,0 +1,26 @@ +package yaml + +// Set the writer error and return false. +func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool { + emitter.error = yaml_WRITER_ERROR + emitter.problem = problem + return false +} + +// Flush the output buffer. +func yaml_emitter_flush(emitter *yaml_emitter_t) bool { + if emitter.write_handler == nil { + panic("write handler not set") + } + + // Check if the buffer is empty. + if emitter.buffer_pos == 0 { + return true + } + + if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil { + return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error()) + } + emitter.buffer_pos = 0 + return true +} diff --git a/vendor/github.com/mikefarah/yaml/v2/yaml.go b/vendor/github.com/mikefarah/yaml/v2/yaml.go new file mode 100644 index 0000000000..de85aa4cdb --- /dev/null +++ b/vendor/github.com/mikefarah/yaml/v2/yaml.go @@ -0,0 +1,466 @@ +// Package yaml implements YAML support for the Go language. +// +// Source code and other details for the project are available at GitHub: +// +// https://github.com/go-yaml/yaml +// +package yaml + +import ( + "errors" + "fmt" + "io" + "reflect" + "strings" + "sync" +) + +// MapSlice encodes and decodes as a YAML map. +// The order of keys is preserved when encoding and decoding. +type MapSlice []MapItem + +// MapItem is an item in a MapSlice. +type MapItem struct { + Key, Value interface{} +} + +// The Unmarshaler interface may be implemented by types to customize their +// behavior when being unmarshaled from a YAML document. The UnmarshalYAML +// method receives a function that may be called to unmarshal the original +// YAML value into a field or variable. It is safe to call the unmarshal +// function parameter more than once if necessary. +type Unmarshaler interface { + UnmarshalYAML(unmarshal func(interface{}) error) error +} + +// The Marshaler interface may be implemented by types to customize their +// behavior when being marshaled into a YAML document. The returned value +// is marshaled in place of the original value implementing Marshaler. +// +// If an error is returned by MarshalYAML, the marshaling procedure stops +// and returns with the provided error. +type Marshaler interface { + MarshalYAML() (interface{}, error) +} + +// Unmarshal decodes the first document found within the in byte slice +// and assigns decoded values into the out value. +// +// Maps and pointers (to a struct, string, int, etc) are accepted as out +// values. If an internal pointer within a struct is not initialized, +// the yaml package will initialize it if necessary for unmarshalling +// the provided data. The out parameter must not be nil. +// +// The type of the decoded values should be compatible with the respective +// values in out. If one or more values cannot be decoded due to a type +// mismatches, decoding continues partially until the end of the YAML +// content, and a *yaml.TypeError is returned with details for all +// missed values. +// +// Struct fields are only unmarshalled if they are exported (have an +// upper case first letter), and are unmarshalled using the field name +// lowercased as the default key. Custom keys may be defined via the +// "yaml" name in the field tag: the content preceding the first comma +// is used as the key, and the following comma-separated options are +// used to tweak the marshalling process (see Marshal). +// Conflicting names result in a runtime error. +// +// For example: +// +// type T struct { +// F int `yaml:"a,omitempty"` +// B int +// } +// var t T +// yaml.Unmarshal([]byte("a: 1\nb: 2"), &t) +// +// See the documentation of Marshal for the format of tags and a list of +// supported tag options. +// +func Unmarshal(in []byte, out interface{}) (err error) { + return unmarshal(in, out, false) +} + +// UnmarshalStrict is like Unmarshal except that any fields that are found +// in the data that do not have corresponding struct members, or mapping +// keys that are duplicates, will result in +// an error. +func UnmarshalStrict(in []byte, out interface{}) (err error) { + return unmarshal(in, out, true) +} + +// A Decorder reads and decodes YAML values from an input stream. +type Decoder struct { + strict bool + parser *parser +} + +// NewDecoder returns a new decoder that reads from r. +// +// The decoder introduces its own buffering and may read +// data from r beyond the YAML values requested. +func NewDecoder(r io.Reader) *Decoder { + return &Decoder{ + parser: newParserFromReader(r), + } +} + +// SetStrict sets whether strict decoding behaviour is enabled when +// decoding items in the data (see UnmarshalStrict). By default, decoding is not strict. +func (dec *Decoder) SetStrict(strict bool) { + dec.strict = strict +} + +// Decode reads the next YAML-encoded value from its input +// and stores it in the value pointed to by v. +// +// See the documentation for Unmarshal for details about the +// conversion of YAML into a Go value. +func (dec *Decoder) Decode(v interface{}) (err error) { + d := newDecoder(dec.strict) + defer handleErr(&err) + node := dec.parser.parse() + if node == nil { + return io.EOF + } + out := reflect.ValueOf(v) + if out.Kind() == reflect.Ptr && !out.IsNil() { + out = out.Elem() + } + d.unmarshal(node, out) + if len(d.terrors) > 0 { + return &TypeError{d.terrors} + } + return nil +} + +func unmarshal(in []byte, out interface{}, strict bool) (err error) { + defer handleErr(&err) + d := newDecoder(strict) + p := newParser(in) + defer p.destroy() + node := p.parse() + if node != nil { + v := reflect.ValueOf(out) + if v.Kind() == reflect.Ptr && !v.IsNil() { + v = v.Elem() + } + d.unmarshal(node, v) + } + if len(d.terrors) > 0 { + return &TypeError{d.terrors} + } + return nil +} + +// Marshal serializes the value provided into a YAML document. The structure +// of the generated document will reflect the structure of the value itself. +// Maps and pointers (to struct, string, int, etc) are accepted as the in value. +// +// Struct fields are only marshalled if they are exported (have an upper case +// first letter), and are marshalled using the field name lowercased as the +// default key. Custom keys may be defined via the "yaml" name in the field +// tag: the content preceding the first comma is used as the key, and the +// following comma-separated options are used to tweak the marshalling process. +// Conflicting names result in a runtime error. +// +// The field tag format accepted is: +// +// `(...) yaml:"[][,[,]]" (...)` +// +// The following flags are currently supported: +// +// omitempty Only include the field if it's not set to the zero +// value for the type or to empty slices or maps. +// Zero valued structs will be omitted if all their public +// fields are zero, unless they implement an IsZero +// method (see the IsZeroer interface type), in which +// case the field will be included if that method returns true. +// +// flow Marshal using a flow style (useful for structs, +// sequences and maps). +// +// inline Inline the field, which must be a struct or a map, +// causing all of its fields or keys to be processed as if +// they were part of the outer struct. For maps, keys must +// not conflict with the yaml keys of other struct fields. +// +// In addition, if the key is "-", the field is ignored. +// +// For example: +// +// type T struct { +// F int `yaml:"a,omitempty"` +// B int +// } +// yaml.Marshal(&T{B: 2}) // Returns "b: 2\n" +// yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n" +// +func Marshal(in interface{}) (out []byte, err error) { + defer handleErr(&err) + e := newEncoder() + defer e.destroy() + e.marshalDoc("", reflect.ValueOf(in)) + e.finish() + out = e.out + return +} + +// An Encoder writes YAML values to an output stream. +type Encoder struct { + encoder *encoder +} + +// NewEncoder returns a new encoder that writes to w. +// The Encoder should be closed after use to flush all data +// to w. +func NewEncoder(w io.Writer) *Encoder { + return &Encoder{ + encoder: newEncoderWithWriter(w), + } +} + +// Encode writes the YAML encoding of v to the stream. +// If multiple items are encoded to the stream, the +// second and subsequent document will be preceded +// with a "---" document separator, but the first will not. +// +// See the documentation for Marshal for details about the conversion of Go +// values to YAML. +func (e *Encoder) Encode(v interface{}) (err error) { + defer handleErr(&err) + e.encoder.marshalDoc("", reflect.ValueOf(v)) + return nil +} + +// Close closes the encoder by writing any remaining data. +// It does not write a stream terminating string "...". +func (e *Encoder) Close() (err error) { + defer handleErr(&err) + e.encoder.finish() + return nil +} + +func handleErr(err *error) { + if v := recover(); v != nil { + if e, ok := v.(yamlError); ok { + *err = e.err + } else { + panic(v) + } + } +} + +type yamlError struct { + err error +} + +func fail(err error) { + panic(yamlError{err}) +} + +func failf(format string, args ...interface{}) { + panic(yamlError{fmt.Errorf("yaml: "+format, args...)}) +} + +// A TypeError is returned by Unmarshal when one or more fields in +// the YAML document cannot be properly decoded into the requested +// types. When this error is returned, the value is still +// unmarshaled partially. +type TypeError struct { + Errors []string +} + +func (e *TypeError) Error() string { + return fmt.Sprintf("yaml: unmarshal errors:\n %s", strings.Join(e.Errors, "\n ")) +} + +// -------------------------------------------------------------------------- +// Maintain a mapping of keys to structure field indexes + +// The code in this section was copied from mgo/bson. + +// structInfo holds details for the serialization of fields of +// a given struct. +type structInfo struct { + FieldsMap map[string]fieldInfo + FieldsList []fieldInfo + + // InlineMap is the number of the field in the struct that + // contains an ,inline map, or -1 if there's none. + InlineMap int +} + +type fieldInfo struct { + Key string + Num int + OmitEmpty bool + Flow bool + // Id holds the unique field identifier, so we can cheaply + // check for field duplicates without maintaining an extra map. + Id int + + // Inline holds the field index if the field is part of an inlined struct. + Inline []int +} + +var structMap = make(map[reflect.Type]*structInfo) +var fieldMapMutex sync.RWMutex + +func getStructInfo(st reflect.Type) (*structInfo, error) { + fieldMapMutex.RLock() + sinfo, found := structMap[st] + fieldMapMutex.RUnlock() + if found { + return sinfo, nil + } + + n := st.NumField() + fieldsMap := make(map[string]fieldInfo) + fieldsList := make([]fieldInfo, 0, n) + inlineMap := -1 + for i := 0; i != n; i++ { + field := st.Field(i) + if field.PkgPath != "" && !field.Anonymous { + continue // Private field + } + + info := fieldInfo{Num: i} + + tag := field.Tag.Get("yaml") + if tag == "" && strings.Index(string(field.Tag), ":") < 0 { + tag = string(field.Tag) + } + if tag == "-" { + continue + } + + inline := false + fields := strings.Split(tag, ",") + if len(fields) > 1 { + for _, flag := range fields[1:] { + switch flag { + case "omitempty": + info.OmitEmpty = true + case "flow": + info.Flow = true + case "inline": + inline = true + default: + return nil, errors.New(fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st)) + } + } + tag = fields[0] + } + + if inline { + switch field.Type.Kind() { + case reflect.Map: + if inlineMap >= 0 { + return nil, errors.New("Multiple ,inline maps in struct " + st.String()) + } + if field.Type.Key() != reflect.TypeOf("") { + return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String()) + } + inlineMap = info.Num + case reflect.Struct: + sinfo, err := getStructInfo(field.Type) + if err != nil { + return nil, err + } + for _, finfo := range sinfo.FieldsList { + if _, found := fieldsMap[finfo.Key]; found { + msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String() + return nil, errors.New(msg) + } + if finfo.Inline == nil { + finfo.Inline = []int{i, finfo.Num} + } else { + finfo.Inline = append([]int{i}, finfo.Inline...) + } + finfo.Id = len(fieldsList) + fieldsMap[finfo.Key] = finfo + fieldsList = append(fieldsList, finfo) + } + default: + //return nil, errors.New("Option ,inline needs a struct value or map field") + return nil, errors.New("Option ,inline needs a struct value field") + } + continue + } + + if tag != "" { + info.Key = tag + } else { + info.Key = strings.ToLower(field.Name) + } + + if _, found = fieldsMap[info.Key]; found { + msg := "Duplicated key '" + info.Key + "' in struct " + st.String() + return nil, errors.New(msg) + } + + info.Id = len(fieldsList) + fieldsList = append(fieldsList, info) + fieldsMap[info.Key] = info + } + + sinfo = &structInfo{ + FieldsMap: fieldsMap, + FieldsList: fieldsList, + InlineMap: inlineMap, + } + + fieldMapMutex.Lock() + structMap[st] = sinfo + fieldMapMutex.Unlock() + return sinfo, nil +} + +// IsZeroer is used to check whether an object is zero to +// determine whether it should be omitted when marshaling +// with the omitempty flag. One notable implementation +// is time.Time. +type IsZeroer interface { + IsZero() bool +} + +func isZero(v reflect.Value) bool { + kind := v.Kind() + if z, ok := v.Interface().(IsZeroer); ok { + if (kind == reflect.Ptr || kind == reflect.Interface) && v.IsNil() { + return true + } + return z.IsZero() + } + switch kind { + case reflect.String: + return len(v.String()) == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + case reflect.Slice: + return v.Len() == 0 + case reflect.Map: + return v.Len() == 0 + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Struct: + vt := v.Type() + for i := v.NumField() - 1; i >= 0; i-- { + if vt.Field(i).PkgPath != "" { + continue // Private field + } + if !isZero(v.Field(i)) { + return false + } + } + return true + } + return false +} diff --git a/vendor/github.com/mikefarah/yaml/v2/yamlh.go b/vendor/github.com/mikefarah/yaml/v2/yamlh.go new file mode 100644 index 0000000000..e25cee563b --- /dev/null +++ b/vendor/github.com/mikefarah/yaml/v2/yamlh.go @@ -0,0 +1,738 @@ +package yaml + +import ( + "fmt" + "io" +) + +// The version directive data. +type yaml_version_directive_t struct { + major int8 // The major version number. + minor int8 // The minor version number. +} + +// The tag directive data. +type yaml_tag_directive_t struct { + handle []byte // The tag handle. + prefix []byte // The tag prefix. +} + +type yaml_encoding_t int + +// The stream encoding. +const ( + // Let the parser choose the encoding. + yaml_ANY_ENCODING yaml_encoding_t = iota + + yaml_UTF8_ENCODING // The default UTF-8 encoding. + yaml_UTF16LE_ENCODING // The UTF-16-LE encoding with BOM. + yaml_UTF16BE_ENCODING // The UTF-16-BE encoding with BOM. +) + +type yaml_break_t int + +// Line break types. +const ( + // Let the parser choose the break type. + yaml_ANY_BREAK yaml_break_t = iota + + yaml_CR_BREAK // Use CR for line breaks (Mac style). + yaml_LN_BREAK // Use LN for line breaks (Unix style). + yaml_CRLN_BREAK // Use CR LN for line breaks (DOS style). +) + +type yaml_error_type_t int + +// Many bad things could happen with the parser and emitter. +const ( + // No error is produced. + yaml_NO_ERROR yaml_error_type_t = iota + + yaml_MEMORY_ERROR // Cannot allocate or reallocate a block of memory. + yaml_READER_ERROR // Cannot read or decode the input stream. + yaml_SCANNER_ERROR // Cannot scan the input stream. + yaml_PARSER_ERROR // Cannot parse the input stream. + yaml_COMPOSER_ERROR // Cannot compose a YAML document. + yaml_WRITER_ERROR // Cannot write to the output stream. + yaml_EMITTER_ERROR // Cannot emit a YAML stream. +) + +// The pointer position. +type yaml_mark_t struct { + index int // The position index. + line int // The position line. + column int // The position column. +} + +// Node Styles + +type yaml_style_t int8 + +type yaml_scalar_style_t yaml_style_t + +// Scalar styles. +const ( + // Let the emitter choose the style. + yaml_ANY_SCALAR_STYLE yaml_scalar_style_t = iota + + yaml_PLAIN_SCALAR_STYLE // The plain scalar style. + yaml_SINGLE_QUOTED_SCALAR_STYLE // The single-quoted scalar style. + yaml_DOUBLE_QUOTED_SCALAR_STYLE // The double-quoted scalar style. + yaml_LITERAL_SCALAR_STYLE // The literal scalar style. + yaml_FOLDED_SCALAR_STYLE // The folded scalar style. +) + +type yaml_sequence_style_t yaml_style_t + +// Sequence styles. +const ( + // Let the emitter choose the style. + yaml_ANY_SEQUENCE_STYLE yaml_sequence_style_t = iota + + yaml_BLOCK_SEQUENCE_STYLE // The block sequence style. + yaml_FLOW_SEQUENCE_STYLE // The flow sequence style. +) + +type yaml_mapping_style_t yaml_style_t + +// Mapping styles. +const ( + // Let the emitter choose the style. + yaml_ANY_MAPPING_STYLE yaml_mapping_style_t = iota + + yaml_BLOCK_MAPPING_STYLE // The block mapping style. + yaml_FLOW_MAPPING_STYLE // The flow mapping style. +) + +// Tokens + +type yaml_token_type_t int + +// Token types. +const ( + // An empty token. + yaml_NO_TOKEN yaml_token_type_t = iota + + yaml_STREAM_START_TOKEN // A STREAM-START token. + yaml_STREAM_END_TOKEN // A STREAM-END token. + + yaml_VERSION_DIRECTIVE_TOKEN // A VERSION-DIRECTIVE token. + yaml_TAG_DIRECTIVE_TOKEN // A TAG-DIRECTIVE token. + yaml_DOCUMENT_START_TOKEN // A DOCUMENT-START token. + yaml_DOCUMENT_END_TOKEN // A DOCUMENT-END token. + + yaml_BLOCK_SEQUENCE_START_TOKEN // A BLOCK-SEQUENCE-START token. + yaml_BLOCK_MAPPING_START_TOKEN // A BLOCK-SEQUENCE-END token. + yaml_BLOCK_END_TOKEN // A BLOCK-END token. + + yaml_FLOW_SEQUENCE_START_TOKEN // A FLOW-SEQUENCE-START token. + yaml_FLOW_SEQUENCE_END_TOKEN // A FLOW-SEQUENCE-END token. + yaml_FLOW_MAPPING_START_TOKEN // A FLOW-MAPPING-START token. + yaml_FLOW_MAPPING_END_TOKEN // A FLOW-MAPPING-END token. + + yaml_BLOCK_ENTRY_TOKEN // A BLOCK-ENTRY token. + yaml_FLOW_ENTRY_TOKEN // A FLOW-ENTRY token. + yaml_KEY_TOKEN // A KEY token. + yaml_VALUE_TOKEN // A VALUE token. + + yaml_ALIAS_TOKEN // An ALIAS token. + yaml_ANCHOR_TOKEN // An ANCHOR token. + yaml_TAG_TOKEN // A TAG token. + yaml_SCALAR_TOKEN // A SCALAR token. +) + +func (tt yaml_token_type_t) String() string { + switch tt { + case yaml_NO_TOKEN: + return "yaml_NO_TOKEN" + case yaml_STREAM_START_TOKEN: + return "yaml_STREAM_START_TOKEN" + case yaml_STREAM_END_TOKEN: + return "yaml_STREAM_END_TOKEN" + case yaml_VERSION_DIRECTIVE_TOKEN: + return "yaml_VERSION_DIRECTIVE_TOKEN" + case yaml_TAG_DIRECTIVE_TOKEN: + return "yaml_TAG_DIRECTIVE_TOKEN" + case yaml_DOCUMENT_START_TOKEN: + return "yaml_DOCUMENT_START_TOKEN" + case yaml_DOCUMENT_END_TOKEN: + return "yaml_DOCUMENT_END_TOKEN" + case yaml_BLOCK_SEQUENCE_START_TOKEN: + return "yaml_BLOCK_SEQUENCE_START_TOKEN" + case yaml_BLOCK_MAPPING_START_TOKEN: + return "yaml_BLOCK_MAPPING_START_TOKEN" + case yaml_BLOCK_END_TOKEN: + return "yaml_BLOCK_END_TOKEN" + case yaml_FLOW_SEQUENCE_START_TOKEN: + return "yaml_FLOW_SEQUENCE_START_TOKEN" + case yaml_FLOW_SEQUENCE_END_TOKEN: + return "yaml_FLOW_SEQUENCE_END_TOKEN" + case yaml_FLOW_MAPPING_START_TOKEN: + return "yaml_FLOW_MAPPING_START_TOKEN" + case yaml_FLOW_MAPPING_END_TOKEN: + return "yaml_FLOW_MAPPING_END_TOKEN" + case yaml_BLOCK_ENTRY_TOKEN: + return "yaml_BLOCK_ENTRY_TOKEN" + case yaml_FLOW_ENTRY_TOKEN: + return "yaml_FLOW_ENTRY_TOKEN" + case yaml_KEY_TOKEN: + return "yaml_KEY_TOKEN" + case yaml_VALUE_TOKEN: + return "yaml_VALUE_TOKEN" + case yaml_ALIAS_TOKEN: + return "yaml_ALIAS_TOKEN" + case yaml_ANCHOR_TOKEN: + return "yaml_ANCHOR_TOKEN" + case yaml_TAG_TOKEN: + return "yaml_TAG_TOKEN" + case yaml_SCALAR_TOKEN: + return "yaml_SCALAR_TOKEN" + } + return "" +} + +// The token structure. +type yaml_token_t struct { + // The token type. + typ yaml_token_type_t + + // The start/end of the token. + start_mark, end_mark yaml_mark_t + + // The stream encoding (for yaml_STREAM_START_TOKEN). + encoding yaml_encoding_t + + // The alias/anchor/scalar value or tag/tag directive handle + // (for yaml_ALIAS_TOKEN, yaml_ANCHOR_TOKEN, yaml_SCALAR_TOKEN, yaml_TAG_TOKEN, yaml_TAG_DIRECTIVE_TOKEN). + value []byte + + // The tag suffix (for yaml_TAG_TOKEN). + suffix []byte + + // The tag directive prefix (for yaml_TAG_DIRECTIVE_TOKEN). + prefix []byte + + // The scalar style (for yaml_SCALAR_TOKEN). + style yaml_scalar_style_t + + // The version directive major/minor (for yaml_VERSION_DIRECTIVE_TOKEN). + major, minor int8 +} + +// Events + +type yaml_event_type_t int8 + +// Event types. +const ( + // An empty event. + yaml_NO_EVENT yaml_event_type_t = iota + + yaml_STREAM_START_EVENT // A STREAM-START event. + yaml_STREAM_END_EVENT // A STREAM-END event. + yaml_DOCUMENT_START_EVENT // A DOCUMENT-START event. + yaml_DOCUMENT_END_EVENT // A DOCUMENT-END event. + yaml_ALIAS_EVENT // An ALIAS event. + yaml_SCALAR_EVENT // A SCALAR event. + yaml_SEQUENCE_START_EVENT // A SEQUENCE-START event. + yaml_SEQUENCE_END_EVENT // A SEQUENCE-END event. + yaml_MAPPING_START_EVENT // A MAPPING-START event. + yaml_MAPPING_END_EVENT // A MAPPING-END event. +) + +var eventStrings = []string{ + yaml_NO_EVENT: "none", + yaml_STREAM_START_EVENT: "stream start", + yaml_STREAM_END_EVENT: "stream end", + yaml_DOCUMENT_START_EVENT: "document start", + yaml_DOCUMENT_END_EVENT: "document end", + yaml_ALIAS_EVENT: "alias", + yaml_SCALAR_EVENT: "scalar", + yaml_SEQUENCE_START_EVENT: "sequence start", + yaml_SEQUENCE_END_EVENT: "sequence end", + yaml_MAPPING_START_EVENT: "mapping start", + yaml_MAPPING_END_EVENT: "mapping end", +} + +func (e yaml_event_type_t) String() string { + if e < 0 || int(e) >= len(eventStrings) { + return fmt.Sprintf("unknown event %d", e) + } + return eventStrings[e] +} + +// The event structure. +type yaml_event_t struct { + + // The event type. + typ yaml_event_type_t + + // The start and end of the event. + start_mark, end_mark yaml_mark_t + + // The document encoding (for yaml_STREAM_START_EVENT). + encoding yaml_encoding_t + + // The version directive (for yaml_DOCUMENT_START_EVENT). + version_directive *yaml_version_directive_t + + // The list of tag directives (for yaml_DOCUMENT_START_EVENT). + tag_directives []yaml_tag_directive_t + + // The anchor (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_ALIAS_EVENT). + anchor []byte + + // The tag (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT). + tag []byte + + // The scalar value (for yaml_SCALAR_EVENT). + value []byte + + // Is the document start/end indicator implicit, or the tag optional? + // (for yaml_DOCUMENT_START_EVENT, yaml_DOCUMENT_END_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_SCALAR_EVENT). + implicit bool + + // Is the tag optional for any non-plain style? (for yaml_SCALAR_EVENT). + quoted_implicit bool + + // The style (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT). + style yaml_style_t +} + +func (e *yaml_event_t) scalar_style() yaml_scalar_style_t { return yaml_scalar_style_t(e.style) } +func (e *yaml_event_t) sequence_style() yaml_sequence_style_t { return yaml_sequence_style_t(e.style) } +func (e *yaml_event_t) mapping_style() yaml_mapping_style_t { return yaml_mapping_style_t(e.style) } + +// Nodes + +const ( + yaml_NULL_TAG = "tag:yaml.org,2002:null" // The tag !!null with the only possible value: null. + yaml_BOOL_TAG = "tag:yaml.org,2002:bool" // The tag !!bool with the values: true and false. + yaml_STR_TAG = "tag:yaml.org,2002:str" // The tag !!str for string values. + yaml_INT_TAG = "tag:yaml.org,2002:int" // The tag !!int for integer values. + yaml_FLOAT_TAG = "tag:yaml.org,2002:float" // The tag !!float for float values. + yaml_TIMESTAMP_TAG = "tag:yaml.org,2002:timestamp" // The tag !!timestamp for date and time values. + + yaml_SEQ_TAG = "tag:yaml.org,2002:seq" // The tag !!seq is used to denote sequences. + yaml_MAP_TAG = "tag:yaml.org,2002:map" // The tag !!map is used to denote mapping. + + // Not in original libyaml. + yaml_BINARY_TAG = "tag:yaml.org,2002:binary" + yaml_MERGE_TAG = "tag:yaml.org,2002:merge" + + yaml_DEFAULT_SCALAR_TAG = yaml_STR_TAG // The default scalar tag is !!str. + yaml_DEFAULT_SEQUENCE_TAG = yaml_SEQ_TAG // The default sequence tag is !!seq. + yaml_DEFAULT_MAPPING_TAG = yaml_MAP_TAG // The default mapping tag is !!map. +) + +type yaml_node_type_t int + +// Node types. +const ( + // An empty node. + yaml_NO_NODE yaml_node_type_t = iota + + yaml_SCALAR_NODE // A scalar node. + yaml_SEQUENCE_NODE // A sequence node. + yaml_MAPPING_NODE // A mapping node. +) + +// An element of a sequence node. +type yaml_node_item_t int + +// An element of a mapping node. +type yaml_node_pair_t struct { + key int // The key of the element. + value int // The value of the element. +} + +// The node structure. +type yaml_node_t struct { + typ yaml_node_type_t // The node type. + tag []byte // The node tag. + + // The node data. + + // The scalar parameters (for yaml_SCALAR_NODE). + scalar struct { + value []byte // The scalar value. + length int // The length of the scalar value. + style yaml_scalar_style_t // The scalar style. + } + + // The sequence parameters (for YAML_SEQUENCE_NODE). + sequence struct { + items_data []yaml_node_item_t // The stack of sequence items. + style yaml_sequence_style_t // The sequence style. + } + + // The mapping parameters (for yaml_MAPPING_NODE). + mapping struct { + pairs_data []yaml_node_pair_t // The stack of mapping pairs (key, value). + pairs_start *yaml_node_pair_t // The beginning of the stack. + pairs_end *yaml_node_pair_t // The end of the stack. + pairs_top *yaml_node_pair_t // The top of the stack. + style yaml_mapping_style_t // The mapping style. + } + + start_mark yaml_mark_t // The beginning of the node. + end_mark yaml_mark_t // The end of the node. + +} + +// The document structure. +type yaml_document_t struct { + + // The document nodes. + nodes []yaml_node_t + + // The version directive. + version_directive *yaml_version_directive_t + + // The list of tag directives. + tag_directives_data []yaml_tag_directive_t + tag_directives_start int // The beginning of the tag directives list. + tag_directives_end int // The end of the tag directives list. + + start_implicit int // Is the document start indicator implicit? + end_implicit int // Is the document end indicator implicit? + + // The start/end of the document. + start_mark, end_mark yaml_mark_t +} + +// The prototype of a read handler. +// +// The read handler is called when the parser needs to read more bytes from the +// source. The handler should write not more than size bytes to the buffer. +// The number of written bytes should be set to the size_read variable. +// +// [in,out] data A pointer to an application data specified by +// yaml_parser_set_input(). +// [out] buffer The buffer to write the data from the source. +// [in] size The size of the buffer. +// [out] size_read The actual number of bytes read from the source. +// +// On success, the handler should return 1. If the handler failed, +// the returned value should be 0. On EOF, the handler should set the +// size_read to 0 and return 1. +type yaml_read_handler_t func(parser *yaml_parser_t, buffer []byte) (n int, err error) + +// This structure holds information about a potential simple key. +type yaml_simple_key_t struct { + possible bool // Is a simple key possible? + required bool // Is a simple key required? + token_number int // The number of the token. + mark yaml_mark_t // The position mark. +} + +// The states of the parser. +type yaml_parser_state_t int + +const ( + yaml_PARSE_STREAM_START_STATE yaml_parser_state_t = iota + + yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE // Expect the beginning of an implicit document. + yaml_PARSE_DOCUMENT_START_STATE // Expect DOCUMENT-START. + yaml_PARSE_DOCUMENT_CONTENT_STATE // Expect the content of a document. + yaml_PARSE_DOCUMENT_END_STATE // Expect DOCUMENT-END. + yaml_PARSE_BLOCK_NODE_STATE // Expect a block node. + yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE // Expect a block node or indentless sequence. + yaml_PARSE_FLOW_NODE_STATE // Expect a flow node. + yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a block sequence. + yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE // Expect an entry of a block sequence. + yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE // Expect an entry of an indentless sequence. + yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping. + yaml_PARSE_BLOCK_MAPPING_KEY_STATE // Expect a block mapping key. + yaml_PARSE_BLOCK_MAPPING_VALUE_STATE // Expect a block mapping value. + yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a flow sequence. + yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE // Expect an entry of a flow sequence. + yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE // Expect a key of an ordered mapping. + yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE // Expect a value of an ordered mapping. + yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE // Expect the and of an ordered mapping entry. + yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping. + yaml_PARSE_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping. + yaml_PARSE_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping. + yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE // Expect an empty value of a flow mapping. + yaml_PARSE_END_STATE // Expect nothing. +) + +func (ps yaml_parser_state_t) String() string { + switch ps { + case yaml_PARSE_STREAM_START_STATE: + return "yaml_PARSE_STREAM_START_STATE" + case yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE: + return "yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE" + case yaml_PARSE_DOCUMENT_START_STATE: + return "yaml_PARSE_DOCUMENT_START_STATE" + case yaml_PARSE_DOCUMENT_CONTENT_STATE: + return "yaml_PARSE_DOCUMENT_CONTENT_STATE" + case yaml_PARSE_DOCUMENT_END_STATE: + return "yaml_PARSE_DOCUMENT_END_STATE" + case yaml_PARSE_BLOCK_NODE_STATE: + return "yaml_PARSE_BLOCK_NODE_STATE" + case yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE: + return "yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE" + case yaml_PARSE_FLOW_NODE_STATE: + return "yaml_PARSE_FLOW_NODE_STATE" + case yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE: + return "yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE" + case yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE: + return "yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE" + case yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE: + return "yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE" + case yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE: + return "yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE" + case yaml_PARSE_BLOCK_MAPPING_KEY_STATE: + return "yaml_PARSE_BLOCK_MAPPING_KEY_STATE" + case yaml_PARSE_BLOCK_MAPPING_VALUE_STATE: + return "yaml_PARSE_BLOCK_MAPPING_VALUE_STATE" + case yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE: + return "yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE" + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE: + return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE" + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE: + return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE" + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE: + return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE" + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE: + return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE" + case yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE: + return "yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE" + case yaml_PARSE_FLOW_MAPPING_KEY_STATE: + return "yaml_PARSE_FLOW_MAPPING_KEY_STATE" + case yaml_PARSE_FLOW_MAPPING_VALUE_STATE: + return "yaml_PARSE_FLOW_MAPPING_VALUE_STATE" + case yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE: + return "yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE" + case yaml_PARSE_END_STATE: + return "yaml_PARSE_END_STATE" + } + return "" +} + +// This structure holds aliases data. +type yaml_alias_data_t struct { + anchor []byte // The anchor. + index int // The node id. + mark yaml_mark_t // The anchor mark. +} + +// The parser structure. +// +// All members are internal. Manage the structure using the +// yaml_parser_ family of functions. +type yaml_parser_t struct { + + // Error handling + + error yaml_error_type_t // Error type. + + problem string // Error description. + + // The byte about which the problem occurred. + problem_offset int + problem_value int + problem_mark yaml_mark_t + + // The error context. + context string + context_mark yaml_mark_t + + // Reader stuff + + read_handler yaml_read_handler_t // Read handler. + + input_reader io.Reader // File input data. + input []byte // String input data. + input_pos int + + eof bool // EOF flag + + buffer []byte // The working buffer. + buffer_pos int // The current position of the buffer. + + unread int // The number of unread characters in the buffer. + + raw_buffer []byte // The raw buffer. + raw_buffer_pos int // The current position of the buffer. + + encoding yaml_encoding_t // The input encoding. + + offset int // The offset of the current position (in bytes). + mark yaml_mark_t // The mark of the current position. + + // Scanner stuff + + stream_start_produced bool // Have we started to scan the input stream? + stream_end_produced bool // Have we reached the end of the input stream? + + flow_level int // The number of unclosed '[' and '{' indicators. + + tokens []yaml_token_t // The tokens queue. + tokens_head int // The head of the tokens queue. + tokens_parsed int // The number of tokens fetched from the queue. + token_available bool // Does the tokens queue contain a token ready for dequeueing. + + indent int // The current indentation level. + indents []int // The indentation levels stack. + + simple_key_allowed bool // May a simple key occur at the current position? + simple_keys []yaml_simple_key_t // The stack of simple keys. + + // Parser stuff + + state yaml_parser_state_t // The current parser state. + states []yaml_parser_state_t // The parser states stack. + marks []yaml_mark_t // The stack of marks. + tag_directives []yaml_tag_directive_t // The list of TAG directives. + + // Dumper stuff + + aliases []yaml_alias_data_t // The alias data. + + document *yaml_document_t // The currently parsed document. +} + +// Emitter Definitions + +// The prototype of a write handler. +// +// The write handler is called when the emitter needs to flush the accumulated +// characters to the output. The handler should write @a size bytes of the +// @a buffer to the output. +// +// @param[in,out] data A pointer to an application data specified by +// yaml_emitter_set_output(). +// @param[in] buffer The buffer with bytes to be written. +// @param[in] size The size of the buffer. +// +// @returns On success, the handler should return @c 1. If the handler failed, +// the returned value should be @c 0. +// +type yaml_write_handler_t func(emitter *yaml_emitter_t, buffer []byte) error + +type yaml_emitter_state_t int + +// The emitter states. +const ( + // Expect STREAM-START. + yaml_EMIT_STREAM_START_STATE yaml_emitter_state_t = iota + + yaml_EMIT_FIRST_DOCUMENT_START_STATE // Expect the first DOCUMENT-START or STREAM-END. + yaml_EMIT_DOCUMENT_START_STATE // Expect DOCUMENT-START or STREAM-END. + yaml_EMIT_DOCUMENT_CONTENT_STATE // Expect the content of a document. + yaml_EMIT_DOCUMENT_END_STATE // Expect DOCUMENT-END. + yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a flow sequence. + yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE // Expect an item of a flow sequence. + yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping. + yaml_EMIT_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping. + yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a flow mapping. + yaml_EMIT_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping. + yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a block sequence. + yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE // Expect an item of a block sequence. + yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping. + yaml_EMIT_BLOCK_MAPPING_KEY_STATE // Expect the key of a block mapping. + yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a block mapping. + yaml_EMIT_BLOCK_MAPPING_VALUE_STATE // Expect a value of a block mapping. + yaml_EMIT_END_STATE // Expect nothing. +) + +// The emitter structure. +// +// All members are internal. Manage the structure using the @c yaml_emitter_ +// family of functions. +type yaml_emitter_t struct { + + // Error handling + + error yaml_error_type_t // Error type. + problem string // Error description. + + // Writer stuff + + write_handler yaml_write_handler_t // Write handler. + + output_buffer *[]byte // String output data. + output_writer io.Writer // File output data. + + buffer []byte // The working buffer. + buffer_pos int // The current position of the buffer. + + raw_buffer []byte // The raw buffer. + raw_buffer_pos int // The current position of the buffer. + + encoding yaml_encoding_t // The stream encoding. + + // Emitter stuff + + canonical bool // If the output is in the canonical style? + best_indent int // The number of indentation spaces. + best_width int // The preferred width of the output lines. + unicode bool // Allow unescaped non-ASCII characters? + line_break yaml_break_t // The preferred line break. + + state yaml_emitter_state_t // The current emitter state. + states []yaml_emitter_state_t // The stack of states. + + events []yaml_event_t // The event queue. + events_head int // The head of the event queue. + + indents []int // The stack of indentation levels. + + tag_directives []yaml_tag_directive_t // The list of tag directives. + + indent int // The current indentation level. + + flow_level int // The current flow level. + + root_context bool // Is it the document root context? + sequence_context bool // Is it a sequence context? + mapping_context bool // Is it a mapping context? + simple_key_context bool // Is it a simple mapping key context? + + line int // The current line. + column int // The current column. + whitespace bool // If the last character was a whitespace? + indention bool // If the last character was an indentation character (' ', '-', '?', ':')? + open_ended bool // If an explicit document end is required? + + // Anchor analysis. + anchor_data struct { + anchor []byte // The anchor value. + alias bool // Is it an alias? + } + + // Tag analysis. + tag_data struct { + handle []byte // The tag handle. + suffix []byte // The tag suffix. + } + + // Scalar analysis. + scalar_data struct { + value []byte // The scalar value. + multiline bool // Does the scalar contain line breaks? + flow_plain_allowed bool // Can the scalar be expessed in the flow plain style? + block_plain_allowed bool // Can the scalar be expressed in the block plain style? + single_quoted_allowed bool // Can the scalar be expressed in the single quoted style? + block_allowed bool // Can the scalar be expressed in the literal or folded styles? + style yaml_scalar_style_t // The output style. + } + + // Dumper stuff + + opened bool // If the stream was already opened? + closed bool // If the stream was already closed? + + // The information associated with the document nodes. + anchors *struct { + references int // The number of references. + anchor int // The anchor id. + serialized bool // If the node has been emitted? + } + + last_anchor_id int // The last assigned anchor id. + + document *yaml_document_t // The currently emitted document. +} diff --git a/vendor/github.com/mikefarah/yaml/v2/yamlprivateh.go b/vendor/github.com/mikefarah/yaml/v2/yamlprivateh.go new file mode 100644 index 0000000000..8110ce3c37 --- /dev/null +++ b/vendor/github.com/mikefarah/yaml/v2/yamlprivateh.go @@ -0,0 +1,173 @@ +package yaml + +const ( + // The size of the input raw buffer. + input_raw_buffer_size = 512 + + // The size of the input buffer. + // It should be possible to decode the whole raw buffer. + input_buffer_size = input_raw_buffer_size * 3 + + // The size of the output buffer. + output_buffer_size = 128 + + // The size of the output raw buffer. + // It should be possible to encode the whole output buffer. + output_raw_buffer_size = (output_buffer_size*2 + 2) + + // The size of other stacks and queues. + initial_stack_size = 16 + initial_queue_size = 16 + initial_string_size = 16 +) + +// Check if the character at the specified position is an alphabetical +// character, a digit, '_', or '-'. +func is_alpha(b []byte, i int) bool { + return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'Z' || b[i] >= 'a' && b[i] <= 'z' || b[i] == '_' || b[i] == '-' +} + +// Check if the character at the specified position is a digit. +func is_digit(b []byte, i int) bool { + return b[i] >= '0' && b[i] <= '9' +} + +// Get the value of a digit. +func as_digit(b []byte, i int) int { + return int(b[i]) - '0' +} + +// Check if the character at the specified position is a hex-digit. +func is_hex(b []byte, i int) bool { + return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'F' || b[i] >= 'a' && b[i] <= 'f' +} + +// Get the value of a hex-digit. +func as_hex(b []byte, i int) int { + bi := b[i] + if bi >= 'A' && bi <= 'F' { + return int(bi) - 'A' + 10 + } + if bi >= 'a' && bi <= 'f' { + return int(bi) - 'a' + 10 + } + return int(bi) - '0' +} + +// Check if the character is ASCII. +func is_ascii(b []byte, i int) bool { + return b[i] <= 0x7F +} + +// Check if the character at the start of the buffer can be printed unescaped. +func is_printable(b []byte, i int) bool { + return ((b[i] == 0x0A) || // . == #x0A + (b[i] >= 0x20 && b[i] <= 0x7E) || // #x20 <= . <= #x7E + (b[i] == 0xC2 && b[i+1] >= 0xA0) || // #0xA0 <= . <= #xD7FF + (b[i] > 0xC2 && b[i] < 0xED) || + (b[i] == 0xED && b[i+1] < 0xA0) || + (b[i] == 0xEE) || + (b[i] == 0xEF && // #xE000 <= . <= #xFFFD + !(b[i+1] == 0xBB && b[i+2] == 0xBF) && // && . != #xFEFF + !(b[i+1] == 0xBF && (b[i+2] == 0xBE || b[i+2] == 0xBF)))) +} + +// Check if the character at the specified position is NUL. +func is_z(b []byte, i int) bool { + return b[i] == 0x00 +} + +// Check if the beginning of the buffer is a BOM. +func is_bom(b []byte, i int) bool { + return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF +} + +// Check if the character at the specified position is space. +func is_space(b []byte, i int) bool { + return b[i] == ' ' +} + +// Check if the character at the specified position is tab. +func is_tab(b []byte, i int) bool { + return b[i] == '\t' +} + +// Check if the character at the specified position is blank (space or tab). +func is_blank(b []byte, i int) bool { + //return is_space(b, i) || is_tab(b, i) + return b[i] == ' ' || b[i] == '\t' +} + +// Check if the character at the specified position is a line break. +func is_break(b []byte, i int) bool { + return (b[i] == '\r' || // CR (#xD) + b[i] == '\n' || // LF (#xA) + b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9) // PS (#x2029) +} + +func is_crlf(b []byte, i int) bool { + return b[i] == '\r' && b[i+1] == '\n' +} + +// Check if the character is a line break or NUL. +func is_breakz(b []byte, i int) bool { + //return is_break(b, i) || is_z(b, i) + return ( // is_break: + b[i] == '\r' || // CR (#xD) + b[i] == '\n' || // LF (#xA) + b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) + // is_z: + b[i] == 0) +} + +// Check if the character is a line break, space, or NUL. +func is_spacez(b []byte, i int) bool { + //return is_space(b, i) || is_breakz(b, i) + return ( // is_space: + b[i] == ' ' || + // is_breakz: + b[i] == '\r' || // CR (#xD) + b[i] == '\n' || // LF (#xA) + b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) + b[i] == 0) +} + +// Check if the character is a line break, space, tab, or NUL. +func is_blankz(b []byte, i int) bool { + //return is_blank(b, i) || is_breakz(b, i) + return ( // is_blank: + b[i] == ' ' || b[i] == '\t' || + // is_breakz: + b[i] == '\r' || // CR (#xD) + b[i] == '\n' || // LF (#xA) + b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) + b[i] == 0) +} + +// Determine the width of the character. +func width(b byte) int { + // Don't replace these by a switch without first + // confirming that it is being inlined. + if b&0x80 == 0x00 { + return 1 + } + if b&0xE0 == 0xC0 { + return 2 + } + if b&0xF0 == 0xE0 { + return 3 + } + if b&0xF8 == 0xF0 { + return 4 + } + return 0 + +} diff --git a/vendor/github.com/mikefarah/yq/v2/.dockerignore b/vendor/github.com/mikefarah/yq/v2/.dockerignore new file mode 100644 index 0000000000..ba077a4031 --- /dev/null +++ b/vendor/github.com/mikefarah/yq/v2/.dockerignore @@ -0,0 +1 @@ +bin diff --git a/vendor/github.com/mikefarah/yq/v2/.gitignore b/vendor/github.com/mikefarah/yq/v2/.gitignore new file mode 100644 index 0000000000..6911801400 --- /dev/null +++ b/vendor/github.com/mikefarah/yq/v2/.gitignore @@ -0,0 +1,39 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test +bin +build +build-done +.DS_Store + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go +coverage.out +*.exe +*.test +*.prof +yaml +vendor/ +tmp/ +cover/ +yq + +# snapcraft +parts/ +prime/ +.snapcraft/ +yq*.snap diff --git a/vendor/github.com/mikefarah/yq/v2/.travis.yml b/vendor/github.com/mikefarah/yq/v2/.travis.yml new file mode 100644 index 0000000000..6f4d9cfde8 --- /dev/null +++ b/vendor/github.com/mikefarah/yq/v2/.travis.yml @@ -0,0 +1,6 @@ +language: go +go: + - 1.13.x +script: + - scripts/devtools.sh + - make local build diff --git a/vendor/github.com/mikefarah/yq/v2/Dockerfile b/vendor/github.com/mikefarah/yq/v2/Dockerfile new file mode 100644 index 0000000000..32060934eb --- /dev/null +++ b/vendor/github.com/mikefarah/yq/v2/Dockerfile @@ -0,0 +1,23 @@ +FROM golang:1.13 as builder + +WORKDIR /go/src/mikefarah/yq + +# cache devtools +COPY ./scripts/devtools.sh /go/src/mikefarah/yq/scripts/devtools.sh +RUN ./scripts/devtools.sh + +COPY . /go/src/mikefarah/yq + +RUN CGO_ENABLED=0 make local build + +# Choose alpine as a base image to make this useful for CI, as many +# CI tools expect an interactive shell inside the container +FROM alpine:3.8 as production + +COPY --from=builder /go/src/mikefarah/yq/yq /usr/bin/yq +RUN chmod +x /usr/bin/yq + +ARG VERSION=none +LABEL version=${VERSION} + +WORKDIR /workdir diff --git a/vendor/github.com/mikefarah/yq/v2/Dockerfile.dev b/vendor/github.com/mikefarah/yq/v2/Dockerfile.dev new file mode 100644 index 0000000000..fb7ddb68e4 --- /dev/null +++ b/vendor/github.com/mikefarah/yq/v2/Dockerfile.dev @@ -0,0 +1,30 @@ +FROM golang:1.13 + +COPY scripts/devtools.sh /opt/devtools.sh + +RUN set -e -x \ + && /opt/devtools.sh + +# install mkdocs +RUN set -ex \ + && buildDeps=' \ + build-essential \ + python-dev \ + ' \ + && apt-get update && apt-get install -y --no-install-recommends \ + $buildDeps \ + python2.7 \ + python-setuptools \ + python-wheel \ + python-pip \ + && pip install --upgrade \ + pip \ + 'Markdown>=2.6.9' \ + 'mkdocs>=0.16.3' \ + 'mkdocs-material>=1.10.1' \ + 'markdown-include>=0.5.1' \ + && apt-get purge -y --auto-remove $buildDeps \ + && rm -rf /var/lib/apt/lists/* + +ENV CGO_ENABLED 0 +ENV GOPATH /go:/yq diff --git a/vendor/github.com/mikefarah/yq/v2/LICENSE b/vendor/github.com/mikefarah/yq/v2/LICENSE new file mode 100644 index 0000000000..e2f1fc03d1 --- /dev/null +++ b/vendor/github.com/mikefarah/yq/v2/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2017 Mike Farah + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/mikefarah/yq/v2/Makefile b/vendor/github.com/mikefarah/yq/v2/Makefile new file mode 100644 index 0000000000..22b72069ae --- /dev/null +++ b/vendor/github.com/mikefarah/yq/v2/Makefile @@ -0,0 +1,114 @@ +MAKEFLAGS += --warn-undefined-variables +SHELL := /bin/bash +.SHELLFLAGS := -o pipefail -euc +.DEFAULT_GOAL := install + +include Makefile.variables + +.PHONY: help +help: + @echo 'Management commands for cicdtest:' + @echo + @echo 'Usage:' + @echo ' ## Develop / Test Commands' + @echo ' make build Build yq binary.' + @echo ' make install Install yq.' + @echo ' make xcompile Build cross-compiled binaries of yq.' + @echo ' make vendor Install dependencies to vendor directory.' + @echo ' make format Run code formatter.' + @echo ' make check Run static code analysis (lint).' + @echo ' make test Run tests on project.' + @echo ' make cover Run tests and capture code coverage metrics on project.' + @echo ' make clean Clean the directory tree of produced artifacts.' + @echo + @echo ' ## Utility Commands' + @echo ' make setup Configures Minishfit/Docker directory mounts.' + @echo + + +.PHONY: clean +clean: + @rm -rf bin build cover *.out + +## prefix before other make targets to run in your local dev environment +local: | quiet + @$(eval DOCKRUN= ) + @mkdir -p tmp + @touch tmp/dev_image_id +quiet: # this is silly but shuts up 'Nothing to be done for `local`' + @: + +prepare: tmp/dev_image_id +tmp/dev_image_id: Dockerfile.dev scripts/devtools.sh + @mkdir -p tmp + @docker rmi -f ${DEV_IMAGE} > /dev/null 2>&1 || true + @docker build -t ${DEV_IMAGE} -f Dockerfile.dev . + @docker inspect -f "{{ .ID }}" ${DEV_IMAGE} > tmp/dev_image_id + +# ---------------------------------------------- +# build +.PHONY: build +build: build/dev + +.PHONY: build/dev +build/dev: test *.go + @mkdir -p bin/ + ${DOCKRUN} go build --ldflags "$(LDFLAGS)" + ${DOCKRUN} bash ./scripts/acceptance.sh + +## Compile the project for multiple OS and Architectures. +xcompile: check + @rm -rf build/ + @mkdir -p build + ${DOCKRUN} bash ./scripts/xcompile.sh + @find build -type d -exec chmod 755 {} \; || : + @find build -type f -exec chmod 755 {} \; || : + +.PHONY: install +install: build + ${DOCKRUN} go install + +# Each of the fetch should be an entry within vendor.json; not currently included within project +.PHONY: vendor +vendor: tmp/dev_image_id + ${DOCKRUN} go mod vendor + +# ---------------------------------------------- +# develop and test + +.PHONY: format +format: vendor + ${DOCKRUN} bash ./scripts/format.sh + +.PHONY: check +check: format + ${DOCKRUN} bash ./scripts/check.sh + +.PHONY: test +test: check + ${DOCKRUN} bash ./scripts/test.sh + +.PHONY: cover +cover: check + @rm -rf cover/ + @mkdir -p cover + ${DOCKRUN} bash ./scripts/coverage.sh + @find cover -type d -exec chmod 755 {} \; || : + @find cover -type f -exec chmod 644 {} \; || : + +.PHONY: build-docs +build-docs: prepare mkdocs.yml mkdocs/* + ${DOCKRUN} mkdocs build + @find docs -type d -exec chmod 755 {} \; || : + @find docs -type f -exec chmod 644 {} \; || : + +.PHONY: release +release: xcompile + ${DOCKRUN} bash ./scripts/publish.sh + +# ---------------------------------------------- +# utilities + +.PHONY: setup +setup: + @bash ./scripts/setup.sh diff --git a/vendor/github.com/mikefarah/yq/v2/Makefile.variables b/vendor/github.com/mikefarah/yq/v2/Makefile.variables new file mode 100644 index 0000000000..406bac34f4 --- /dev/null +++ b/vendor/github.com/mikefarah/yq/v2/Makefile.variables @@ -0,0 +1,38 @@ +export PROJECT = yq +IMPORT_PATH := github.com/mikefarah/${PROJECT} + +export GIT_COMMIT = $(shell git rev-parse --short HEAD) +export GIT_DIRTY = $(shell test -n "$$(git status --porcelain)" && echo "+CHANGES" || true) +export GIT_DESCRIBE = $(shell git describe --tags --always) +LDFLAGS := +LDFLAGS += -X main.GitCommit=${GIT_COMMIT}${GIT_DIRTY} +LDFLAGS += -X main.GitDescribe=${GIT_DESCRIBE} + +GITHUB_TOKEN ?= + +# Windows environment? +CYG_CHECK := $(shell hash cygpath 2>/dev/null && echo 1) +ifeq ($(CYG_CHECK),1) + VBOX_CHECK := $(shell hash VBoxManage 2>/dev/null && echo 1) + + # Docker Toolbox (pre-Windows 10) + ifeq ($(VBOX_CHECK),1) + ROOT := /${PROJECT} + else + # Docker Windows + ROOT := $(shell cygpath -m -a "$(shell pwd)") + endif +else + # all non-windows environments + ROOT := $(shell pwd) +endif + +DEV_IMAGE := ${PROJECT}_dev + +DOCKRUN := docker run --rm \ + -e LDFLAGS="${LDFLAGS}" \ + -e GITHUB_TOKEN="${GITHUB_TOKEN}" \ + -v ${ROOT}/vendor:/go/src \ + -v ${ROOT}:/${PROJECT}/src/${IMPORT_PATH} \ + -w /${PROJECT}/src/${IMPORT_PATH} \ + ${DEV_IMAGE} diff --git a/vendor/github.com/mikefarah/yq/v2/README.md b/vendor/github.com/mikefarah/yq/v2/README.md new file mode 100644 index 0000000000..f11624a9d2 --- /dev/null +++ b/vendor/github.com/mikefarah/yq/v2/README.md @@ -0,0 +1,127 @@ +# yq + +[![Build Status](https://travis-ci.org/mikefarah/yq.svg?branch=master)](https://travis-ci.org/mikefarah/yq) ![Docker Pulls](https://img.shields.io/docker/pulls/mikefarah/yq.svg) ![Github Releases (by Release)](https://img.shields.io/github/downloads/mikefarah/yq/total.svg) ![Go Report](https://goreportcard.com/badge/github.com/mikefarah/yq) + + +a lightweight and portable command-line YAML processor + +The aim of the project is to be the [jq](https://github.com/stedolan/jq) or sed of yaml files. + +## Install +### On MacOS: +``` +brew install yq +``` +### On Ubuntu and other Linux distros supporting `snap` packages: +``` +snap install yq +``` + +#### Snap notes +`yq` installs with with [_strict confinement_](https://docs.snapcraft.io/snap-confinement/6233) in snap, this means it doesn't have direct access to root files. To read root files you can: + +``` +sudo cat /etc/myfile | yq -r - somecommand +``` + +And to write to a root file you can either use [sponge](https://linux.die.net/man/1/sponge): +``` +sudo cat /etc/myfile | yq -r - somecommand | sudo sponge /etc/myfile +``` +or write to a temporary file: +``` +sudo cat /etc/myfile | yq -r - somecommand | sudo tee /etc/myfile.tmp +sudo mv /etc/myfile.tmp /etc/myfile +rm /etc/myfile.tmp +``` + +### On Ubuntu 16.04 or higher from Debian package: +``` +sudo add-apt-repository ppa:rmescandon/yq +sudo apt update +sudo apt install yq -y +``` +### or, [Download latest binary](https://github.com/mikefarah/yq/releases/latest) or alternatively: +``` +GO111MODULE=on go get github.com/mikefarah/yq@2.4.1 +``` + +## Run with Docker + +Oneshot use: + +```bash +docker run --rm -v ${PWD}:/workdir mikefarah/yq yq [flags] FILE... +``` + +Run commands interactively: + +```bash +docker run --rm -it -v ${PWD}:/workdir mikefarah/yq sh +``` + +It can be useful to have a bash function to avoid typing the whole docker command: + +```bash +yq() { + docker run --rm -i -v ${PWD}:/workdir mikefarah/yq yq $@ +} +``` + +## Features +- Written in portable go, so you can download a lovely dependency free binary +- Deep read a yaml file with a given path +- Update a yaml file given a path +- Update a yaml file given a script file +- Update creates any missing entries in the path on the fly +- Create a yaml file given a deep path and value +- Create a yaml file given a script file +- Prefix a path to a yaml file +- Convert from json to yaml +- Convert from yaml to json +- Pipe data in by using '-' +- Merge multiple yaml files where each additional file sets values for missing or null value keys. +- Merge multiple yaml files and override previous values. +- Merge multiple yaml files and append array values. +- Supports multiple documents in a single yaml file + +## [Usage](http://mikefarah.github.io/yq/) + +Check out the [documentation](http://mikefarah.github.io/yq/) for more detailed and advanced usage. + +``` +yq is a lightweight and portable command-line YAML processor. It aims to be the jq or sed of yaml files. + +Usage: + yq [flags] + yq [command] + +Available Commands: + delete yq d [--inplace/-i] [--doc/-d index] sample.yaml a.b.c + help Help about any command + merge yq m [--inplace/-i] [--doc/-d index] [--overwrite/-x] [--append/-a] sample.yaml sample2.yaml + new yq n [--script/-s script_file] a.b.c newValue + prefix yq p [--inplace/-i] [--doc/-d index] sample.yaml a.b.c + read yq r [--doc/-d index] sample.yaml a.b.c + write yq w [--inplace/-i] [--script/-s script_file] [--doc/-d index] sample.yaml a.b.c newValue + +Flags: + -h, --help help for yq + -t, --trim trim yaml output (default true) + -v, --verbose verbose mode + -V, --version Print version information and quit + +Use "yq [command] --help" for more information about a command. +``` + +## Contribute +1. `scripts/devtools.sh` +2. `make [local] vendor` +3. add unit tests +4. apply changes to go.mod +5. `make [local] build` +6. If required, update the user documentation + - Update README.md and/or documentation under the mkdocs folder + - `make [local] build-docs` + - browse to docs/index.html and check your changes +7. profit diff --git a/vendor/github.com/mikefarah/yq/v2/data_navigator.go b/vendor/github.com/mikefarah/yq/v2/data_navigator.go new file mode 100644 index 0000000000..5746db7095 --- /dev/null +++ b/vendor/github.com/mikefarah/yq/v2/data_navigator.go @@ -0,0 +1,349 @@ +package main + +import ( + "fmt" + "reflect" + "strconv" + "strings" + + yaml "github.com/mikefarah/yaml/v2" +) + +func matchesKey(key string, actual interface{}) bool { + var actualString = fmt.Sprintf("%v", actual) + var prefixMatch = strings.TrimSuffix(key, "*") + if prefixMatch != key { + return strings.HasPrefix(actualString, prefixMatch) + } + return actualString == key +} + +func entriesInSlice(context yaml.MapSlice, key string) []*yaml.MapItem { + var matches = make([]*yaml.MapItem, 0) + for idx := range context { + var entry = &context[idx] + if matchesKey(key, entry.Key) { + matches = append(matches, entry) + } + } + return matches +} + +func getMapSlice(context interface{}) yaml.MapSlice { + var mapSlice yaml.MapSlice + switch context := context.(type) { + case yaml.MapSlice: + mapSlice = context + default: + mapSlice = make(yaml.MapSlice, 0) + } + return mapSlice +} + +func getArray(context interface{}) (array []interface{}, ok bool) { + switch context := context.(type) { + case []interface{}: + array = context + ok = true + default: + array = make([]interface{}, 0) + ok = false + } + return +} + +func writeMap(context interface{}, paths []string, value interface{}) interface{} { + log.Debugf("writeMap with path %v for %v to set value %v\n", paths, context, value) + + mapSlice := getMapSlice(context) + + if len(paths) == 0 { + return context + } + + children := entriesInSlice(mapSlice, paths[0]) + + if len(children) == 0 && paths[0] == "*" { + log.Debugf("\tNo matches, return map as is") + return context + } + + if len(children) == 0 { + newChild := yaml.MapItem{Key: paths[0]} + mapSlice = append(mapSlice, newChild) + children = entriesInSlice(mapSlice, paths[0]) + log.Debugf("\tAppended child at %v for mapSlice %v\n", paths[0], mapSlice) + } + + remainingPaths := paths[1:] + for _, child := range children { + child.Value = updatedChildValue(child.Value, remainingPaths, value) + } + log.Debugf("\tReturning mapSlice %v\n", mapSlice) + return mapSlice +} + +func updatedChildValue(child interface{}, remainingPaths []string, value interface{}) interface{} { + if len(remainingPaths) == 0 { + return value + } + log.Debugf("updatedChildValue for child %v with path %v to set value %v", child, remainingPaths, value) + log.Debugf("type of child is %v", reflect.TypeOf(child)) + + switch child := child.(type) { + case nil: + if remainingPaths[0] == "+" || remainingPaths[0] == "*" { + return writeArray(child, remainingPaths, value) + } + case []interface{}: + _, nextIndexErr := strconv.ParseInt(remainingPaths[0], 10, 64) + arrayCommand := nextIndexErr == nil || remainingPaths[0] == "+" || remainingPaths[0] == "*" + if arrayCommand { + return writeArray(child, remainingPaths, value) + } + } + return writeMap(child, remainingPaths, value) +} + +func writeArray(context interface{}, paths []string, value interface{}) []interface{} { + log.Debugf("writeArray with path %v for %v to set value %v\n", paths, context, value) + array, _ := getArray(context) + + if len(paths) == 0 { + return array + } + + log.Debugf("\tarray %v\n", array) + + rawIndex := paths[0] + remainingPaths := paths[1:] + var index int64 + // the append array indicator + if rawIndex == "+" { + index = int64(len(array)) + } else if rawIndex == "*" { + for index, oldChild := range array { + array[index] = updatedChildValue(oldChild, remainingPaths, value) + } + return array + } else { + index, _ = strconv.ParseInt(rawIndex, 10, 64) // nolint + // writeArray is only called by updatedChildValue which handles parsing the + // index, as such this renders this dead code. + } + + for index >= int64(len(array)) { + array = append(array, nil) + } + currentChild := array[index] + + log.Debugf("\tcurrentChild %v\n", currentChild) + + array[index] = updatedChildValue(currentChild, remainingPaths, value) + log.Debugf("\tReturning array %v\n", array) + return array +} + +func readMap(context yaml.MapSlice, head string, tail []string) (interface{}, error) { + log.Debugf("readingMap %v with key %v\n", context, head) + if head == "*" { + return readMapSplat(context, tail) + } + + entries := entriesInSlice(context, head) + if len(entries) == 1 { + return calculateValue(entries[0].Value, tail) + } else if len(entries) == 0 { + return nil, nil + } + var errInIdx error + values := make([]interface{}, len(entries)) + for idx, entry := range entries { + values[idx], errInIdx = calculateValue(entry.Value, tail) + if errInIdx != nil { + log.Errorf("Error updating index %v in %v", idx, context) + return nil, errInIdx + } + + } + return values, nil +} + +func readMapSplat(context yaml.MapSlice, tail []string) (interface{}, error) { + var newArray = make([]interface{}, len(context)) + var i = 0 + for _, entry := range context { + if len(tail) > 0 { + val, err := recurse(entry.Value, tail[0], tail[1:]) + if err != nil { + return nil, err + } + newArray[i] = val + } else { + newArray[i] = entry.Value + } + i++ + } + return newArray, nil +} + +func recurse(value interface{}, head string, tail []string) (interface{}, error) { + switch value := value.(type) { + case []interface{}: + if head == "*" { + return readArraySplat(value, tail) + } + index, err := strconv.ParseInt(head, 10, 64) + if err != nil { + return nil, fmt.Errorf("error accessing array: %v", err) + } + return readArray(value, index, tail) + case yaml.MapSlice: + return readMap(value, head, tail) + default: + return nil, nil + } +} + +func readArray(array []interface{}, head int64, tail []string) (interface{}, error) { + if head >= int64(len(array)) { + return nil, nil + } + + value := array[head] + return calculateValue(value, tail) +} + +func readArraySplat(array []interface{}, tail []string) (interface{}, error) { + var newArray = make([]interface{}, len(array)) + for index, value := range array { + val, err := calculateValue(value, tail) + if err != nil { + return nil, err + } + newArray[index] = val + } + return newArray, nil +} + +func calculateValue(value interface{}, tail []string) (interface{}, error) { + if len(tail) > 0 { + return recurse(value, tail[0], tail[1:]) + } + return value, nil +} + +func deleteMap(context interface{}, paths []string) (yaml.MapSlice, error) { + log.Debugf("deleteMap for %v for %v\n", paths, context) + + mapSlice := getMapSlice(context) + + if len(paths) == 0 { + return mapSlice, nil + } + + var index int + var child yaml.MapItem + for index, child = range mapSlice { + if matchesKey(paths[0], child.Key) { + log.Debugf("\tMatched [%v] with [%v] at index %v", paths[0], child.Key, index) + var badDelete error + mapSlice, badDelete = deleteEntryInMap(mapSlice, child, index, paths) + if badDelete != nil { + return nil, badDelete + } + } + } + + return mapSlice, nil + +} + +func deleteEntryInMap(original yaml.MapSlice, child yaml.MapItem, index int, paths []string) (yaml.MapSlice, error) { + remainingPaths := paths[1:] + + var newSlice yaml.MapSlice + if len(remainingPaths) > 0 { + newChild := yaml.MapItem{Key: child.Key} + var errorDeleting error + newChild.Value, errorDeleting = deleteChildValue(child.Value, remainingPaths) + if errorDeleting != nil { + return nil, errorDeleting + } + + newSlice = make(yaml.MapSlice, len(original)) + for i := range original { + item := original[i] + if i == index { + item = newChild + } + newSlice[i] = item + } + } else { + // Delete item from slice at index + newSlice = append(original[:index], original[index+1:]...) + log.Debugf("\tDeleted item index %d from original", index) + } + + log.Debugf("\tReturning original %v\n", original) + return newSlice, nil +} + +func deleteArraySplat(array []interface{}, tail []string) (interface{}, error) { + log.Debugf("deleteArraySplat for %v for %v\n", tail, array) + var newArray = make([]interface{}, len(array)) + for index, value := range array { + val, err := deleteChildValue(value, tail) + if err != nil { + return nil, err + } + newArray[index] = val + } + return newArray, nil +} + +func deleteArray(array []interface{}, paths []string, index int64) (interface{}, error) { + log.Debugf("deleteArray for %v for %v\n", paths, array) + + if index >= int64(len(array)) { + return array, nil + } + + remainingPaths := paths[1:] + if len(remainingPaths) > 0 { + // Recurse into the array element at index + var errorDeleting error + array[index], errorDeleting = deleteMap(array[index], remainingPaths) + if errorDeleting != nil { + return nil, errorDeleting + } + + } else { + // Delete the array element at index + array = append(array[:index], array[index+1:]...) + log.Debugf("\tDeleted item index %d from array, leaving %v", index, array) + } + + log.Debugf("\tReturning array: %v\n", array) + return array, nil +} + +func deleteChildValue(child interface{}, remainingPaths []string) (interface{}, error) { + log.Debugf("deleteChildValue for %v for %v\n", remainingPaths, child) + var head = remainingPaths[0] + var tail = remainingPaths[1:] + switch child := child.(type) { + case yaml.MapSlice: + return deleteMap(child, remainingPaths) + case []interface{}: + if head == "*" { + return deleteArraySplat(child, tail) + } + index, err := strconv.ParseInt(head, 10, 64) + if err != nil { + return nil, fmt.Errorf("error accessing array: %v", err) + } + return deleteArray(child, remainingPaths, index) + } + return child, nil +} diff --git a/vendor/github.com/mikefarah/yq/v2/go.mod b/vendor/github.com/mikefarah/yq/v2/go.mod new file mode 100644 index 0000000000..0b008769e5 --- /dev/null +++ b/vendor/github.com/mikefarah/yq/v2/go.mod @@ -0,0 +1,12 @@ +module github.com/mikefarah/yq/v2 + +require ( + github.com/mikefarah/yaml/v2 v2.4.0 + github.com/pkg/errors v0.8.1 + github.com/spf13/cobra v0.0.5 + golang.org/x/tools v0.0.0-20191030203535-5e247c9ad0a0 // indirect + gopkg.in/imdario/mergo.v0 v0.3.7 + gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 +) + +go 1.13 diff --git a/vendor/github.com/mikefarah/yq/v2/go.sum b/vendor/github.com/mikefarah/yq/v2/go.sum new file mode 100644 index 0000000000..40ff65e55b --- /dev/null +++ b/vendor/github.com/mikefarah/yq/v2/go.sum @@ -0,0 +1,50 @@ +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mikefarah/yaml/v2 v2.4.0 h1:eYqfooY0BnvKTJxr7+ABJs13n3dg9n347GScDaU2Lww= +github.com/mikefarah/yaml/v2 v2.4.0/go.mod h1:ahVqZF4n1W4NqwvVnZzC4es67xsW9uR/RRf2RRxieJU= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20191030203535-5e247c9ad0a0 h1:s5lp4ug7qHzUccgyFdjsX7OZDzHXRaePrF3B3vmUiuM= +golang.org/x/tools v0.0.0-20191030203535-5e247c9ad0a0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/imdario/mergo.v0 v0.3.7 h1:QDotlIZtaO/p+Um0ok18HRTpq5i5/SAk/qprsor+9c8= +gopkg.in/imdario/mergo.v0 v0.3.7/go.mod h1:9qPP6AGrlC1G2PTNXko614FwGZvorN7MiBU0Eppok+U= +gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 h1:6D+BvnJ/j6e222UW8s2qTSe3wGBtvo0MbVQG/c5k8RE= +gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473/go.mod h1:N1eN2tsCx0Ydtgjl4cqmbRCsY4/+z4cYDeqwZTk6zog= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/mikefarah/yq/v2/json_converter.go b/vendor/github.com/mikefarah/yq/v2/json_converter.go new file mode 100644 index 0000000000..10e152b9da --- /dev/null +++ b/vendor/github.com/mikefarah/yq/v2/json_converter.go @@ -0,0 +1,44 @@ +package main + +import ( + "encoding/json" + "fmt" + "strconv" + + yaml "github.com/mikefarah/yaml/v2" +) + +func jsonToString(context interface{}) (string, error) { + out, err := json.Marshal(toJSON(context)) + if err != nil { + return "", fmt.Errorf("error printing yaml as json: %v", err) + } + return string(out), nil +} + +func toJSON(context interface{}) interface{} { + switch context := context.(type) { + case []interface{}: + oldArray := context + newArray := make([]interface{}, len(oldArray)) + for index, value := range oldArray { + newArray[index] = toJSON(value) + } + return newArray + case yaml.MapSlice: + oldMap := context + newMap := make(map[string]interface{}) + for _, entry := range oldMap { + if str, ok := entry.Key.(string); ok { + newMap[str] = toJSON(entry.Value) + } else if i, ok := entry.Key.(int); ok { + newMap[strconv.Itoa(i)] = toJSON(entry.Value) + } else if b, ok := entry.Key.(bool); ok { + newMap[strconv.FormatBool(b)] = toJSON(entry.Value) + } + } + return newMap + default: + return context + } +} diff --git a/vendor/github.com/mikefarah/yq/v2/merge.go b/vendor/github.com/mikefarah/yq/v2/merge.go new file mode 100644 index 0000000000..e80bce3647 --- /dev/null +++ b/vendor/github.com/mikefarah/yq/v2/merge.go @@ -0,0 +1,12 @@ +package main + +import mergo "gopkg.in/imdario/mergo.v0" + +func merge(dst interface{}, src interface{}, overwrite bool, append bool) error { + if overwrite { + return mergo.Merge(dst, src, mergo.WithOverride) + } else if append { + return mergo.Merge(dst, src, mergo.WithAppendSlice) + } + return mergo.Merge(dst, src) +} diff --git a/vendor/github.com/mikefarah/yq/v2/mkdocs.yml b/vendor/github.com/mikefarah/yq/v2/mkdocs.yml new file mode 100644 index 0000000000..99164cbf5f --- /dev/null +++ b/vendor/github.com/mikefarah/yq/v2/mkdocs.yml @@ -0,0 +1,28 @@ +docs_dir: mkdocs +site_dir: docs +site_name: Yq +theme: 'material' +pages: + - Install: index.md + - Read: read.md + - Write/Update: write.md + - Prefix: prefix.md + - Delete: delete.md + - Create: create.md + - Convert: convert.md + - Merge: merge.md +repo_name: 'mikefarah/yq' +repo_url: 'https://github.com/mikefarah/yq' + +extra: + social: + - type: 'github' + link: 'https://github.com/mikefarah' + - type: 'linkedin' + link: 'https://www.linkedin.com/in/mike-farah-b5a75b2/' + +markdown_extensions: + - markdown_include.include: + base_path: mkdocs + - toc: + permalink: True diff --git a/vendor/github.com/mikefarah/yq/v2/path_parser.go b/vendor/github.com/mikefarah/yq/v2/path_parser.go new file mode 100644 index 0000000000..0eed4a49d4 --- /dev/null +++ b/vendor/github.com/mikefarah/yq/v2/path_parser.go @@ -0,0 +1,55 @@ +package main + +func parsePath(path string) []string { + return parsePathAccum([]string{}, path) +} + +func parsePathAccum(paths []string, remaining string) []string { + head, tail := nextYamlPath(remaining) + if tail == "" { + return append(paths, head) + } + return parsePathAccum(append(paths, head), tail) +} + +func nextYamlPath(path string) (pathElement string, remaining string) { + switch path[0] { + case '[': + // e.g [0].blah.cat -> we need to return "0" and "blah.cat" + return search(path[1:], []uint8{']'}, true) + case '"': + // e.g "a.b".blah.cat -> we need to return "a.b" and "blah.cat" + return search(path[1:], []uint8{'"'}, true) + default: + // e.g "a.blah.cat" -> return "a" and "blah.cat" + return search(path[0:], []uint8{'.', '['}, false) + } +} + +func search(path string, matchingChars []uint8, skipNext bool) (pathElement string, remaining string) { + for i := 0; i < len(path); i++ { + var char = path[i] + if contains(matchingChars, char) { + var remainingStart = i + 1 + if skipNext { + remainingStart = remainingStart + 1 + } else if !skipNext && char != '.' { + remainingStart = i + } + if remainingStart > len(path) { + remainingStart = len(path) + } + return path[0:i], path[remainingStart:] + } + } + return path, "" +} + +func contains(matchingChars []uint8, candidate uint8) bool { + for _, a := range matchingChars { + if a == candidate { + return true + } + } + return false +} diff --git a/vendor/github.com/mikefarah/yq/v2/release_instructions.txt b/vendor/github.com/mikefarah/yq/v2/release_instructions.txt new file mode 100644 index 0000000000..07b005f7d5 --- /dev/null +++ b/vendor/github.com/mikefarah/yq/v2/release_instructions.txt @@ -0,0 +1,46 @@ +- increment version in version.go +- increment version in snapcraft.yaml +- commit +- tag git with same version number + - be sure to also tag with 'v' for gopkg.in +- make sure local build passes +- push tag to git + - git push --tags +- make local xcompile (builds binaries for all platforms) + +- git release + ./scripts/publish.sh + +- snapcraft + - will auto create a candidate, test it works then promote + - see https://build.snapcraft.io/user/mikefarah/yq + + sudo snap remove yq + sudo snap install --edge yq + + then use the UI (https://snapcraft.io/yq/release) + +- go get + - update the readme instructions + +- brew + - brew bump-formula-pr --url=https://github.com/mikefarah/yq/archive/2.2.0.tar.gz yq + - if that fails with random ruby errors try: + - clearing out the gems rm -rf .gem/ruby/2.3.0 + - export HOMEBREW_FORCE_VENDOR_RUBY=1 + +- docker + - build and push latest and new version tag + - docker build . -t mikefarah/yq:latest -t mikefarah/yq:VERSION + +- debian package + - execute + ```dch -i``` + - fill debian/changelog with changes from last version + - build the package sources + ```debuild -i -I -S -sa``` + (signing with gpg key is required in order to put it to ppa) + - put to PPA + ```dput ppa: ../yq__source.changes``` + (current distro repository is ppa:rmescandon/yq. In case that a new version + is released, please contact rmescandon@gmail.com to bump debian package) \ No newline at end of file diff --git a/vendor/github.com/mikefarah/yq/v2/test.yml b/vendor/github.com/mikefarah/yq/v2/test.yml new file mode 100644 index 0000000000..71f1098c66 --- /dev/null +++ b/vendor/github.com/mikefarah/yq/v2/test.yml @@ -0,0 +1,2 @@ +a: apple +c: cat diff --git a/vendor/github.com/mikefarah/yq/v2/version.go b/vendor/github.com/mikefarah/yq/v2/version.go new file mode 100644 index 0000000000..eca405f09a --- /dev/null +++ b/vendor/github.com/mikefarah/yq/v2/version.go @@ -0,0 +1,49 @@ +package main + +import ( + "fmt" + "strings" +) + +// The git commit that was compiled. This will be filled in by the compiler. +var ( + GitCommit string + GitDescribe string + + // Version is main version number that is being run at the moment. + Version = "2.4.1" + + // VersionPrerelease is a pre-release marker for the version. If this is "" (empty string) + // then it means that it is a final release. Otherwise, this is a pre-release + // such as "dev" (in development), "beta", "rc1", etc. + VersionPrerelease = "" +) + +// ProductName is the name of the product +const ProductName = "yq" + +// GetVersionDisplay composes the parts of the version in a way that's suitable +// for displaying to humans. +func GetVersionDisplay() string { + return fmt.Sprintf("%s version %s\n", ProductName, getHumanVersion()) +} + +func getHumanVersion() string { + version := Version + if GitDescribe != "" { + version = GitDescribe + } + + release := VersionPrerelease + if release != "" { + if !strings.Contains(version, release) { + version += fmt.Sprintf("-%s", release) + } + if GitCommit != "" { + version += fmt.Sprintf(" (%s)", GitCommit) + } + } + + // Strip off any single quotes added by the git information. + return strings.Replace(version, "'", "", -1) +} diff --git a/vendor/github.com/mikefarah/yq/v2/yq.go b/vendor/github.com/mikefarah/yq/v2/yq.go new file mode 100644 index 0000000000..c59128f1ed --- /dev/null +++ b/vendor/github.com/mikefarah/yq/v2/yq.go @@ -0,0 +1,709 @@ +package main + +import ( + "bufio" + "fmt" + "io" + "io/ioutil" + "os" + "reflect" + "strconv" + "strings" + + errors "github.com/pkg/errors" + + yaml "github.com/mikefarah/yaml/v2" + "github.com/spf13/cobra" + logging "gopkg.in/op/go-logging.v1" +) + +var trimOutput = true +var writeInplace = false +var writeScript = "" +var outputToJSON = false +var overwriteFlag = false +var allowEmptyFlag = false +var appendFlag = false +var verbose = false +var version = false +var docIndex = "0" +var log = logging.MustGetLogger("yq") + +func main() { + cmd := newCommandCLI() + if err := cmd.Execute(); err != nil { + log.Error(err.Error()) + os.Exit(1) + } +} + +func newCommandCLI() *cobra.Command { + yaml.DefaultMapType = reflect.TypeOf(yaml.MapSlice{}) + var rootCmd = &cobra.Command{ + Use: "yq", + Short: "yq is a lightweight and portable command-line YAML processor.", + Long: `yq is a lightweight and portable command-line YAML processor. It aims to be the jq or sed of yaml files.`, + RunE: func(cmd *cobra.Command, args []string) error { + if version { + cmd.Print(GetVersionDisplay()) + return nil + } + cmd.Println(cmd.UsageString()) + + return nil + }, + PersistentPreRun: func(cmd *cobra.Command, args []string) { + var format = logging.MustStringFormatter( + `%{color}%{time:15:04:05} %{shortfunc} [%{level:.4s}]%{color:reset} %{message}`, + ) + var backend = logging.AddModuleLevel( + logging.NewBackendFormatter(logging.NewLogBackend(os.Stderr, "", 0), format)) + + if verbose { + backend.SetLevel(logging.DEBUG, "") + } else { + backend.SetLevel(logging.ERROR, "") + } + + logging.SetBackend(backend) + }, + } + + rootCmd.PersistentFlags().BoolVarP(&trimOutput, "trim", "t", true, "trim yaml output") + rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose mode") + rootCmd.Flags().BoolVarP(&version, "version", "V", false, "Print version information and quit") + + rootCmd.AddCommand( + createReadCmd(), + createWriteCmd(), + createPrefixCmd(), + createDeleteCmd(), + createNewCmd(), + createMergeCmd(), + ) + rootCmd.SetOutput(os.Stdout) + + return rootCmd +} + +func createReadCmd() *cobra.Command { + var cmdRead = &cobra.Command{ + Use: "read [yaml_file] [path]", + Aliases: []string{"r"}, + Short: "yq r [--doc/-d index] sample.yaml a.b.c", + Example: ` +yq read things.yaml a.b.c +yq r - a.b.c (reads from stdin) +yq r things.yaml a.*.c +yq r -d1 things.yaml a.array[0].blah +yq r things.yaml a.array[*].blah +yq r -- things.yaml --key-starting-with-dashes + `, + Long: "Outputs the value of the given path in the yaml file to STDOUT", + RunE: readProperty, + } + cmdRead.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)") + cmdRead.PersistentFlags().BoolVarP(&outputToJSON, "tojson", "j", false, "output as json") + return cmdRead +} + +func createWriteCmd() *cobra.Command { + var cmdWrite = &cobra.Command{ + Use: "write [yaml_file] [path] [value]", + Aliases: []string{"w"}, + Short: "yq w [--inplace/-i] [--script/-s script_file] [--doc/-d index] sample.yaml a.b.c newValue", + Example: ` +yq write things.yaml a.b.c cat +yq write --inplace -- things.yaml a.b.c --cat +yq w -i things.yaml a.b.c cat +yq w --script update_script.yaml things.yaml +yq w -i -s update_script.yaml things.yaml +yq w --doc 2 things.yaml a.b.d[+] foo +yq w -d2 things.yaml a.b.d[+] foo + `, + Long: `Updates the yaml file w.r.t the given path and value. +Outputs to STDOUT unless the inplace flag is used, in which case the file is updated instead. + +Append value to array adds the value to the end of array. + +Update Scripts: +Note that you can give an update script to perform more sophisticated updated. Update script +format is a yaml map where the key is the path and the value is..well the value. e.g.: +--- +a.b.c: true, +a.b.e: + - name: bob +`, + RunE: writeProperty, + } + cmdWrite.PersistentFlags().BoolVarP(&writeInplace, "inplace", "i", false, "update the yaml file inplace") + cmdWrite.PersistentFlags().StringVarP(&writeScript, "script", "s", "", "yaml script for updating yaml") + cmdWrite.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)") + return cmdWrite +} + +func createPrefixCmd() *cobra.Command { + var cmdWrite = &cobra.Command{ + Use: "prefix [yaml_file] [path]", + Aliases: []string{"p"}, + Short: "yq p [--inplace/-i] [--doc/-d index] sample.yaml a.b.c", + Example: ` +yq prefix things.yaml a.b.c +yq prefix --inplace things.yaml a.b.c +yq prefix --inplace -- things.yaml --key-starting-with-dash +yq p -i things.yaml a.b.c +yq p --doc 2 things.yaml a.b.d +yq p -d2 things.yaml a.b.d + `, + Long: `Prefixes w.r.t to the yaml file at the given path. +Outputs to STDOUT unless the inplace flag is used, in which case the file is updated instead. +`, + RunE: prefixProperty, + } + cmdWrite.PersistentFlags().BoolVarP(&writeInplace, "inplace", "i", false, "update the yaml file inplace") + cmdWrite.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)") + return cmdWrite +} + +func createDeleteCmd() *cobra.Command { + var cmdDelete = &cobra.Command{ + Use: "delete [yaml_file] [path]", + Aliases: []string{"d"}, + Short: "yq d [--inplace/-i] [--doc/-d index] sample.yaml a.b.c", + Example: ` +yq delete things.yaml a.b.c +yq delete --inplace things.yaml a.b.c +yq delete --inplace -- things.yaml --key-starting-with-dash +yq d -i things.yaml a.b.c +yq d things.yaml a.b.c + `, + Long: `Deletes the given path from the YAML file. +Outputs to STDOUT unless the inplace flag is used, in which case the file is updated instead. +`, + RunE: deleteProperty, + } + cmdDelete.PersistentFlags().BoolVarP(&writeInplace, "inplace", "i", false, "update the yaml file inplace") + cmdDelete.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)") + return cmdDelete +} + +func createNewCmd() *cobra.Command { + var cmdNew = &cobra.Command{ + Use: "new [path] [value]", + Aliases: []string{"n"}, + Short: "yq n [--script/-s script_file] a.b.c newValue", + Example: ` +yq new a.b.c cat +yq n a.b.c cat +yq n -- --key-starting-with-dash cat +yq n --script create_script.yaml + `, + Long: `Creates a new yaml w.r.t the given path and value. +Outputs to STDOUT + +Create Scripts: +Note that you can give a create script to perform more sophisticated yaml. This follows the same format as the update script. +`, + RunE: newProperty, + } + cmdNew.PersistentFlags().StringVarP(&writeScript, "script", "s", "", "yaml script for updating yaml") + return cmdNew +} + +func createMergeCmd() *cobra.Command { + var cmdMerge = &cobra.Command{ + Use: "merge [initial_yaml_file] [additional_yaml_file]...", + Aliases: []string{"m"}, + Short: "yq m [--inplace/-i] [--doc/-d index] [--overwrite/-x] [--append/-a] sample.yaml sample2.yaml", + Example: ` +yq merge things.yaml other.yaml +yq merge --inplace things.yaml other.yaml +yq m -i things.yaml other.yaml +yq m --overwrite things.yaml other.yaml +yq m -i -x things.yaml other.yaml +yq m -i -a things.yaml other.yaml + `, + Long: `Updates the yaml file by adding/updating the path(s) and value(s) from additional yaml file(s). +Outputs to STDOUT unless the inplace flag is used, in which case the file is updated instead. + +If overwrite flag is set then existing values will be overwritten using the values from each additional yaml file. +If append flag is set then existing arrays will be merged with the arrays from each additional yaml file. + +Note that if you set both flags only overwrite will take effect. +`, + RunE: mergeProperties, + } + cmdMerge.PersistentFlags().BoolVarP(&writeInplace, "inplace", "i", false, "update the yaml file inplace") + cmdMerge.PersistentFlags().BoolVarP(&overwriteFlag, "overwrite", "x", false, "update the yaml file by overwriting existing values") + cmdMerge.PersistentFlags().BoolVarP(&appendFlag, "append", "a", false, "update the yaml file by appending array values") + cmdMerge.PersistentFlags().BoolVarP(&allowEmptyFlag, "allow-empty", "e", false, "allow empty yaml files") + cmdMerge.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)") + return cmdMerge +} + +func readProperty(cmd *cobra.Command, args []string) error { + var path = "" + + if len(args) < 1 { + return errors.New("Must provide filename") + } else if len(args) > 1 { + path = args[1] + } + + var updateAll, docIndexInt, errorParsingDocIndex = parseDocumentIndex() + if errorParsingDocIndex != nil { + return errorParsingDocIndex + } + var mappedDocs []interface{} + var dataBucket interface{} + var currentIndex = 0 + var errorReadingStream = readStream(args[0], func(decoder *yaml.Decoder) error { + for { + errorReading := decoder.Decode(&dataBucket) + if errorReading == io.EOF { + log.Debugf("done %v / %v", currentIndex, docIndexInt) + if !updateAll && currentIndex <= docIndexInt { + return fmt.Errorf("asked to process document index %v but there are only %v document(s)", docIndex, currentIndex) + } + return nil + } + log.Debugf("processing %v - requested index %v", currentIndex, docIndexInt) + if updateAll || currentIndex == docIndexInt { + log.Debugf("reading %v in index %v", path, currentIndex) + mappedDoc, errorParsing := readPath(dataBucket, path) + log.Debugf("%v", mappedDoc) + if errorParsing != nil { + return errors.Wrapf(errorParsing, "Error reading path in document index %v", currentIndex) + } + mappedDocs = append(mappedDocs, mappedDoc) + } + currentIndex = currentIndex + 1 + } + }) + + if errorReadingStream != nil { + return errorReadingStream + } + + if !updateAll { + dataBucket = mappedDocs[0] + } else { + dataBucket = mappedDocs + } + + dataStr, err := toString(dataBucket) + if err != nil { + return err + } + cmd.Println(dataStr) + return nil +} + +func readPath(dataBucket interface{}, path string) (interface{}, error) { + if path == "" { + log.Debug("no path") + return dataBucket, nil + } + var paths = parsePath(path) + return recurse(dataBucket, paths[0], paths[1:]) +} + +func newProperty(cmd *cobra.Command, args []string) error { + updatedData, err := newYaml(args) + if err != nil { + return err + } + dataStr, err := toString(updatedData) + if err != nil { + return err + } + cmd.Println(dataStr) + return nil +} + +func newYaml(args []string) (interface{}, error) { + var writeCommands, writeCommandsError = readWriteCommands(args, 2, "Must provide ") + if writeCommandsError != nil { + return nil, writeCommandsError + } + + var dataBucket interface{} + var isArray = strings.HasPrefix(writeCommands[0].Key.(string), "[") + if isArray { + dataBucket = make([]interface{}, 0) + } else { + dataBucket = make(yaml.MapSlice, 0) + } + + for _, entry := range writeCommands { + path := entry.Key.(string) + value := entry.Value + log.Debugf("setting %v to %v", path, value) + var paths = parsePath(path) + dataBucket = updatedChildValue(dataBucket, paths, value) + } + + return dataBucket, nil +} + +func parseDocumentIndex() (bool, int, error) { + if docIndex == "*" { + return true, -1, nil + } + docIndexInt64, err := strconv.ParseInt(docIndex, 10, 32) + if err != nil { + return false, -1, errors.Wrapf(err, "Document index %v is not a integer or *", docIndex) + } + return false, int(docIndexInt64), nil +} + +type updateDataFn func(dataBucket interface{}, currentIndex int) (interface{}, error) + +func mapYamlDecoder(updateData updateDataFn, encoder *yaml.Encoder) yamlDecoderFn { + return func(decoder *yaml.Decoder) error { + var dataBucket interface{} + var errorReading error + var errorWriting error + var errorUpdating error + var currentIndex = 0 + + var updateAll, docIndexInt, errorParsingDocIndex = parseDocumentIndex() + if errorParsingDocIndex != nil { + return errorParsingDocIndex + } + + for { + log.Debugf("Read doc %v", currentIndex) + errorReading = decoder.Decode(&dataBucket) + + if errorReading == io.EOF { + if !updateAll && currentIndex <= docIndexInt { + return fmt.Errorf("asked to process document index %v but there are only %v document(s)", docIndex, currentIndex) + } + return nil + } else if errorReading != nil { + return errors.Wrapf(errorReading, "Error reading document at index %v, %v", currentIndex, errorReading) + } + dataBucket, errorUpdating = updateData(dataBucket, currentIndex) + if errorUpdating != nil { + return errors.Wrapf(errorUpdating, "Error updating document at index %v", currentIndex) + } + + errorWriting = encoder.Encode(dataBucket) + + if errorWriting != nil { + return errors.Wrapf(errorWriting, "Error writing document at index %v, %v", currentIndex, errorWriting) + } + currentIndex = currentIndex + 1 + } + } +} + +func writeProperty(cmd *cobra.Command, args []string) error { + var writeCommands, writeCommandsError = readWriteCommands(args, 3, "Must provide ") + if writeCommandsError != nil { + return writeCommandsError + } + var updateAll, docIndexInt, errorParsingDocIndex = parseDocumentIndex() + if errorParsingDocIndex != nil { + return errorParsingDocIndex + } + + var updateData = func(dataBucket interface{}, currentIndex int) (interface{}, error) { + if updateAll || currentIndex == docIndexInt { + log.Debugf("Updating doc %v", currentIndex) + for _, entry := range writeCommands { + path := entry.Key.(string) + value := entry.Value + log.Debugf("setting %v to %v", path, value) + var paths = parsePath(path) + dataBucket = updatedChildValue(dataBucket, paths, value) + } + } + return dataBucket, nil + } + return readAndUpdate(cmd.OutOrStdout(), args[0], updateData) +} + +func prefixProperty(cmd *cobra.Command, args []string) error { + if len(args) != 2 { + return errors.New("Must provide ") + } + var updateAll, docIndexInt, errorParsingDocIndex = parseDocumentIndex() + if errorParsingDocIndex != nil { + return errorParsingDocIndex + } + + var paths = parsePath(args[1]) + + // Inverse order + for i := len(paths)/2 - 1; i >= 0; i-- { + opp := len(paths) - 1 - i + paths[i], paths[opp] = paths[opp], paths[i] + } + + var updateData = func(dataBucket interface{}, currentIndex int) (interface{}, error) { + + if updateAll || currentIndex == docIndexInt { + log.Debugf("Prefixing %v to doc %v", paths, currentIndex) + var mapDataBucket = dataBucket + for _, key := range paths { + singlePath := []string{key} + mapDataBucket = updatedChildValue(nil, singlePath, mapDataBucket) + } + return mapDataBucket, nil + } + return dataBucket, nil + } + return readAndUpdate(cmd.OutOrStdout(), args[0], updateData) +} + +func readAndUpdate(stdOut io.Writer, inputFile string, updateData updateDataFn) error { + var destination io.Writer + var destinationName string + if writeInplace { + info, err := os.Stat(inputFile) + if err != nil { + return err + } + tempFile, err := ioutil.TempFile("", "temp") + if err != nil { + return err + } + destinationName = tempFile.Name() + err = os.Chmod(destinationName, info.Mode()) + if err != nil { + return err + } + destination = tempFile + defer func() { + safelyCloseFile(tempFile) + safelyRenameFile(tempFile.Name(), inputFile) + }() + } else { + var writer = bufio.NewWriter(stdOut) + destination = writer + destinationName = "Stdout" + defer safelyFlush(writer) + } + var encoder = yaml.NewEncoder(destination) + log.Debugf("Writing to %v from %v", destinationName, inputFile) + return readStream(inputFile, mapYamlDecoder(updateData, encoder)) +} + +func deleteProperty(cmd *cobra.Command, args []string) error { + if len(args) < 2 { + return errors.New("Must provide ") + } + var deletePath = args[1] + var paths = parsePath(deletePath) + var updateAll, docIndexInt, errorParsingDocIndex = parseDocumentIndex() + if errorParsingDocIndex != nil { + return errorParsingDocIndex + } + + var updateData = func(dataBucket interface{}, currentIndex int) (interface{}, error) { + if updateAll || currentIndex == docIndexInt { + log.Debugf("Deleting path in doc %v", currentIndex) + return deleteChildValue(dataBucket, paths) + } + return dataBucket, nil + } + + return readAndUpdate(cmd.OutOrStdout(), args[0], updateData) +} + +func mergeProperties(cmd *cobra.Command, args []string) error { + if len(args) < 2 { + return errors.New("Must provide at least 2 yaml files") + } + var input = args[0] + var filesToMerge = args[1:] + var updateAll, docIndexInt, errorParsingDocIndex = parseDocumentIndex() + if errorParsingDocIndex != nil { + return errorParsingDocIndex + } + + var updateData = func(dataBucket interface{}, currentIndex int) (interface{}, error) { + if updateAll || currentIndex == docIndexInt { + log.Debugf("Merging doc %v", currentIndex) + var mergedData map[interface{}]interface{} + // merge only works for maps, so put everything in a temporary + // map + var mapDataBucket = make(map[interface{}]interface{}) + mapDataBucket["root"] = dataBucket + if err := merge(&mergedData, mapDataBucket, overwriteFlag, appendFlag); err != nil { + return nil, err + } + for _, f := range filesToMerge { + var fileToMerge interface{} + if err := readData(f, 0, &fileToMerge); err != nil { + if allowEmptyFlag && err == io.EOF { + continue + } + return nil, err + } + mapDataBucket["root"] = fileToMerge + if err := merge(&mergedData, mapDataBucket, overwriteFlag, appendFlag); err != nil { + return nil, err + } + } + return mergedData["root"], nil + } + return dataBucket, nil + } + yaml.DefaultMapType = reflect.TypeOf(map[interface{}]interface{}{}) + defer func() { yaml.DefaultMapType = reflect.TypeOf(yaml.MapSlice{}) }() + return readAndUpdate(cmd.OutOrStdout(), input, updateData) +} + +func readWriteCommands(args []string, expectedArgs int, badArgsMessage string) (yaml.MapSlice, error) { + var writeCommands yaml.MapSlice + if writeScript != "" { + if err := readData(writeScript, 0, &writeCommands); err != nil { + return nil, err + } + } else if len(args) < expectedArgs { + return nil, errors.New(badArgsMessage) + } else { + writeCommands = make(yaml.MapSlice, 1) + writeCommands[0] = yaml.MapItem{Key: args[expectedArgs-2], Value: parseValue(args[expectedArgs-1])} + } + return writeCommands, nil +} + +func parseValue(argument string) interface{} { + var value, err interface{} + var inQuotes = len(argument) > 0 && argument[0] == '"' + if !inQuotes { + value, err = strconv.ParseFloat(argument, 64) + if err == nil { + return value + } + value, err = strconv.ParseBool(argument) + if err == nil { + return value + } + if argument == "[]" { + return make([]interface{}, 0) + } + return argument + } + return argument[1 : len(argument)-1] +} + +func toString(context interface{}) (string, error) { + if outputToJSON { + return jsonToString(context) + } + return yamlToString(context) +} + +func yamlToString(context interface{}) (string, error) { + switch context := context.(type) { + case string: + return context, nil + default: + return marshalContext(context) + } +} + +func marshalContext(context interface{}) (string, error) { + out, err := yaml.Marshal(context) + + if err != nil { + return "", errors.Wrap(err, "error printing yaml") + } + + outStr := string(out) + // trim the trailing new line as it's easier for a script to add + // it in if required than to remove it + if trimOutput { + return strings.Trim(outStr, "\n "), nil + } + return outStr, nil +} + +func safelyRenameFile(from string, to string) { + if renameError := os.Rename(from, to); renameError != nil { + log.Debugf("Error renaming from %v to %v, attemting to copy contents", from, to) + log.Debug(renameError.Error()) + // can't do this rename when running in docker to a file targeted in a mounted volume, + // so gracefully degrade to copying the entire contents. + if copyError := copyFileContents(from, to); copyError != nil { + log.Errorf("Failed copying from %v to %v", from, to) + log.Error(copyError.Error()) + } else { + removeErr := os.Remove(from) + if removeErr != nil { + log.Errorf("failed removing original file: %s", from) + } + } + } +} + +// thanks https://stackoverflow.com/questions/21060945/simple-way-to-copy-a-file-in-golang +func copyFileContents(src, dst string) (err error) { + in, err := os.Open(src) // nolint gosec + if err != nil { + return err + } + defer safelyCloseFile(in) + out, err := os.Create(dst) + if err != nil { + return err + } + defer safelyCloseFile(out) + if _, err = io.Copy(out, in); err != nil { + return err + } + return out.Sync() +} + +func safelyFlush(writer *bufio.Writer) { + if err := writer.Flush(); err != nil { + log.Error("Error flushing writer!") + log.Error(err.Error()) + } + +} +func safelyCloseFile(file *os.File) { + err := file.Close() + if err != nil { + log.Error("Error closing file!") + log.Error(err.Error()) + } +} + +type yamlDecoderFn func(*yaml.Decoder) error + +func readStream(filename string, yamlDecoder yamlDecoderFn) error { + if filename == "" { + return errors.New("Must provide filename") + } + + var stream io.Reader + if filename == "-" { + stream = bufio.NewReader(os.Stdin) + } else { + file, err := os.Open(filename) // nolint gosec + if err != nil { + return err + } + defer safelyCloseFile(file) + stream = file + } + return yamlDecoder(yaml.NewDecoder(stream)) +} + +func readData(filename string, indexToRead int, parsedData interface{}) error { + return readStream(filename, func(decoder *yaml.Decoder) error { + for currentIndex := 0; currentIndex < indexToRead; currentIndex++ { + errorSkipping := decoder.Decode(parsedData) + if errorSkipping != nil { + return errors.Wrapf(errorSkipping, "Error processing document at index %v, %v", currentIndex, errorSkipping) + } + } + return decoder.Decode(parsedData) + }) +} diff --git a/vendor/golang.org/x/sync/AUTHORS b/vendor/golang.org/x/sync/AUTHORS deleted file mode 100644 index 15167cd746..0000000000 --- a/vendor/golang.org/x/sync/AUTHORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code refers to The Go Authors for copyright purposes. -# The master list of authors is in the main Go distribution, -# visible at http://tip.golang.org/AUTHORS. diff --git a/vendor/golang.org/x/sync/CONTRIBUTORS b/vendor/golang.org/x/sync/CONTRIBUTORS deleted file mode 100644 index 1c4577e968..0000000000 --- a/vendor/golang.org/x/sync/CONTRIBUTORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code was written by the Go contributors. -# The master list of contributors is in the main Go distribution, -# visible at http://tip.golang.org/CONTRIBUTORS. diff --git a/vendor/golang.org/x/sync/PATENTS b/vendor/golang.org/x/sync/PATENTS deleted file mode 100644 index 733099041f..0000000000 --- a/vendor/golang.org/x/sync/PATENTS +++ /dev/null @@ -1,22 +0,0 @@ -Additional IP Rights Grant (Patents) - -"This implementation" means the copyrightable works distributed by -Google as part of the Go project. - -Google hereby grants to You a perpetual, worldwide, non-exclusive, -no-charge, royalty-free, irrevocable (except as stated in this section) -patent license to make, have made, use, offer to sell, sell, import, -transfer and otherwise run, modify and propagate the contents of this -implementation of Go, where such license applies only to those patent -claims, both currently owned or controlled by Google and acquired in -the future, licensable by Google that are necessarily infringed by this -implementation of Go. This grant does not include claims that would be -infringed only as a consequence of further modification of this -implementation. If you or your agent or exclusive licensee institute or -order or agree to the institution of patent litigation against any -entity (including a cross-claim or counterclaim in a lawsuit) alleging -that this implementation of Go or any code incorporated within this -implementation of Go constitutes direct or contributory patent -infringement, or inducement of patent infringement, then any patent -rights granted to you under this License for this implementation of Go -shall terminate as of the date such litigation is filed. diff --git a/vendor/golang.org/x/sync/errgroup/errgroup.go b/vendor/golang.org/x/sync/errgroup/errgroup.go deleted file mode 100644 index 9857fe53d3..0000000000 --- a/vendor/golang.org/x/sync/errgroup/errgroup.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package errgroup provides synchronization, error propagation, and Context -// cancelation for groups of goroutines working on subtasks of a common task. -package errgroup - -import ( - "context" - "sync" -) - -// A Group is a collection of goroutines working on subtasks that are part of -// the same overall task. -// -// A zero Group is valid and does not cancel on error. -type Group struct { - cancel func() - - wg sync.WaitGroup - - errOnce sync.Once - err error -} - -// WithContext returns a new Group and an associated Context derived from ctx. -// -// The derived Context is canceled the first time a function passed to Go -// returns a non-nil error or the first time Wait returns, whichever occurs -// first. -func WithContext(ctx context.Context) (*Group, context.Context) { - ctx, cancel := context.WithCancel(ctx) - return &Group{cancel: cancel}, ctx -} - -// Wait blocks until all function calls from the Go method have returned, then -// returns the first non-nil error (if any) from them. -func (g *Group) Wait() error { - g.wg.Wait() - if g.cancel != nil { - g.cancel() - } - return g.err -} - -// Go calls the given function in a new goroutine. -// -// The first call to return a non-nil error cancels the group; its error will be -// returned by Wait. -func (g *Group) Go(f func() error) { - g.wg.Add(1) - - go func() { - defer g.wg.Done() - - if err := f(); err != nil { - g.errOnce.Do(func() { - g.err = err - if g.cancel != nil { - g.cancel() - } - }) - } - }() -} diff --git a/vendor/golang.org/x/sys/unix/mkasm_darwin.go b/vendor/golang.org/x/sys/unix/mkasm_darwin.go new file mode 100644 index 0000000000..4548b993db --- /dev/null +++ b/vendor/golang.org/x/sys/unix/mkasm_darwin.go @@ -0,0 +1,61 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// mkasm_darwin.go generates assembly trampolines to call libSystem routines from Go. +//This program must be run after mksyscall.go. +package main + +import ( + "bytes" + "fmt" + "io/ioutil" + "log" + "os" + "strings" +) + +func main() { + in1, err := ioutil.ReadFile("syscall_darwin.go") + if err != nil { + log.Fatalf("can't open syscall_darwin.go: %s", err) + } + arch := os.Args[1] + in2, err := ioutil.ReadFile(fmt.Sprintf("syscall_darwin_%s.go", arch)) + if err != nil { + log.Fatalf("can't open syscall_darwin_%s.go: %s", arch, err) + } + in3, err := ioutil.ReadFile(fmt.Sprintf("zsyscall_darwin_%s.go", arch)) + if err != nil { + log.Fatalf("can't open zsyscall_darwin_%s.go: %s", arch, err) + } + in := string(in1) + string(in2) + string(in3) + + trampolines := map[string]bool{} + + var out bytes.Buffer + + fmt.Fprintf(&out, "// go run mkasm_darwin.go %s\n", strings.Join(os.Args[1:], " ")) + fmt.Fprintf(&out, "// Code generated by the command above; DO NOT EDIT.\n") + fmt.Fprintf(&out, "\n") + fmt.Fprintf(&out, "// +build go1.12\n") + fmt.Fprintf(&out, "\n") + fmt.Fprintf(&out, "#include \"textflag.h\"\n") + for _, line := range strings.Split(in, "\n") { + if !strings.HasPrefix(line, "func ") || !strings.HasSuffix(line, "_trampoline()") { + continue + } + fn := line[5 : len(line)-13] + if !trampolines[fn] { + trampolines[fn] = true + fmt.Fprintf(&out, "TEXT ·%s_trampoline(SB),NOSPLIT,$0-0\n", fn) + fmt.Fprintf(&out, "\tJMP\t%s(SB)\n", fn) + } + } + err = ioutil.WriteFile(fmt.Sprintf("zsyscall_darwin_%s.s", arch), out.Bytes(), 0644) + if err != nil { + log.Fatalf("can't write zsyscall_darwin_%s.s: %s", arch, err) + } +} diff --git a/vendor/golang.org/x/sys/unix/mkpost.go b/vendor/golang.org/x/sys/unix/mkpost.go new file mode 100644 index 0000000000..eb4332059a --- /dev/null +++ b/vendor/golang.org/x/sys/unix/mkpost.go @@ -0,0 +1,122 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// mkpost processes the output of cgo -godefs to +// modify the generated types. It is used to clean up +// the sys API in an architecture specific manner. +// +// mkpost is run after cgo -godefs; see README.md. +package main + +import ( + "bytes" + "fmt" + "go/format" + "io/ioutil" + "log" + "os" + "regexp" +) + +func main() { + // Get the OS and architecture (using GOARCH_TARGET if it exists) + goos := os.Getenv("GOOS") + goarch := os.Getenv("GOARCH_TARGET") + if goarch == "" { + goarch = os.Getenv("GOARCH") + } + // Check that we are using the Docker-based build system if we should be. + if goos == "linux" { + if os.Getenv("GOLANG_SYS_BUILD") != "docker" { + os.Stderr.WriteString("In the Docker-based build system, mkpost should not be called directly.\n") + os.Stderr.WriteString("See README.md\n") + os.Exit(1) + } + } + + b, err := ioutil.ReadAll(os.Stdin) + if err != nil { + log.Fatal(err) + } + + if goos == "aix" { + // Replace type of Atim, Mtim and Ctim by Timespec in Stat_t + // to avoid having both StTimespec and Timespec. + sttimespec := regexp.MustCompile(`_Ctype_struct_st_timespec`) + b = sttimespec.ReplaceAll(b, []byte("Timespec")) + } + + // Intentionally export __val fields in Fsid and Sigset_t + valRegex := regexp.MustCompile(`type (Fsid|Sigset_t) struct {(\s+)X__(bits|val)(\s+\S+\s+)}`) + b = valRegex.ReplaceAll(b, []byte("type $1 struct {${2}Val$4}")) + + // Intentionally export __fds_bits field in FdSet + fdSetRegex := regexp.MustCompile(`type (FdSet) struct {(\s+)X__fds_bits(\s+\S+\s+)}`) + b = fdSetRegex.ReplaceAll(b, []byte("type $1 struct {${2}Bits$3}")) + + // If we have empty Ptrace structs, we should delete them. Only s390x emits + // nonempty Ptrace structs. + ptraceRexexp := regexp.MustCompile(`type Ptrace((Psw|Fpregs|Per) struct {\s*})`) + b = ptraceRexexp.ReplaceAll(b, nil) + + // Replace the control_regs union with a blank identifier for now. + controlRegsRegex := regexp.MustCompile(`(Control_regs)\s+\[0\]uint64`) + b = controlRegsRegex.ReplaceAll(b, []byte("_ [0]uint64")) + + // Remove fields that are added by glibc + // Note that this is unstable as the identifers are private. + removeFieldsRegex := regexp.MustCompile(`X__glibc\S*`) + b = removeFieldsRegex.ReplaceAll(b, []byte("_")) + + // Convert [65]int8 to [65]byte in Utsname members to simplify + // conversion to string; see golang.org/issue/20753 + convertUtsnameRegex := regexp.MustCompile(`((Sys|Node|Domain)name|Release|Version|Machine)(\s+)\[(\d+)\]u?int8`) + b = convertUtsnameRegex.ReplaceAll(b, []byte("$1$3[$4]byte")) + + // Convert [1024]int8 to [1024]byte in Ptmget members + convertPtmget := regexp.MustCompile(`([SC]n)(\s+)\[(\d+)\]u?int8`) + b = convertPtmget.ReplaceAll(b, []byte("$1[$3]byte")) + + // Remove spare fields (e.g. in Statx_t) + spareFieldsRegex := regexp.MustCompile(`X__spare\S*`) + b = spareFieldsRegex.ReplaceAll(b, []byte("_")) + + // Remove cgo padding fields + removePaddingFieldsRegex := regexp.MustCompile(`Pad_cgo_\d+`) + b = removePaddingFieldsRegex.ReplaceAll(b, []byte("_")) + + // Remove padding, hidden, or unused fields + removeFieldsRegex = regexp.MustCompile(`\b(X_\S+|Padding)`) + b = removeFieldsRegex.ReplaceAll(b, []byte("_")) + + // Remove the first line of warning from cgo + b = b[bytes.IndexByte(b, '\n')+1:] + // Modify the command in the header to include: + // mkpost, our own warning, and a build tag. + replacement := fmt.Sprintf(`$1 | go run mkpost.go +// Code generated by the command above; see README.md. DO NOT EDIT. + +// +build %s,%s`, goarch, goos) + cgoCommandRegex := regexp.MustCompile(`(cgo -godefs .*)`) + b = cgoCommandRegex.ReplaceAll(b, []byte(replacement)) + + // Rename Stat_t time fields + if goos == "freebsd" && goarch == "386" { + // Hide Stat_t.[AMCB]tim_ext fields + renameStatTimeExtFieldsRegex := regexp.MustCompile(`[AMCB]tim_ext`) + b = renameStatTimeExtFieldsRegex.ReplaceAll(b, []byte("_")) + } + renameStatTimeFieldsRegex := regexp.MustCompile(`([AMCB])(?:irth)?time?(?:spec)?\s+(Timespec|StTimespec)`) + b = renameStatTimeFieldsRegex.ReplaceAll(b, []byte("${1}tim ${2}")) + + // gofmt + b, err = format.Source(b) + if err != nil { + log.Fatal(err) + } + + os.Stdout.Write(b) +} diff --git a/vendor/golang.org/x/sys/unix/mksyscall.go b/vendor/golang.org/x/sys/unix/mksyscall.go new file mode 100644 index 0000000000..e4af9424e9 --- /dev/null +++ b/vendor/golang.org/x/sys/unix/mksyscall.go @@ -0,0 +1,407 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +/* +This program reads a file containing function prototypes +(like syscall_darwin.go) and generates system call bodies. +The prototypes are marked by lines beginning with "//sys" +and read like func declarations if //sys is replaced by func, but: + * The parameter lists must give a name for each argument. + This includes return parameters. + * The parameter lists must give a type for each argument: + the (x, y, z int) shorthand is not allowed. + * If the return parameter is an error number, it must be named errno. + +A line beginning with //sysnb is like //sys, except that the +goroutine will not be suspended during the execution of the system +call. This must only be used for system calls which can never +block, as otherwise the system call could cause all goroutines to +hang. +*/ +package main + +import ( + "bufio" + "flag" + "fmt" + "os" + "regexp" + "strings" +) + +var ( + b32 = flag.Bool("b32", false, "32bit big-endian") + l32 = flag.Bool("l32", false, "32bit little-endian") + plan9 = flag.Bool("plan9", false, "plan9") + openbsd = flag.Bool("openbsd", false, "openbsd") + netbsd = flag.Bool("netbsd", false, "netbsd") + dragonfly = flag.Bool("dragonfly", false, "dragonfly") + arm = flag.Bool("arm", false, "arm") // 64-bit value should use (even, odd)-pair + tags = flag.String("tags", "", "build tags") + filename = flag.String("output", "", "output file name (standard output if omitted)") +) + +// cmdLine returns this programs's commandline arguments +func cmdLine() string { + return "go run mksyscall.go " + strings.Join(os.Args[1:], " ") +} + +// buildTags returns build tags +func buildTags() string { + return *tags +} + +// Param is function parameter +type Param struct { + Name string + Type string +} + +// usage prints the program usage +func usage() { + fmt.Fprintf(os.Stderr, "usage: go run mksyscall.go [-b32 | -l32] [-tags x,y] [file ...]\n") + os.Exit(1) +} + +// parseParamList parses parameter list and returns a slice of parameters +func parseParamList(list string) []string { + list = strings.TrimSpace(list) + if list == "" { + return []string{} + } + return regexp.MustCompile(`\s*,\s*`).Split(list, -1) +} + +// parseParam splits a parameter into name and type +func parseParam(p string) Param { + ps := regexp.MustCompile(`^(\S*) (\S*)$`).FindStringSubmatch(p) + if ps == nil { + fmt.Fprintf(os.Stderr, "malformed parameter: %s\n", p) + os.Exit(1) + } + return Param{ps[1], ps[2]} +} + +func main() { + // Get the OS and architecture (using GOARCH_TARGET if it exists) + goos := os.Getenv("GOOS") + if goos == "" { + fmt.Fprintln(os.Stderr, "GOOS not defined in environment") + os.Exit(1) + } + goarch := os.Getenv("GOARCH_TARGET") + if goarch == "" { + goarch = os.Getenv("GOARCH") + } + + // Check that we are using the Docker-based build system if we should + if goos == "linux" { + if os.Getenv("GOLANG_SYS_BUILD") != "docker" { + fmt.Fprintf(os.Stderr, "In the Docker-based build system, mksyscall should not be called directly.\n") + fmt.Fprintf(os.Stderr, "See README.md\n") + os.Exit(1) + } + } + + flag.Usage = usage + flag.Parse() + if len(flag.Args()) <= 0 { + fmt.Fprintf(os.Stderr, "no files to parse provided\n") + usage() + } + + endianness := "" + if *b32 { + endianness = "big-endian" + } else if *l32 { + endianness = "little-endian" + } + + libc := false + if goos == "darwin" && strings.Contains(buildTags(), ",go1.12") { + libc = true + } + trampolines := map[string]bool{} + + text := "" + for _, path := range flag.Args() { + file, err := os.Open(path) + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + s := bufio.NewScanner(file) + for s.Scan() { + t := s.Text() + t = strings.TrimSpace(t) + t = regexp.MustCompile(`\s+`).ReplaceAllString(t, ` `) + nonblock := regexp.MustCompile(`^\/\/sysnb `).FindStringSubmatch(t) + if regexp.MustCompile(`^\/\/sys `).FindStringSubmatch(t) == nil && nonblock == nil { + continue + } + + // Line must be of the form + // func Open(path string, mode int, perm int) (fd int, errno error) + // Split into name, in params, out params. + f := regexp.MustCompile(`^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*((?i)SYS_[A-Z0-9_]+))?$`).FindStringSubmatch(t) + if f == nil { + fmt.Fprintf(os.Stderr, "%s:%s\nmalformed //sys declaration\n", path, t) + os.Exit(1) + } + funct, inps, outps, sysname := f[2], f[3], f[4], f[5] + + // ClockGettime doesn't have a syscall number on Darwin, only generate libc wrappers. + if goos == "darwin" && !libc && funct == "ClockGettime" { + continue + } + + // Split argument lists on comma. + in := parseParamList(inps) + out := parseParamList(outps) + + // Try in vain to keep people from editing this file. + // The theory is that they jump into the middle of the file + // without reading the header. + text += "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n" + + // Go function header. + outDecl := "" + if len(out) > 0 { + outDecl = fmt.Sprintf(" (%s)", strings.Join(out, ", ")) + } + text += fmt.Sprintf("func %s(%s)%s {\n", funct, strings.Join(in, ", "), outDecl) + + // Check if err return available + errvar := "" + for _, param := range out { + p := parseParam(param) + if p.Type == "error" { + errvar = p.Name + break + } + } + + // Prepare arguments to Syscall. + var args []string + n := 0 + for _, param := range in { + p := parseParam(param) + if regexp.MustCompile(`^\*`).FindStringSubmatch(p.Type) != nil { + args = append(args, "uintptr(unsafe.Pointer("+p.Name+"))") + } else if p.Type == "string" && errvar != "" { + text += fmt.Sprintf("\tvar _p%d *byte\n", n) + text += fmt.Sprintf("\t_p%d, %s = BytePtrFromString(%s)\n", n, errvar, p.Name) + text += fmt.Sprintf("\tif %s != nil {\n\t\treturn\n\t}\n", errvar) + args = append(args, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n)) + n++ + } else if p.Type == "string" { + fmt.Fprintf(os.Stderr, path+":"+funct+" uses string arguments, but has no error return\n") + text += fmt.Sprintf("\tvar _p%d *byte\n", n) + text += fmt.Sprintf("\t_p%d, _ = BytePtrFromString(%s)\n", n, p.Name) + args = append(args, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n)) + n++ + } else if regexp.MustCompile(`^\[\](.*)`).FindStringSubmatch(p.Type) != nil { + // Convert slice into pointer, length. + // Have to be careful not to take address of &a[0] if len == 0: + // pass dummy pointer in that case. + // Used to pass nil, but some OSes or simulators reject write(fd, nil, 0). + text += fmt.Sprintf("\tvar _p%d unsafe.Pointer\n", n) + text += fmt.Sprintf("\tif len(%s) > 0 {\n\t\t_p%d = unsafe.Pointer(&%s[0])\n\t}", p.Name, n, p.Name) + text += fmt.Sprintf(" else {\n\t\t_p%d = unsafe.Pointer(&_zero)\n\t}\n", n) + args = append(args, fmt.Sprintf("uintptr(_p%d)", n), fmt.Sprintf("uintptr(len(%s))", p.Name)) + n++ + } else if p.Type == "int64" && (*openbsd || *netbsd) { + args = append(args, "0") + if endianness == "big-endian" { + args = append(args, fmt.Sprintf("uintptr(%s>>32)", p.Name), fmt.Sprintf("uintptr(%s)", p.Name)) + } else if endianness == "little-endian" { + args = append(args, fmt.Sprintf("uintptr(%s)", p.Name), fmt.Sprintf("uintptr(%s>>32)", p.Name)) + } else { + args = append(args, fmt.Sprintf("uintptr(%s)", p.Name)) + } + } else if p.Type == "int64" && *dragonfly { + if regexp.MustCompile(`^(?i)extp(read|write)`).FindStringSubmatch(funct) == nil { + args = append(args, "0") + } + if endianness == "big-endian" { + args = append(args, fmt.Sprintf("uintptr(%s>>32)", p.Name), fmt.Sprintf("uintptr(%s)", p.Name)) + } else if endianness == "little-endian" { + args = append(args, fmt.Sprintf("uintptr(%s)", p.Name), fmt.Sprintf("uintptr(%s>>32)", p.Name)) + } else { + args = append(args, fmt.Sprintf("uintptr(%s)", p.Name)) + } + } else if (p.Type == "int64" || p.Type == "uint64") && endianness != "" { + if len(args)%2 == 1 && *arm { + // arm abi specifies 64-bit argument uses + // (even, odd) pair + args = append(args, "0") + } + if endianness == "big-endian" { + args = append(args, fmt.Sprintf("uintptr(%s>>32)", p.Name), fmt.Sprintf("uintptr(%s)", p.Name)) + } else { + args = append(args, fmt.Sprintf("uintptr(%s)", p.Name), fmt.Sprintf("uintptr(%s>>32)", p.Name)) + } + } else { + args = append(args, fmt.Sprintf("uintptr(%s)", p.Name)) + } + } + + // Determine which form to use; pad args with zeros. + asm := "Syscall" + if nonblock != nil { + if errvar == "" && goos == "linux" { + asm = "RawSyscallNoError" + } else { + asm = "RawSyscall" + } + } else { + if errvar == "" && goos == "linux" { + asm = "SyscallNoError" + } + } + if len(args) <= 3 { + for len(args) < 3 { + args = append(args, "0") + } + } else if len(args) <= 6 { + asm += "6" + for len(args) < 6 { + args = append(args, "0") + } + } else if len(args) <= 9 { + asm += "9" + for len(args) < 9 { + args = append(args, "0") + } + } else { + fmt.Fprintf(os.Stderr, "%s:%s too many arguments to system call\n", path, funct) + } + + // System call number. + if sysname == "" { + sysname = "SYS_" + funct + sysname = regexp.MustCompile(`([a-z])([A-Z])`).ReplaceAllString(sysname, `${1}_$2`) + sysname = strings.ToUpper(sysname) + } + + var libcFn string + if libc { + asm = "syscall_" + strings.ToLower(asm[:1]) + asm[1:] // internal syscall call + sysname = strings.TrimPrefix(sysname, "SYS_") // remove SYS_ + sysname = strings.ToLower(sysname) // lowercase + if sysname == "getdirentries64" { + // Special case - libSystem name and + // raw syscall name don't match. + sysname = "__getdirentries64" + } + libcFn = sysname + sysname = "funcPC(libc_" + sysname + "_trampoline)" + } + + // Actual call. + arglist := strings.Join(args, ", ") + call := fmt.Sprintf("%s(%s, %s)", asm, sysname, arglist) + + // Assign return values. + body := "" + ret := []string{"_", "_", "_"} + doErrno := false + for i := 0; i < len(out); i++ { + p := parseParam(out[i]) + reg := "" + if p.Name == "err" && !*plan9 { + reg = "e1" + ret[2] = reg + doErrno = true + } else if p.Name == "err" && *plan9 { + ret[0] = "r0" + ret[2] = "e1" + break + } else { + reg = fmt.Sprintf("r%d", i) + ret[i] = reg + } + if p.Type == "bool" { + reg = fmt.Sprintf("%s != 0", reg) + } + if p.Type == "int64" && endianness != "" { + // 64-bit number in r1:r0 or r0:r1. + if i+2 > len(out) { + fmt.Fprintf(os.Stderr, "%s:%s not enough registers for int64 return\n", path, funct) + } + if endianness == "big-endian" { + reg = fmt.Sprintf("int64(r%d)<<32 | int64(r%d)", i, i+1) + } else { + reg = fmt.Sprintf("int64(r%d)<<32 | int64(r%d)", i+1, i) + } + ret[i] = fmt.Sprintf("r%d", i) + ret[i+1] = fmt.Sprintf("r%d", i+1) + } + if reg != "e1" || *plan9 { + body += fmt.Sprintf("\t%s = %s(%s)\n", p.Name, p.Type, reg) + } + } + if ret[0] == "_" && ret[1] == "_" && ret[2] == "_" { + text += fmt.Sprintf("\t%s\n", call) + } else { + if errvar == "" && goos == "linux" { + // raw syscall without error on Linux, see golang.org/issue/22924 + text += fmt.Sprintf("\t%s, %s := %s\n", ret[0], ret[1], call) + } else { + text += fmt.Sprintf("\t%s, %s, %s := %s\n", ret[0], ret[1], ret[2], call) + } + } + text += body + + if *plan9 && ret[2] == "e1" { + text += "\tif int32(r0) == -1 {\n" + text += "\t\terr = e1\n" + text += "\t}\n" + } else if doErrno { + text += "\tif e1 != 0 {\n" + text += "\t\terr = errnoErr(e1)\n" + text += "\t}\n" + } + text += "\treturn\n" + text += "}\n\n" + + if libc && !trampolines[libcFn] { + // some system calls share a trampoline, like read and readlen. + trampolines[libcFn] = true + // Declare assembly trampoline. + text += fmt.Sprintf("func libc_%s_trampoline()\n", libcFn) + // Assembly trampoline calls the libc_* function, which this magic + // redirects to use the function from libSystem. + text += fmt.Sprintf("//go:linkname libc_%s libc_%s\n", libcFn, libcFn) + text += fmt.Sprintf("//go:cgo_import_dynamic libc_%s %s \"/usr/lib/libSystem.B.dylib\"\n", libcFn, libcFn) + text += "\n" + } + } + if err := s.Err(); err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + file.Close() + } + fmt.Printf(srcTemplate, cmdLine(), buildTags(), text) +} + +const srcTemplate = `// %s +// Code generated by the command above; see README.md. DO NOT EDIT. + +// +build %s + +package unix + +import ( + "syscall" + "unsafe" +) + +var _ syscall.Errno + +%s +` diff --git a/vendor/golang.org/x/sys/unix/mksyscall_aix_ppc.go b/vendor/golang.org/x/sys/unix/mksyscall_aix_ppc.go new file mode 100644 index 0000000000..3be3cdfc3b --- /dev/null +++ b/vendor/golang.org/x/sys/unix/mksyscall_aix_ppc.go @@ -0,0 +1,415 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +/* +This program reads a file containing function prototypes +(like syscall_aix.go) and generates system call bodies. +The prototypes are marked by lines beginning with "//sys" +and read like func declarations if //sys is replaced by func, but: + * The parameter lists must give a name for each argument. + This includes return parameters. + * The parameter lists must give a type for each argument: + the (x, y, z int) shorthand is not allowed. + * If the return parameter is an error number, it must be named err. + * If go func name needs to be different than its libc name, + * or the function is not in libc, name could be specified + * at the end, after "=" sign, like + //sys getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (err error) = libsocket.getsockopt +*/ +package main + +import ( + "bufio" + "flag" + "fmt" + "os" + "regexp" + "strings" +) + +var ( + b32 = flag.Bool("b32", false, "32bit big-endian") + l32 = flag.Bool("l32", false, "32bit little-endian") + aix = flag.Bool("aix", false, "aix") + tags = flag.String("tags", "", "build tags") +) + +// cmdLine returns this programs's commandline arguments +func cmdLine() string { + return "go run mksyscall_aix_ppc.go " + strings.Join(os.Args[1:], " ") +} + +// buildTags returns build tags +func buildTags() string { + return *tags +} + +// Param is function parameter +type Param struct { + Name string + Type string +} + +// usage prints the program usage +func usage() { + fmt.Fprintf(os.Stderr, "usage: go run mksyscall_aix_ppc.go [-b32 | -l32] [-tags x,y] [file ...]\n") + os.Exit(1) +} + +// parseParamList parses parameter list and returns a slice of parameters +func parseParamList(list string) []string { + list = strings.TrimSpace(list) + if list == "" { + return []string{} + } + return regexp.MustCompile(`\s*,\s*`).Split(list, -1) +} + +// parseParam splits a parameter into name and type +func parseParam(p string) Param { + ps := regexp.MustCompile(`^(\S*) (\S*)$`).FindStringSubmatch(p) + if ps == nil { + fmt.Fprintf(os.Stderr, "malformed parameter: %s\n", p) + os.Exit(1) + } + return Param{ps[1], ps[2]} +} + +func main() { + flag.Usage = usage + flag.Parse() + if len(flag.Args()) <= 0 { + fmt.Fprintf(os.Stderr, "no files to parse provided\n") + usage() + } + + endianness := "" + if *b32 { + endianness = "big-endian" + } else if *l32 { + endianness = "little-endian" + } + + pack := "" + text := "" + cExtern := "/*\n#include \n#include \n" + for _, path := range flag.Args() { + file, err := os.Open(path) + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + s := bufio.NewScanner(file) + for s.Scan() { + t := s.Text() + t = strings.TrimSpace(t) + t = regexp.MustCompile(`\s+`).ReplaceAllString(t, ` `) + if p := regexp.MustCompile(`^package (\S+)$`).FindStringSubmatch(t); p != nil && pack == "" { + pack = p[1] + } + nonblock := regexp.MustCompile(`^\/\/sysnb `).FindStringSubmatch(t) + if regexp.MustCompile(`^\/\/sys `).FindStringSubmatch(t) == nil && nonblock == nil { + continue + } + + // Line must be of the form + // func Open(path string, mode int, perm int) (fd int, err error) + // Split into name, in params, out params. + f := regexp.MustCompile(`^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*(?:(\w*)\.)?(\w*))?$`).FindStringSubmatch(t) + if f == nil { + fmt.Fprintf(os.Stderr, "%s:%s\nmalformed //sys declaration\n", path, t) + os.Exit(1) + } + funct, inps, outps, modname, sysname := f[2], f[3], f[4], f[5], f[6] + + // Split argument lists on comma. + in := parseParamList(inps) + out := parseParamList(outps) + + inps = strings.Join(in, ", ") + outps = strings.Join(out, ", ") + + // Try in vain to keep people from editing this file. + // The theory is that they jump into the middle of the file + // without reading the header. + text += "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n" + + // Check if value return, err return available + errvar := "" + retvar := "" + rettype := "" + for _, param := range out { + p := parseParam(param) + if p.Type == "error" { + errvar = p.Name + } else { + retvar = p.Name + rettype = p.Type + } + } + + // System call name. + if sysname == "" { + sysname = funct + } + sysname = regexp.MustCompile(`([a-z])([A-Z])`).ReplaceAllString(sysname, `${1}_$2`) + sysname = strings.ToLower(sysname) // All libc functions are lowercase. + + cRettype := "" + if rettype == "unsafe.Pointer" { + cRettype = "uintptr_t" + } else if rettype == "uintptr" { + cRettype = "uintptr_t" + } else if regexp.MustCompile(`^_`).FindStringSubmatch(rettype) != nil { + cRettype = "uintptr_t" + } else if rettype == "int" { + cRettype = "int" + } else if rettype == "int32" { + cRettype = "int" + } else if rettype == "int64" { + cRettype = "long long" + } else if rettype == "uint32" { + cRettype = "unsigned int" + } else if rettype == "uint64" { + cRettype = "unsigned long long" + } else { + cRettype = "int" + } + if sysname == "exit" { + cRettype = "void" + } + + // Change p.Types to c + var cIn []string + for _, param := range in { + p := parseParam(param) + if regexp.MustCompile(`^\*`).FindStringSubmatch(p.Type) != nil { + cIn = append(cIn, "uintptr_t") + } else if p.Type == "string" { + cIn = append(cIn, "uintptr_t") + } else if regexp.MustCompile(`^\[\](.*)`).FindStringSubmatch(p.Type) != nil { + cIn = append(cIn, "uintptr_t", "size_t") + } else if p.Type == "unsafe.Pointer" { + cIn = append(cIn, "uintptr_t") + } else if p.Type == "uintptr" { + cIn = append(cIn, "uintptr_t") + } else if regexp.MustCompile(`^_`).FindStringSubmatch(p.Type) != nil { + cIn = append(cIn, "uintptr_t") + } else if p.Type == "int" { + cIn = append(cIn, "int") + } else if p.Type == "int32" { + cIn = append(cIn, "int") + } else if p.Type == "int64" { + cIn = append(cIn, "long long") + } else if p.Type == "uint32" { + cIn = append(cIn, "unsigned int") + } else if p.Type == "uint64" { + cIn = append(cIn, "unsigned long long") + } else { + cIn = append(cIn, "int") + } + } + + if funct != "fcntl" && funct != "FcntlInt" && funct != "readlen" && funct != "writelen" { + if sysname == "select" { + // select is a keyword of Go. Its name is + // changed to c_select. + cExtern += "#define c_select select\n" + } + // Imports of system calls from libc + cExtern += fmt.Sprintf("%s %s", cRettype, sysname) + cIn := strings.Join(cIn, ", ") + cExtern += fmt.Sprintf("(%s);\n", cIn) + } + + // So file name. + if *aix { + if modname == "" { + modname = "libc.a/shr_64.o" + } else { + fmt.Fprintf(os.Stderr, "%s: only syscall using libc are available\n", funct) + os.Exit(1) + } + } + + strconvfunc := "C.CString" + + // Go function header. + if outps != "" { + outps = fmt.Sprintf(" (%s)", outps) + } + if text != "" { + text += "\n" + } + + text += fmt.Sprintf("func %s(%s)%s {\n", funct, strings.Join(in, ", "), outps) + + // Prepare arguments to Syscall. + var args []string + n := 0 + argN := 0 + for _, param := range in { + p := parseParam(param) + if regexp.MustCompile(`^\*`).FindStringSubmatch(p.Type) != nil { + args = append(args, "C.uintptr_t(uintptr(unsafe.Pointer("+p.Name+")))") + } else if p.Type == "string" && errvar != "" { + text += fmt.Sprintf("\t_p%d := uintptr(unsafe.Pointer(%s(%s)))\n", n, strconvfunc, p.Name) + args = append(args, fmt.Sprintf("C.uintptr_t(_p%d)", n)) + n++ + } else if p.Type == "string" { + fmt.Fprintf(os.Stderr, path+":"+funct+" uses string arguments, but has no error return\n") + text += fmt.Sprintf("\t_p%d := uintptr(unsafe.Pointer(%s(%s)))\n", n, strconvfunc, p.Name) + args = append(args, fmt.Sprintf("C.uintptr_t(_p%d)", n)) + n++ + } else if m := regexp.MustCompile(`^\[\](.*)`).FindStringSubmatch(p.Type); m != nil { + // Convert slice into pointer, length. + // Have to be careful not to take address of &a[0] if len == 0: + // pass nil in that case. + text += fmt.Sprintf("\tvar _p%d *%s\n", n, m[1]) + text += fmt.Sprintf("\tif len(%s) > 0 {\n\t\t_p%d = &%s[0]\n\t}\n", p.Name, n, p.Name) + args = append(args, fmt.Sprintf("C.uintptr_t(uintptr(unsafe.Pointer(_p%d)))", n)) + n++ + text += fmt.Sprintf("\tvar _p%d int\n", n) + text += fmt.Sprintf("\t_p%d = len(%s)\n", n, p.Name) + args = append(args, fmt.Sprintf("C.size_t(_p%d)", n)) + n++ + } else if p.Type == "int64" && endianness != "" { + if endianness == "big-endian" { + args = append(args, fmt.Sprintf("uintptr(%s>>32)", p.Name), fmt.Sprintf("uintptr(%s)", p.Name)) + } else { + args = append(args, fmt.Sprintf("uintptr(%s)", p.Name), fmt.Sprintf("uintptr(%s>>32)", p.Name)) + } + n++ + } else if p.Type == "bool" { + text += fmt.Sprintf("\tvar _p%d uint32\n", n) + text += fmt.Sprintf("\tif %s {\n\t\t_p%d = 1\n\t} else {\n\t\t_p%d = 0\n\t}\n", p.Name, n, n) + args = append(args, fmt.Sprintf("_p%d", n)) + } else if regexp.MustCompile(`^_`).FindStringSubmatch(p.Type) != nil { + args = append(args, fmt.Sprintf("C.uintptr_t(uintptr(%s))", p.Name)) + } else if p.Type == "unsafe.Pointer" { + args = append(args, fmt.Sprintf("C.uintptr_t(uintptr(%s))", p.Name)) + } else if p.Type == "int" { + if (argN == 2) && ((funct == "readlen") || (funct == "writelen")) { + args = append(args, fmt.Sprintf("C.size_t(%s)", p.Name)) + } else if argN == 0 && funct == "fcntl" { + args = append(args, fmt.Sprintf("C.uintptr_t(%s)", p.Name)) + } else if (argN == 2) && ((funct == "fcntl") || (funct == "FcntlInt")) { + args = append(args, fmt.Sprintf("C.uintptr_t(%s)", p.Name)) + } else { + args = append(args, fmt.Sprintf("C.int(%s)", p.Name)) + } + } else if p.Type == "int32" { + args = append(args, fmt.Sprintf("C.int(%s)", p.Name)) + } else if p.Type == "int64" { + args = append(args, fmt.Sprintf("C.longlong(%s)", p.Name)) + } else if p.Type == "uint32" { + args = append(args, fmt.Sprintf("C.uint(%s)", p.Name)) + } else if p.Type == "uint64" { + args = append(args, fmt.Sprintf("C.ulonglong(%s)", p.Name)) + } else if p.Type == "uintptr" { + args = append(args, fmt.Sprintf("C.uintptr_t(%s)", p.Name)) + } else { + args = append(args, fmt.Sprintf("C.int(%s)", p.Name)) + } + argN++ + } + + // Actual call. + arglist := strings.Join(args, ", ") + call := "" + if sysname == "exit" { + if errvar != "" { + call += "er :=" + } else { + call += "" + } + } else if errvar != "" { + call += "r0,er :=" + } else if retvar != "" { + call += "r0,_ :=" + } else { + call += "" + } + if sysname == "select" { + // select is a keyword of Go. Its name is + // changed to c_select. + call += fmt.Sprintf("C.c_%s(%s)", sysname, arglist) + } else { + call += fmt.Sprintf("C.%s(%s)", sysname, arglist) + } + + // Assign return values. + body := "" + for i := 0; i < len(out); i++ { + p := parseParam(out[i]) + reg := "" + if p.Name == "err" { + reg = "e1" + } else { + reg = "r0" + } + if reg != "e1" { + body += fmt.Sprintf("\t%s = %s(%s)\n", p.Name, p.Type, reg) + } + } + + // verify return + if sysname != "exit" && errvar != "" { + if regexp.MustCompile(`^uintptr`).FindStringSubmatch(cRettype) != nil { + body += "\tif (uintptr(r0) ==^uintptr(0) && er != nil) {\n" + body += fmt.Sprintf("\t\t%s = er\n", errvar) + body += "\t}\n" + } else { + body += "\tif (r0 ==-1 && er != nil) {\n" + body += fmt.Sprintf("\t\t%s = er\n", errvar) + body += "\t}\n" + } + } else if errvar != "" { + body += "\tif (er != nil) {\n" + body += fmt.Sprintf("\t\t%s = er\n", errvar) + body += "\t}\n" + } + + text += fmt.Sprintf("\t%s\n", call) + text += body + + text += "\treturn\n" + text += "}\n" + } + if err := s.Err(); err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + file.Close() + } + imp := "" + if pack != "unix" { + imp = "import \"golang.org/x/sys/unix\"\n" + + } + fmt.Printf(srcTemplate, cmdLine(), buildTags(), pack, cExtern, imp, text) +} + +const srcTemplate = `// %s +// Code generated by the command above; see README.md. DO NOT EDIT. + +// +build %s + +package %s + + +%s +*/ +import "C" +import ( + "unsafe" +) + + +%s + +%s +` diff --git a/vendor/golang.org/x/sys/unix/mksyscall_aix_ppc64.go b/vendor/golang.org/x/sys/unix/mksyscall_aix_ppc64.go new file mode 100644 index 0000000000..c960099517 --- /dev/null +++ b/vendor/golang.org/x/sys/unix/mksyscall_aix_ppc64.go @@ -0,0 +1,614 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +/* +This program reads a file containing function prototypes +(like syscall_aix.go) and generates system call bodies. +The prototypes are marked by lines beginning with "//sys" +and read like func declarations if //sys is replaced by func, but: + * The parameter lists must give a name for each argument. + This includes return parameters. + * The parameter lists must give a type for each argument: + the (x, y, z int) shorthand is not allowed. + * If the return parameter is an error number, it must be named err. + * If go func name needs to be different than its libc name, + * or the function is not in libc, name could be specified + * at the end, after "=" sign, like + //sys getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (err error) = libsocket.getsockopt + + +This program will generate three files and handle both gc and gccgo implementation: + - zsyscall_aix_ppc64.go: the common part of each implementation (error handler, pointer creation) + - zsyscall_aix_ppc64_gc.go: gc part with //go_cgo_import_dynamic and a call to syscall6 + - zsyscall_aix_ppc64_gccgo.go: gccgo part with C function and conversion to C type. + + The generated code looks like this + +zsyscall_aix_ppc64.go +func asyscall(...) (n int, err error) { + // Pointer Creation + r1, e1 := callasyscall(...) + // Type Conversion + // Error Handler + return +} + +zsyscall_aix_ppc64_gc.go +//go:cgo_import_dynamic libc_asyscall asyscall "libc.a/shr_64.o" +//go:linkname libc_asyscall libc_asyscall +var asyscall syscallFunc + +func callasyscall(...) (r1 uintptr, e1 Errno) { + r1, _, e1 = syscall6(uintptr(unsafe.Pointer(&libc_asyscall)), "nb_args", ... ) + return +} + +zsyscall_aix_ppc64_ggcgo.go + +// int asyscall(...) + +import "C" + +func callasyscall(...) (r1 uintptr, e1 Errno) { + r1 = uintptr(C.asyscall(...)) + e1 = syscall.GetErrno() + return +} +*/ + +package main + +import ( + "bufio" + "flag" + "fmt" + "io/ioutil" + "os" + "regexp" + "strings" +) + +var ( + b32 = flag.Bool("b32", false, "32bit big-endian") + l32 = flag.Bool("l32", false, "32bit little-endian") + aix = flag.Bool("aix", false, "aix") + tags = flag.String("tags", "", "build tags") +) + +// cmdLine returns this programs's commandline arguments +func cmdLine() string { + return "go run mksyscall_aix_ppc64.go " + strings.Join(os.Args[1:], " ") +} + +// buildTags returns build tags +func buildTags() string { + return *tags +} + +// Param is function parameter +type Param struct { + Name string + Type string +} + +// usage prints the program usage +func usage() { + fmt.Fprintf(os.Stderr, "usage: go run mksyscall_aix_ppc64.go [-b32 | -l32] [-tags x,y] [file ...]\n") + os.Exit(1) +} + +// parseParamList parses parameter list and returns a slice of parameters +func parseParamList(list string) []string { + list = strings.TrimSpace(list) + if list == "" { + return []string{} + } + return regexp.MustCompile(`\s*,\s*`).Split(list, -1) +} + +// parseParam splits a parameter into name and type +func parseParam(p string) Param { + ps := regexp.MustCompile(`^(\S*) (\S*)$`).FindStringSubmatch(p) + if ps == nil { + fmt.Fprintf(os.Stderr, "malformed parameter: %s\n", p) + os.Exit(1) + } + return Param{ps[1], ps[2]} +} + +func main() { + flag.Usage = usage + flag.Parse() + if len(flag.Args()) <= 0 { + fmt.Fprintf(os.Stderr, "no files to parse provided\n") + usage() + } + + endianness := "" + if *b32 { + endianness = "big-endian" + } else if *l32 { + endianness = "little-endian" + } + + pack := "" + // GCCGO + textgccgo := "" + cExtern := "/*\n#include \n" + // GC + textgc := "" + dynimports := "" + linknames := "" + var vars []string + // COMMON + textcommon := "" + for _, path := range flag.Args() { + file, err := os.Open(path) + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + s := bufio.NewScanner(file) + for s.Scan() { + t := s.Text() + t = strings.TrimSpace(t) + t = regexp.MustCompile(`\s+`).ReplaceAllString(t, ` `) + if p := regexp.MustCompile(`^package (\S+)$`).FindStringSubmatch(t); p != nil && pack == "" { + pack = p[1] + } + nonblock := regexp.MustCompile(`^\/\/sysnb `).FindStringSubmatch(t) + if regexp.MustCompile(`^\/\/sys `).FindStringSubmatch(t) == nil && nonblock == nil { + continue + } + + // Line must be of the form + // func Open(path string, mode int, perm int) (fd int, err error) + // Split into name, in params, out params. + f := regexp.MustCompile(`^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*(?:(\w*)\.)?(\w*))?$`).FindStringSubmatch(t) + if f == nil { + fmt.Fprintf(os.Stderr, "%s:%s\nmalformed //sys declaration\n", path, t) + os.Exit(1) + } + funct, inps, outps, modname, sysname := f[2], f[3], f[4], f[5], f[6] + + // Split argument lists on comma. + in := parseParamList(inps) + out := parseParamList(outps) + + inps = strings.Join(in, ", ") + outps = strings.Join(out, ", ") + + if sysname == "" { + sysname = funct + } + + onlyCommon := false + if funct == "readlen" || funct == "writelen" || funct == "FcntlInt" || funct == "FcntlFlock" { + // This function call another syscall which is already implemented. + // Therefore, the gc and gccgo part must not be generated. + onlyCommon = true + } + + // Try in vain to keep people from editing this file. + // The theory is that they jump into the middle of the file + // without reading the header. + + textcommon += "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n" + if !onlyCommon { + textgccgo += "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n" + textgc += "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n" + } + + // Check if value return, err return available + errvar := "" + rettype := "" + for _, param := range out { + p := parseParam(param) + if p.Type == "error" { + errvar = p.Name + } else { + rettype = p.Type + } + } + + sysname = regexp.MustCompile(`([a-z])([A-Z])`).ReplaceAllString(sysname, `${1}_$2`) + sysname = strings.ToLower(sysname) // All libc functions are lowercase. + + // GCCGO Prototype return type + cRettype := "" + if rettype == "unsafe.Pointer" { + cRettype = "uintptr_t" + } else if rettype == "uintptr" { + cRettype = "uintptr_t" + } else if regexp.MustCompile(`^_`).FindStringSubmatch(rettype) != nil { + cRettype = "uintptr_t" + } else if rettype == "int" { + cRettype = "int" + } else if rettype == "int32" { + cRettype = "int" + } else if rettype == "int64" { + cRettype = "long long" + } else if rettype == "uint32" { + cRettype = "unsigned int" + } else if rettype == "uint64" { + cRettype = "unsigned long long" + } else { + cRettype = "int" + } + if sysname == "exit" { + cRettype = "void" + } + + // GCCGO Prototype arguments type + var cIn []string + for i, param := range in { + p := parseParam(param) + if regexp.MustCompile(`^\*`).FindStringSubmatch(p.Type) != nil { + cIn = append(cIn, "uintptr_t") + } else if p.Type == "string" { + cIn = append(cIn, "uintptr_t") + } else if regexp.MustCompile(`^\[\](.*)`).FindStringSubmatch(p.Type) != nil { + cIn = append(cIn, "uintptr_t", "size_t") + } else if p.Type == "unsafe.Pointer" { + cIn = append(cIn, "uintptr_t") + } else if p.Type == "uintptr" { + cIn = append(cIn, "uintptr_t") + } else if regexp.MustCompile(`^_`).FindStringSubmatch(p.Type) != nil { + cIn = append(cIn, "uintptr_t") + } else if p.Type == "int" { + if (i == 0 || i == 2) && funct == "fcntl" { + // These fcntl arguments needs to be uintptr to be able to call FcntlInt and FcntlFlock + cIn = append(cIn, "uintptr_t") + } else { + cIn = append(cIn, "int") + } + + } else if p.Type == "int32" { + cIn = append(cIn, "int") + } else if p.Type == "int64" { + cIn = append(cIn, "long long") + } else if p.Type == "uint32" { + cIn = append(cIn, "unsigned int") + } else if p.Type == "uint64" { + cIn = append(cIn, "unsigned long long") + } else { + cIn = append(cIn, "int") + } + } + + if !onlyCommon { + // GCCGO Prototype Generation + // Imports of system calls from libc + if sysname == "select" { + // select is a keyword of Go. Its name is + // changed to c_select. + cExtern += "#define c_select select\n" + } + cExtern += fmt.Sprintf("%s %s", cRettype, sysname) + cIn := strings.Join(cIn, ", ") + cExtern += fmt.Sprintf("(%s);\n", cIn) + } + // GC Library name + if modname == "" { + modname = "libc.a/shr_64.o" + } else { + fmt.Fprintf(os.Stderr, "%s: only syscall using libc are available\n", funct) + os.Exit(1) + } + sysvarname := fmt.Sprintf("libc_%s", sysname) + + if !onlyCommon { + // GC Runtime import of function to allow cross-platform builds. + dynimports += fmt.Sprintf("//go:cgo_import_dynamic %s %s \"%s\"\n", sysvarname, sysname, modname) + // GC Link symbol to proc address variable. + linknames += fmt.Sprintf("//go:linkname %s %s\n", sysvarname, sysvarname) + // GC Library proc address variable. + vars = append(vars, sysvarname) + } + + strconvfunc := "BytePtrFromString" + strconvtype := "*byte" + + // Go function header. + if outps != "" { + outps = fmt.Sprintf(" (%s)", outps) + } + if textcommon != "" { + textcommon += "\n" + } + + textcommon += fmt.Sprintf("func %s(%s)%s {\n", funct, strings.Join(in, ", "), outps) + + // Prepare arguments tocall. + var argscommon []string // Arguments in the common part + var argscall []string // Arguments for call prototype + var argsgc []string // Arguments for gc call (with syscall6) + var argsgccgo []string // Arguments for gccgo call (with C.name_of_syscall) + n := 0 + argN := 0 + for _, param := range in { + p := parseParam(param) + if regexp.MustCompile(`^\*`).FindStringSubmatch(p.Type) != nil { + argscommon = append(argscommon, fmt.Sprintf("uintptr(unsafe.Pointer(%s))", p.Name)) + argscall = append(argscall, fmt.Sprintf("%s uintptr", p.Name)) + argsgc = append(argsgc, p.Name) + argsgccgo = append(argsgccgo, fmt.Sprintf("C.uintptr_t(%s)", p.Name)) + } else if p.Type == "string" && errvar != "" { + textcommon += fmt.Sprintf("\tvar _p%d %s\n", n, strconvtype) + textcommon += fmt.Sprintf("\t_p%d, %s = %s(%s)\n", n, errvar, strconvfunc, p.Name) + textcommon += fmt.Sprintf("\tif %s != nil {\n\t\treturn\n\t}\n", errvar) + + argscommon = append(argscommon, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n)) + argscall = append(argscall, fmt.Sprintf("_p%d uintptr ", n)) + argsgc = append(argsgc, fmt.Sprintf("_p%d", n)) + argsgccgo = append(argsgccgo, fmt.Sprintf("C.uintptr_t(_p%d)", n)) + n++ + } else if p.Type == "string" { + fmt.Fprintf(os.Stderr, path+":"+funct+" uses string arguments, but has no error return\n") + textcommon += fmt.Sprintf("\tvar _p%d %s\n", n, strconvtype) + textcommon += fmt.Sprintf("\t_p%d, %s = %s(%s)\n", n, errvar, strconvfunc, p.Name) + textcommon += fmt.Sprintf("\tif %s != nil {\n\t\treturn\n\t}\n", errvar) + + argscommon = append(argscommon, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n)) + argscall = append(argscall, fmt.Sprintf("_p%d uintptr", n)) + argsgc = append(argsgc, fmt.Sprintf("_p%d", n)) + argsgccgo = append(argsgccgo, fmt.Sprintf("C.uintptr_t(_p%d)", n)) + n++ + } else if m := regexp.MustCompile(`^\[\](.*)`).FindStringSubmatch(p.Type); m != nil { + // Convert slice into pointer, length. + // Have to be careful not to take address of &a[0] if len == 0: + // pass nil in that case. + textcommon += fmt.Sprintf("\tvar _p%d *%s\n", n, m[1]) + textcommon += fmt.Sprintf("\tif len(%s) > 0 {\n\t\t_p%d = &%s[0]\n\t}\n", p.Name, n, p.Name) + argscommon = append(argscommon, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n), fmt.Sprintf("len(%s)", p.Name)) + argscall = append(argscall, fmt.Sprintf("_p%d uintptr", n), fmt.Sprintf("_lenp%d int", n)) + argsgc = append(argsgc, fmt.Sprintf("_p%d", n), fmt.Sprintf("uintptr(_lenp%d)", n)) + argsgccgo = append(argsgccgo, fmt.Sprintf("C.uintptr_t(_p%d)", n), fmt.Sprintf("C.size_t(_lenp%d)", n)) + n++ + } else if p.Type == "int64" && endianness != "" { + fmt.Fprintf(os.Stderr, path+":"+funct+" uses int64 with 32 bits mode. Case not yet implemented\n") + } else if p.Type == "bool" { + fmt.Fprintf(os.Stderr, path+":"+funct+" uses bool. Case not yet implemented\n") + } else if regexp.MustCompile(`^_`).FindStringSubmatch(p.Type) != nil || p.Type == "unsafe.Pointer" { + argscommon = append(argscommon, fmt.Sprintf("uintptr(%s)", p.Name)) + argscall = append(argscall, fmt.Sprintf("%s uintptr", p.Name)) + argsgc = append(argsgc, p.Name) + argsgccgo = append(argsgccgo, fmt.Sprintf("C.uintptr_t(%s)", p.Name)) + } else if p.Type == "int" { + if (argN == 0 || argN == 2) && ((funct == "fcntl") || (funct == "FcntlInt") || (funct == "FcntlFlock")) { + // These fcntl arguments need to be uintptr to be able to call FcntlInt and FcntlFlock + argscommon = append(argscommon, fmt.Sprintf("uintptr(%s)", p.Name)) + argscall = append(argscall, fmt.Sprintf("%s uintptr", p.Name)) + argsgc = append(argsgc, p.Name) + argsgccgo = append(argsgccgo, fmt.Sprintf("C.uintptr_t(%s)", p.Name)) + + } else { + argscommon = append(argscommon, p.Name) + argscall = append(argscall, fmt.Sprintf("%s int", p.Name)) + argsgc = append(argsgc, fmt.Sprintf("uintptr(%s)", p.Name)) + argsgccgo = append(argsgccgo, fmt.Sprintf("C.int(%s)", p.Name)) + } + } else if p.Type == "int32" { + argscommon = append(argscommon, p.Name) + argscall = append(argscall, fmt.Sprintf("%s int32", p.Name)) + argsgc = append(argsgc, fmt.Sprintf("uintptr(%s)", p.Name)) + argsgccgo = append(argsgccgo, fmt.Sprintf("C.int(%s)", p.Name)) + } else if p.Type == "int64" { + argscommon = append(argscommon, p.Name) + argscall = append(argscall, fmt.Sprintf("%s int64", p.Name)) + argsgc = append(argsgc, fmt.Sprintf("uintptr(%s)", p.Name)) + argsgccgo = append(argsgccgo, fmt.Sprintf("C.longlong(%s)", p.Name)) + } else if p.Type == "uint32" { + argscommon = append(argscommon, p.Name) + argscall = append(argscall, fmt.Sprintf("%s uint32", p.Name)) + argsgc = append(argsgc, fmt.Sprintf("uintptr(%s)", p.Name)) + argsgccgo = append(argsgccgo, fmt.Sprintf("C.uint(%s)", p.Name)) + } else if p.Type == "uint64" { + argscommon = append(argscommon, p.Name) + argscall = append(argscall, fmt.Sprintf("%s uint64", p.Name)) + argsgc = append(argsgc, fmt.Sprintf("uintptr(%s)", p.Name)) + argsgccgo = append(argsgccgo, fmt.Sprintf("C.ulonglong(%s)", p.Name)) + } else if p.Type == "uintptr" { + argscommon = append(argscommon, p.Name) + argscall = append(argscall, fmt.Sprintf("%s uintptr", p.Name)) + argsgc = append(argsgc, p.Name) + argsgccgo = append(argsgccgo, fmt.Sprintf("C.uintptr_t(%s)", p.Name)) + } else { + argscommon = append(argscommon, fmt.Sprintf("int(%s)", p.Name)) + argscall = append(argscall, fmt.Sprintf("%s int", p.Name)) + argsgc = append(argsgc, fmt.Sprintf("uintptr(%s)", p.Name)) + argsgccgo = append(argsgccgo, fmt.Sprintf("C.int(%s)", p.Name)) + } + argN++ + } + nargs := len(argsgc) + + // COMMON function generation + argscommonlist := strings.Join(argscommon, ", ") + callcommon := fmt.Sprintf("call%s(%s)", sysname, argscommonlist) + ret := []string{"_", "_"} + body := "" + doErrno := false + for i := 0; i < len(out); i++ { + p := parseParam(out[i]) + reg := "" + if p.Name == "err" { + reg = "e1" + ret[1] = reg + doErrno = true + } else { + reg = "r0" + ret[0] = reg + } + if p.Type == "bool" { + reg = fmt.Sprintf("%s != 0", reg) + } + if reg != "e1" { + body += fmt.Sprintf("\t%s = %s(%s)\n", p.Name, p.Type, reg) + } + } + if ret[0] == "_" && ret[1] == "_" { + textcommon += fmt.Sprintf("\t%s\n", callcommon) + } else { + textcommon += fmt.Sprintf("\t%s, %s := %s\n", ret[0], ret[1], callcommon) + } + textcommon += body + + if doErrno { + textcommon += "\tif e1 != 0 {\n" + textcommon += "\t\terr = errnoErr(e1)\n" + textcommon += "\t}\n" + } + textcommon += "\treturn\n" + textcommon += "}\n" + + if onlyCommon { + continue + } + + // CALL Prototype + callProto := fmt.Sprintf("func call%s(%s) (r1 uintptr, e1 Errno) {\n", sysname, strings.Join(argscall, ", ")) + + // GC function generation + asm := "syscall6" + if nonblock != nil { + asm = "rawSyscall6" + } + + if len(argsgc) <= 6 { + for len(argsgc) < 6 { + argsgc = append(argsgc, "0") + } + } else { + fmt.Fprintf(os.Stderr, "%s: too many arguments to system call", funct) + os.Exit(1) + } + argsgclist := strings.Join(argsgc, ", ") + callgc := fmt.Sprintf("%s(uintptr(unsafe.Pointer(&%s)), %d, %s)", asm, sysvarname, nargs, argsgclist) + + textgc += callProto + textgc += fmt.Sprintf("\tr1, _, e1 = %s\n", callgc) + textgc += "\treturn\n}\n" + + // GCCGO function generation + argsgccgolist := strings.Join(argsgccgo, ", ") + var callgccgo string + if sysname == "select" { + // select is a keyword of Go. Its name is + // changed to c_select. + callgccgo = fmt.Sprintf("C.c_%s(%s)", sysname, argsgccgolist) + } else { + callgccgo = fmt.Sprintf("C.%s(%s)", sysname, argsgccgolist) + } + textgccgo += callProto + textgccgo += fmt.Sprintf("\tr1 = uintptr(%s)\n", callgccgo) + textgccgo += "\te1 = syscall.GetErrno()\n" + textgccgo += "\treturn\n}\n" + } + if err := s.Err(); err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + file.Close() + } + imp := "" + if pack != "unix" { + imp = "import \"golang.org/x/sys/unix\"\n" + + } + + // Print zsyscall_aix_ppc64.go + err := ioutil.WriteFile("zsyscall_aix_ppc64.go", + []byte(fmt.Sprintf(srcTemplate1, cmdLine(), buildTags(), pack, imp, textcommon)), + 0644) + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + + // Print zsyscall_aix_ppc64_gc.go + vardecls := "\t" + strings.Join(vars, ",\n\t") + vardecls += " syscallFunc" + err = ioutil.WriteFile("zsyscall_aix_ppc64_gc.go", + []byte(fmt.Sprintf(srcTemplate2, cmdLine(), buildTags(), pack, imp, dynimports, linknames, vardecls, textgc)), + 0644) + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + + // Print zsyscall_aix_ppc64_gccgo.go + err = ioutil.WriteFile("zsyscall_aix_ppc64_gccgo.go", + []byte(fmt.Sprintf(srcTemplate3, cmdLine(), buildTags(), pack, cExtern, imp, textgccgo)), + 0644) + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } +} + +const srcTemplate1 = `// %s +// Code generated by the command above; see README.md. DO NOT EDIT. + +// +build %s + +package %s + +import ( + "unsafe" +) + + +%s + +%s +` +const srcTemplate2 = `// %s +// Code generated by the command above; see README.md. DO NOT EDIT. + +// +build %s +// +build !gccgo + +package %s + +import ( + "unsafe" +) +%s +%s +%s +type syscallFunc uintptr + +var ( +%s +) + +// Implemented in runtime/syscall_aix.go. +func rawSyscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) +func syscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) + +%s +` +const srcTemplate3 = `// %s +// Code generated by the command above; see README.md. DO NOT EDIT. + +// +build %s +// +build gccgo + +package %s + +%s +*/ +import "C" +import ( + "syscall" +) + + +%s + +%s +` diff --git a/vendor/golang.org/x/sys/unix/mksyscall_solaris.go b/vendor/golang.org/x/sys/unix/mksyscall_solaris.go new file mode 100644 index 0000000000..3d864738b6 --- /dev/null +++ b/vendor/golang.org/x/sys/unix/mksyscall_solaris.go @@ -0,0 +1,335 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +/* + This program reads a file containing function prototypes + (like syscall_solaris.go) and generates system call bodies. + The prototypes are marked by lines beginning with "//sys" + and read like func declarations if //sys is replaced by func, but: + * The parameter lists must give a name for each argument. + This includes return parameters. + * The parameter lists must give a type for each argument: + the (x, y, z int) shorthand is not allowed. + * If the return parameter is an error number, it must be named err. + * If go func name needs to be different than its libc name, + * or the function is not in libc, name could be specified + * at the end, after "=" sign, like + //sys getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (err error) = libsocket.getsockopt +*/ + +package main + +import ( + "bufio" + "flag" + "fmt" + "os" + "regexp" + "strings" +) + +var ( + b32 = flag.Bool("b32", false, "32bit big-endian") + l32 = flag.Bool("l32", false, "32bit little-endian") + tags = flag.String("tags", "", "build tags") +) + +// cmdLine returns this programs's commandline arguments +func cmdLine() string { + return "go run mksyscall_solaris.go " + strings.Join(os.Args[1:], " ") +} + +// buildTags returns build tags +func buildTags() string { + return *tags +} + +// Param is function parameter +type Param struct { + Name string + Type string +} + +// usage prints the program usage +func usage() { + fmt.Fprintf(os.Stderr, "usage: go run mksyscall_solaris.go [-b32 | -l32] [-tags x,y] [file ...]\n") + os.Exit(1) +} + +// parseParamList parses parameter list and returns a slice of parameters +func parseParamList(list string) []string { + list = strings.TrimSpace(list) + if list == "" { + return []string{} + } + return regexp.MustCompile(`\s*,\s*`).Split(list, -1) +} + +// parseParam splits a parameter into name and type +func parseParam(p string) Param { + ps := regexp.MustCompile(`^(\S*) (\S*)$`).FindStringSubmatch(p) + if ps == nil { + fmt.Fprintf(os.Stderr, "malformed parameter: %s\n", p) + os.Exit(1) + } + return Param{ps[1], ps[2]} +} + +func main() { + flag.Usage = usage + flag.Parse() + if len(flag.Args()) <= 0 { + fmt.Fprintf(os.Stderr, "no files to parse provided\n") + usage() + } + + endianness := "" + if *b32 { + endianness = "big-endian" + } else if *l32 { + endianness = "little-endian" + } + + pack := "" + text := "" + dynimports := "" + linknames := "" + var vars []string + for _, path := range flag.Args() { + file, err := os.Open(path) + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + s := bufio.NewScanner(file) + for s.Scan() { + t := s.Text() + t = strings.TrimSpace(t) + t = regexp.MustCompile(`\s+`).ReplaceAllString(t, ` `) + if p := regexp.MustCompile(`^package (\S+)$`).FindStringSubmatch(t); p != nil && pack == "" { + pack = p[1] + } + nonblock := regexp.MustCompile(`^\/\/sysnb `).FindStringSubmatch(t) + if regexp.MustCompile(`^\/\/sys `).FindStringSubmatch(t) == nil && nonblock == nil { + continue + } + + // Line must be of the form + // func Open(path string, mode int, perm int) (fd int, err error) + // Split into name, in params, out params. + f := regexp.MustCompile(`^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*(?:(\w*)\.)?(\w*))?$`).FindStringSubmatch(t) + if f == nil { + fmt.Fprintf(os.Stderr, "%s:%s\nmalformed //sys declaration\n", path, t) + os.Exit(1) + } + funct, inps, outps, modname, sysname := f[2], f[3], f[4], f[5], f[6] + + // Split argument lists on comma. + in := parseParamList(inps) + out := parseParamList(outps) + + inps = strings.Join(in, ", ") + outps = strings.Join(out, ", ") + + // Try in vain to keep people from editing this file. + // The theory is that they jump into the middle of the file + // without reading the header. + text += "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n" + + // So file name. + if modname == "" { + modname = "libc" + } + + // System call name. + if sysname == "" { + sysname = funct + } + + // System call pointer variable name. + sysvarname := fmt.Sprintf("proc%s", sysname) + + strconvfunc := "BytePtrFromString" + strconvtype := "*byte" + + sysname = strings.ToLower(sysname) // All libc functions are lowercase. + + // Runtime import of function to allow cross-platform builds. + dynimports += fmt.Sprintf("//go:cgo_import_dynamic libc_%s %s \"%s.so\"\n", sysname, sysname, modname) + // Link symbol to proc address variable. + linknames += fmt.Sprintf("//go:linkname %s libc_%s\n", sysvarname, sysname) + // Library proc address variable. + vars = append(vars, sysvarname) + + // Go function header. + outlist := strings.Join(out, ", ") + if outlist != "" { + outlist = fmt.Sprintf(" (%s)", outlist) + } + if text != "" { + text += "\n" + } + text += fmt.Sprintf("func %s(%s)%s {\n", funct, strings.Join(in, ", "), outlist) + + // Check if err return available + errvar := "" + for _, param := range out { + p := parseParam(param) + if p.Type == "error" { + errvar = p.Name + continue + } + } + + // Prepare arguments to Syscall. + var args []string + n := 0 + for _, param := range in { + p := parseParam(param) + if regexp.MustCompile(`^\*`).FindStringSubmatch(p.Type) != nil { + args = append(args, "uintptr(unsafe.Pointer("+p.Name+"))") + } else if p.Type == "string" && errvar != "" { + text += fmt.Sprintf("\tvar _p%d %s\n", n, strconvtype) + text += fmt.Sprintf("\t_p%d, %s = %s(%s)\n", n, errvar, strconvfunc, p.Name) + text += fmt.Sprintf("\tif %s != nil {\n\t\treturn\n\t}\n", errvar) + args = append(args, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n)) + n++ + } else if p.Type == "string" { + fmt.Fprintf(os.Stderr, path+":"+funct+" uses string arguments, but has no error return\n") + text += fmt.Sprintf("\tvar _p%d %s\n", n, strconvtype) + text += fmt.Sprintf("\t_p%d, _ = %s(%s)\n", n, strconvfunc, p.Name) + args = append(args, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n)) + n++ + } else if s := regexp.MustCompile(`^\[\](.*)`).FindStringSubmatch(p.Type); s != nil { + // Convert slice into pointer, length. + // Have to be careful not to take address of &a[0] if len == 0: + // pass nil in that case. + text += fmt.Sprintf("\tvar _p%d *%s\n", n, s[1]) + text += fmt.Sprintf("\tif len(%s) > 0 {\n\t\t_p%d = &%s[0]\n\t}\n", p.Name, n, p.Name) + args = append(args, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n), fmt.Sprintf("uintptr(len(%s))", p.Name)) + n++ + } else if p.Type == "int64" && endianness != "" { + if endianness == "big-endian" { + args = append(args, fmt.Sprintf("uintptr(%s>>32)", p.Name), fmt.Sprintf("uintptr(%s)", p.Name)) + } else { + args = append(args, fmt.Sprintf("uintptr(%s)", p.Name), fmt.Sprintf("uintptr(%s>>32)", p.Name)) + } + } else if p.Type == "bool" { + text += fmt.Sprintf("\tvar _p%d uint32\n", n) + text += fmt.Sprintf("\tif %s {\n\t\t_p%d = 1\n\t} else {\n\t\t_p%d = 0\n\t}\n", p.Name, n, n) + args = append(args, fmt.Sprintf("uintptr(_p%d)", n)) + n++ + } else { + args = append(args, fmt.Sprintf("uintptr(%s)", p.Name)) + } + } + nargs := len(args) + + // Determine which form to use; pad args with zeros. + asm := "sysvicall6" + if nonblock != nil { + asm = "rawSysvicall6" + } + if len(args) <= 6 { + for len(args) < 6 { + args = append(args, "0") + } + } else { + fmt.Fprintf(os.Stderr, "%s: too many arguments to system call\n", path) + os.Exit(1) + } + + // Actual call. + arglist := strings.Join(args, ", ") + call := fmt.Sprintf("%s(uintptr(unsafe.Pointer(&%s)), %d, %s)", asm, sysvarname, nargs, arglist) + + // Assign return values. + body := "" + ret := []string{"_", "_", "_"} + doErrno := false + for i := 0; i < len(out); i++ { + p := parseParam(out[i]) + reg := "" + if p.Name == "err" { + reg = "e1" + ret[2] = reg + doErrno = true + } else { + reg = fmt.Sprintf("r%d", i) + ret[i] = reg + } + if p.Type == "bool" { + reg = fmt.Sprintf("%d != 0", reg) + } + if p.Type == "int64" && endianness != "" { + // 64-bit number in r1:r0 or r0:r1. + if i+2 > len(out) { + fmt.Fprintf(os.Stderr, "%s: not enough registers for int64 return\n", path) + os.Exit(1) + } + if endianness == "big-endian" { + reg = fmt.Sprintf("int64(r%d)<<32 | int64(r%d)", i, i+1) + } else { + reg = fmt.Sprintf("int64(r%d)<<32 | int64(r%d)", i+1, i) + } + ret[i] = fmt.Sprintf("r%d", i) + ret[i+1] = fmt.Sprintf("r%d", i+1) + } + if reg != "e1" { + body += fmt.Sprintf("\t%s = %s(%s)\n", p.Name, p.Type, reg) + } + } + if ret[0] == "_" && ret[1] == "_" && ret[2] == "_" { + text += fmt.Sprintf("\t%s\n", call) + } else { + text += fmt.Sprintf("\t%s, %s, %s := %s\n", ret[0], ret[1], ret[2], call) + } + text += body + + if doErrno { + text += "\tif e1 != 0 {\n" + text += "\t\terr = e1\n" + text += "\t}\n" + } + text += "\treturn\n" + text += "}\n" + } + if err := s.Err(); err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + file.Close() + } + imp := "" + if pack != "unix" { + imp = "import \"golang.org/x/sys/unix\"\n" + + } + vardecls := "\t" + strings.Join(vars, ",\n\t") + vardecls += " syscallFunc" + fmt.Printf(srcTemplate, cmdLine(), buildTags(), pack, imp, dynimports, linknames, vardecls, text) +} + +const srcTemplate = `// %s +// Code generated by the command above; see README.md. DO NOT EDIT. + +// +build %s + +package %s + +import ( + "syscall" + "unsafe" +) +%s +%s +%s +var ( +%s +) + +%s +` diff --git a/vendor/golang.org/x/sys/unix/mksysctl_openbsd.go b/vendor/golang.org/x/sys/unix/mksysctl_openbsd.go new file mode 100644 index 0000000000..b6b409909c --- /dev/null +++ b/vendor/golang.org/x/sys/unix/mksysctl_openbsd.go @@ -0,0 +1,355 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// Parse the header files for OpenBSD and generate a Go usable sysctl MIB. +// +// Build a MIB with each entry being an array containing the level, type and +// a hash that will contain additional entries if the current entry is a node. +// We then walk this MIB and create a flattened sysctl name to OID hash. + +package main + +import ( + "bufio" + "fmt" + "os" + "path/filepath" + "regexp" + "sort" + "strings" +) + +var ( + goos, goarch string +) + +// cmdLine returns this programs's commandline arguments. +func cmdLine() string { + return "go run mksysctl_openbsd.go " + strings.Join(os.Args[1:], " ") +} + +// buildTags returns build tags. +func buildTags() string { + return fmt.Sprintf("%s,%s", goarch, goos) +} + +// reMatch performs regular expression match and stores the substring slice to value pointed by m. +func reMatch(re *regexp.Regexp, str string, m *[]string) bool { + *m = re.FindStringSubmatch(str) + if *m != nil { + return true + } + return false +} + +type nodeElement struct { + n int + t string + pE *map[string]nodeElement +} + +var ( + debugEnabled bool + mib map[string]nodeElement + node *map[string]nodeElement + nodeMap map[string]string + sysCtl []string +) + +var ( + ctlNames1RE = regexp.MustCompile(`^#define\s+(CTL_NAMES)\s+{`) + ctlNames2RE = regexp.MustCompile(`^#define\s+(CTL_(.*)_NAMES)\s+{`) + ctlNames3RE = regexp.MustCompile(`^#define\s+((.*)CTL_NAMES)\s+{`) + netInetRE = regexp.MustCompile(`^netinet/`) + netInet6RE = regexp.MustCompile(`^netinet6/`) + netRE = regexp.MustCompile(`^net/`) + bracesRE = regexp.MustCompile(`{.*}`) + ctlTypeRE = regexp.MustCompile(`{\s+"(\w+)",\s+(CTLTYPE_[A-Z]+)\s+}`) + fsNetKernRE = regexp.MustCompile(`^(fs|net|kern)_`) +) + +func debug(s string) { + if debugEnabled { + fmt.Fprintln(os.Stderr, s) + } +} + +// Walk the MIB and build a sysctl name to OID mapping. +func buildSysctl(pNode *map[string]nodeElement, name string, oid []int) { + lNode := pNode // local copy of pointer to node + var keys []string + for k := range *lNode { + keys = append(keys, k) + } + sort.Strings(keys) + + for _, key := range keys { + nodename := name + if name != "" { + nodename += "." + } + nodename += key + + nodeoid := append(oid, (*pNode)[key].n) + + if (*pNode)[key].t == `CTLTYPE_NODE` { + if _, ok := nodeMap[nodename]; ok { + lNode = &mib + ctlName := nodeMap[nodename] + for _, part := range strings.Split(ctlName, ".") { + lNode = ((*lNode)[part]).pE + } + } else { + lNode = (*pNode)[key].pE + } + buildSysctl(lNode, nodename, nodeoid) + } else if (*pNode)[key].t != "" { + oidStr := []string{} + for j := range nodeoid { + oidStr = append(oidStr, fmt.Sprintf("%d", nodeoid[j])) + } + text := "\t{ \"" + nodename + "\", []_C_int{ " + strings.Join(oidStr, ", ") + " } }, \n" + sysCtl = append(sysCtl, text) + } + } +} + +func main() { + // Get the OS (using GOOS_TARGET if it exist) + goos = os.Getenv("GOOS_TARGET") + if goos == "" { + goos = os.Getenv("GOOS") + } + // Get the architecture (using GOARCH_TARGET if it exists) + goarch = os.Getenv("GOARCH_TARGET") + if goarch == "" { + goarch = os.Getenv("GOARCH") + } + // Check if GOOS and GOARCH environment variables are defined + if goarch == "" || goos == "" { + fmt.Fprintf(os.Stderr, "GOARCH or GOOS not defined in environment\n") + os.Exit(1) + } + + mib = make(map[string]nodeElement) + headers := [...]string{ + `sys/sysctl.h`, + `sys/socket.h`, + `sys/tty.h`, + `sys/malloc.h`, + `sys/mount.h`, + `sys/namei.h`, + `sys/sem.h`, + `sys/shm.h`, + `sys/vmmeter.h`, + `uvm/uvmexp.h`, + `uvm/uvm_param.h`, + `uvm/uvm_swap_encrypt.h`, + `ddb/db_var.h`, + `net/if.h`, + `net/if_pfsync.h`, + `net/pipex.h`, + `netinet/in.h`, + `netinet/icmp_var.h`, + `netinet/igmp_var.h`, + `netinet/ip_ah.h`, + `netinet/ip_carp.h`, + `netinet/ip_divert.h`, + `netinet/ip_esp.h`, + `netinet/ip_ether.h`, + `netinet/ip_gre.h`, + `netinet/ip_ipcomp.h`, + `netinet/ip_ipip.h`, + `netinet/pim_var.h`, + `netinet/tcp_var.h`, + `netinet/udp_var.h`, + `netinet6/in6.h`, + `netinet6/ip6_divert.h`, + `netinet6/pim6_var.h`, + `netinet/icmp6.h`, + `netmpls/mpls.h`, + } + + ctls := [...]string{ + `kern`, + `vm`, + `fs`, + `net`, + //debug /* Special handling required */ + `hw`, + //machdep /* Arch specific */ + `user`, + `ddb`, + //vfs /* Special handling required */ + `fs.posix`, + `kern.forkstat`, + `kern.intrcnt`, + `kern.malloc`, + `kern.nchstats`, + `kern.seminfo`, + `kern.shminfo`, + `kern.timecounter`, + `kern.tty`, + `kern.watchdog`, + `net.bpf`, + `net.ifq`, + `net.inet`, + `net.inet.ah`, + `net.inet.carp`, + `net.inet.divert`, + `net.inet.esp`, + `net.inet.etherip`, + `net.inet.gre`, + `net.inet.icmp`, + `net.inet.igmp`, + `net.inet.ip`, + `net.inet.ip.ifq`, + `net.inet.ipcomp`, + `net.inet.ipip`, + `net.inet.mobileip`, + `net.inet.pfsync`, + `net.inet.pim`, + `net.inet.tcp`, + `net.inet.udp`, + `net.inet6`, + `net.inet6.divert`, + `net.inet6.ip6`, + `net.inet6.icmp6`, + `net.inet6.pim6`, + `net.inet6.tcp6`, + `net.inet6.udp6`, + `net.mpls`, + `net.mpls.ifq`, + `net.key`, + `net.pflow`, + `net.pfsync`, + `net.pipex`, + `net.rt`, + `vm.swapencrypt`, + //vfsgenctl /* Special handling required */ + } + + // Node name "fixups" + ctlMap := map[string]string{ + "ipproto": "net.inet", + "net.inet.ipproto": "net.inet", + "net.inet6.ipv6proto": "net.inet6", + "net.inet6.ipv6": "net.inet6.ip6", + "net.inet.icmpv6": "net.inet6.icmp6", + "net.inet6.divert6": "net.inet6.divert", + "net.inet6.tcp6": "net.inet.tcp", + "net.inet6.udp6": "net.inet.udp", + "mpls": "net.mpls", + "swpenc": "vm.swapencrypt", + } + + // Node mappings + nodeMap = map[string]string{ + "net.inet.ip.ifq": "net.ifq", + "net.inet.pfsync": "net.pfsync", + "net.mpls.ifq": "net.ifq", + } + + mCtls := make(map[string]bool) + for _, ctl := range ctls { + mCtls[ctl] = true + } + + for _, header := range headers { + debug("Processing " + header) + file, err := os.Open(filepath.Join("/usr/include", header)) + if err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + os.Exit(1) + } + s := bufio.NewScanner(file) + for s.Scan() { + var sub []string + if reMatch(ctlNames1RE, s.Text(), &sub) || + reMatch(ctlNames2RE, s.Text(), &sub) || + reMatch(ctlNames3RE, s.Text(), &sub) { + if sub[1] == `CTL_NAMES` { + // Top level. + node = &mib + } else { + // Node. + nodename := strings.ToLower(sub[2]) + ctlName := "" + if reMatch(netInetRE, header, &sub) { + ctlName = "net.inet." + nodename + } else if reMatch(netInet6RE, header, &sub) { + ctlName = "net.inet6." + nodename + } else if reMatch(netRE, header, &sub) { + ctlName = "net." + nodename + } else { + ctlName = nodename + ctlName = fsNetKernRE.ReplaceAllString(ctlName, `$1.`) + } + + if val, ok := ctlMap[ctlName]; ok { + ctlName = val + } + if _, ok := mCtls[ctlName]; !ok { + debug("Ignoring " + ctlName + "...") + continue + } + + // Walk down from the top of the MIB. + node = &mib + for _, part := range strings.Split(ctlName, ".") { + if _, ok := (*node)[part]; !ok { + debug("Missing node " + part) + (*node)[part] = nodeElement{n: 0, t: "", pE: &map[string]nodeElement{}} + } + node = (*node)[part].pE + } + } + + // Populate current node with entries. + i := -1 + for !strings.HasPrefix(s.Text(), "}") { + s.Scan() + if reMatch(bracesRE, s.Text(), &sub) { + i++ + } + if !reMatch(ctlTypeRE, s.Text(), &sub) { + continue + } + (*node)[sub[1]] = nodeElement{n: i, t: sub[2], pE: &map[string]nodeElement{}} + } + } + } + err = s.Err() + if err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + os.Exit(1) + } + file.Close() + } + buildSysctl(&mib, "", []int{}) + + sort.Strings(sysCtl) + text := strings.Join(sysCtl, "") + + fmt.Printf(srcTemplate, cmdLine(), buildTags(), text) +} + +const srcTemplate = `// %s +// Code generated by the command above; DO NOT EDIT. + +// +build %s + +package unix + +type mibentry struct { + ctlname string + ctloid []_C_int +} + +var sysctlMib = []mibentry { +%s +} +` diff --git a/vendor/golang.org/x/sys/unix/mksysnum.go b/vendor/golang.org/x/sys/unix/mksysnum.go new file mode 100644 index 0000000000..baa6ecd850 --- /dev/null +++ b/vendor/golang.org/x/sys/unix/mksysnum.go @@ -0,0 +1,190 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// Generate system call table for DragonFly, NetBSD, +// FreeBSD, OpenBSD or Darwin from master list +// (for example, /usr/src/sys/kern/syscalls.master or +// sys/syscall.h). +package main + +import ( + "bufio" + "fmt" + "io" + "io/ioutil" + "net/http" + "os" + "regexp" + "strings" +) + +var ( + goos, goarch string +) + +// cmdLine returns this programs's commandline arguments +func cmdLine() string { + return "go run mksysnum.go " + strings.Join(os.Args[1:], " ") +} + +// buildTags returns build tags +func buildTags() string { + return fmt.Sprintf("%s,%s", goarch, goos) +} + +func checkErr(err error) { + if err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + os.Exit(1) + } +} + +// source string and substring slice for regexp +type re struct { + str string // source string + sub []string // matched sub-string +} + +// Match performs regular expression match +func (r *re) Match(exp string) bool { + r.sub = regexp.MustCompile(exp).FindStringSubmatch(r.str) + if r.sub != nil { + return true + } + return false +} + +// fetchFile fetches a text file from URL +func fetchFile(URL string) io.Reader { + resp, err := http.Get(URL) + checkErr(err) + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + checkErr(err) + return strings.NewReader(string(body)) +} + +// readFile reads a text file from path +func readFile(path string) io.Reader { + file, err := os.Open(os.Args[1]) + checkErr(err) + return file +} + +func format(name, num, proto string) string { + name = strings.ToUpper(name) + // There are multiple entries for enosys and nosys, so comment them out. + nm := re{str: name} + if nm.Match(`^SYS_E?NOSYS$`) { + name = fmt.Sprintf("// %s", name) + } + if name == `SYS_SYS_EXIT` { + name = `SYS_EXIT` + } + return fmt.Sprintf(" %s = %s; // %s\n", name, num, proto) +} + +func main() { + // Get the OS (using GOOS_TARGET if it exist) + goos = os.Getenv("GOOS_TARGET") + if goos == "" { + goos = os.Getenv("GOOS") + } + // Get the architecture (using GOARCH_TARGET if it exists) + goarch = os.Getenv("GOARCH_TARGET") + if goarch == "" { + goarch = os.Getenv("GOARCH") + } + // Check if GOOS and GOARCH environment variables are defined + if goarch == "" || goos == "" { + fmt.Fprintf(os.Stderr, "GOARCH or GOOS not defined in environment\n") + os.Exit(1) + } + + file := strings.TrimSpace(os.Args[1]) + var syscalls io.Reader + if strings.HasPrefix(file, "https://") || strings.HasPrefix(file, "http://") { + // Download syscalls.master file + syscalls = fetchFile(file) + } else { + syscalls = readFile(file) + } + + var text, line string + s := bufio.NewScanner(syscalls) + for s.Scan() { + t := re{str: line} + if t.Match(`^(.*)\\$`) { + // Handle continuation + line = t.sub[1] + line += strings.TrimLeft(s.Text(), " \t") + } else { + // New line + line = s.Text() + } + t = re{str: line} + if t.Match(`\\$`) { + continue + } + t = re{str: line} + + switch goos { + case "dragonfly": + if t.Match(`^([0-9]+)\s+STD\s+({ \S+\s+(\w+).*)$`) { + num, proto := t.sub[1], t.sub[2] + name := fmt.Sprintf("SYS_%s", t.sub[3]) + text += format(name, num, proto) + } + case "freebsd": + if t.Match(`^([0-9]+)\s+\S+\s+(?:(?:NO)?STD|COMPAT10)\s+({ \S+\s+(\w+).*)$`) { + num, proto := t.sub[1], t.sub[2] + name := fmt.Sprintf("SYS_%s", t.sub[3]) + text += format(name, num, proto) + } + case "openbsd": + if t.Match(`^([0-9]+)\s+STD\s+(NOLOCK\s+)?({ \S+\s+\*?(\w+).*)$`) { + num, proto, name := t.sub[1], t.sub[3], t.sub[4] + text += format(name, num, proto) + } + case "netbsd": + if t.Match(`^([0-9]+)\s+((STD)|(NOERR))\s+(RUMP\s+)?({\s+\S+\s*\*?\s*\|(\S+)\|(\S*)\|(\w+).*\s+})(\s+(\S+))?$`) { + num, proto, compat := t.sub[1], t.sub[6], t.sub[8] + name := t.sub[7] + "_" + t.sub[9] + if t.sub[11] != "" { + name = t.sub[7] + "_" + t.sub[11] + } + name = strings.ToUpper(name) + if compat == "" || compat == "13" || compat == "30" || compat == "50" { + text += fmt.Sprintf(" %s = %s; // %s\n", name, num, proto) + } + } + case "darwin": + if t.Match(`^#define\s+SYS_(\w+)\s+([0-9]+)`) { + name, num := t.sub[1], t.sub[2] + name = strings.ToUpper(name) + text += fmt.Sprintf(" SYS_%s = %s;\n", name, num) + } + default: + fmt.Fprintf(os.Stderr, "unrecognized GOOS=%s\n", goos) + os.Exit(1) + + } + } + err := s.Err() + checkErr(err) + + fmt.Printf(template, cmdLine(), buildTags(), text) +} + +const template = `// %s +// Code generated by the command above; see README.md. DO NOT EDIT. + +// +build %s + +package unix + +const( +%s)` diff --git a/vendor/golang.org/x/sys/unix/types_aix.go b/vendor/golang.org/x/sys/unix/types_aix.go new file mode 100644 index 0000000000..40d2beede5 --- /dev/null +++ b/vendor/golang.org/x/sys/unix/types_aix.go @@ -0,0 +1,237 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore +// +build aix + +/* +Input to cgo -godefs. See also mkerrors.sh and mkall.sh +*/ + +// +godefs map struct_in_addr [4]byte /* in_addr */ +// +godefs map struct_in6_addr [16]byte /* in6_addr */ + +package unix + +/* +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + + +#include +#include + +enum { + sizeofPtr = sizeof(void*), +}; + +union sockaddr_all { + struct sockaddr s1; // this one gets used for fields + struct sockaddr_in s2; // these pad it out + struct sockaddr_in6 s3; + struct sockaddr_un s4; + struct sockaddr_dl s5; +}; + +struct sockaddr_any { + struct sockaddr addr; + char pad[sizeof(union sockaddr_all) - sizeof(struct sockaddr)]; +}; + +*/ +import "C" + +// Machine characteristics + +const ( + SizeofPtr = C.sizeofPtr + SizeofShort = C.sizeof_short + SizeofInt = C.sizeof_int + SizeofLong = C.sizeof_long + SizeofLongLong = C.sizeof_longlong + PathMax = C.PATH_MAX +) + +// Basic types + +type ( + _C_short C.short + _C_int C.int + _C_long C.long + _C_long_long C.longlong +) + +type off64 C.off64_t +type off C.off_t +type Mode_t C.mode_t + +// Time + +type Timespec C.struct_timespec + +type Timeval C.struct_timeval + +type Timeval32 C.struct_timeval32 + +type Timex C.struct_timex + +type Time_t C.time_t + +type Tms C.struct_tms + +type Utimbuf C.struct_utimbuf + +type Timezone C.struct_timezone + +// Processes + +type Rusage C.struct_rusage + +type Rlimit C.struct_rlimit64 + +type Pid_t C.pid_t + +type _Gid_t C.gid_t + +type dev_t C.dev_t + +// Files + +type Stat_t C.struct_stat + +type StatxTimestamp C.struct_statx_timestamp + +type Statx_t C.struct_statx + +type Dirent C.struct_dirent + +// Sockets + +type RawSockaddrInet4 C.struct_sockaddr_in + +type RawSockaddrInet6 C.struct_sockaddr_in6 + +type RawSockaddrUnix C.struct_sockaddr_un + +type RawSockaddrDatalink C.struct_sockaddr_dl + +type RawSockaddr C.struct_sockaddr + +type RawSockaddrAny C.struct_sockaddr_any + +type _Socklen C.socklen_t + +type Cmsghdr C.struct_cmsghdr + +type ICMPv6Filter C.struct_icmp6_filter + +type Iovec C.struct_iovec + +type IPMreq C.struct_ip_mreq + +type IPv6Mreq C.struct_ipv6_mreq + +type IPv6MTUInfo C.struct_ip6_mtuinfo + +type Linger C.struct_linger + +type Msghdr C.struct_msghdr + +const ( + SizeofSockaddrInet4 = C.sizeof_struct_sockaddr_in + SizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 + SizeofSockaddrAny = C.sizeof_struct_sockaddr_any + SizeofSockaddrUnix = C.sizeof_struct_sockaddr_un + SizeofSockaddrDatalink = C.sizeof_struct_sockaddr_dl + SizeofLinger = C.sizeof_struct_linger + SizeofIPMreq = C.sizeof_struct_ip_mreq + SizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq + SizeofIPv6MTUInfo = C.sizeof_struct_ip6_mtuinfo + SizeofMsghdr = C.sizeof_struct_msghdr + SizeofCmsghdr = C.sizeof_struct_cmsghdr + SizeofICMPv6Filter = C.sizeof_struct_icmp6_filter +) + +// Routing and interface messages + +const ( + SizeofIfMsghdr = C.sizeof_struct_if_msghdr +) + +type IfMsgHdr C.struct_if_msghdr + +// Misc + +type FdSet C.fd_set + +type Utsname C.struct_utsname + +type Ustat_t C.struct_ustat + +type Sigset_t C.sigset_t + +const ( + AT_FDCWD = C.AT_FDCWD + AT_REMOVEDIR = C.AT_REMOVEDIR + AT_SYMLINK_NOFOLLOW = C.AT_SYMLINK_NOFOLLOW +) + +// Terminal handling + +type Termios C.struct_termios + +type Termio C.struct_termio + +type Winsize C.struct_winsize + +//poll + +type PollFd struct { + Fd int32 + Events uint16 + Revents uint16 +} + +const ( + POLLERR = C.POLLERR + POLLHUP = C.POLLHUP + POLLIN = C.POLLIN + POLLNVAL = C.POLLNVAL + POLLOUT = C.POLLOUT + POLLPRI = C.POLLPRI + POLLRDBAND = C.POLLRDBAND + POLLRDNORM = C.POLLRDNORM + POLLWRBAND = C.POLLWRBAND + POLLWRNORM = C.POLLWRNORM +) + +//flock_t + +type Flock_t C.struct_flock64 + +// Statfs + +type Fsid_t C.struct_fsid_t +type Fsid64_t C.struct_fsid64_t + +type Statfs_t C.struct_statfs + +const RNDGETENTCNT = 0x80045200 diff --git a/vendor/golang.org/x/sys/unix/types_darwin.go b/vendor/golang.org/x/sys/unix/types_darwin.go new file mode 100644 index 0000000000..155c2e692b --- /dev/null +++ b/vendor/golang.org/x/sys/unix/types_darwin.go @@ -0,0 +1,283 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +/* +Input to cgo -godefs. See README.md +*/ + +// +godefs map struct_in_addr [4]byte /* in_addr */ +// +godefs map struct_in6_addr [16]byte /* in6_addr */ + +package unix + +/* +#define __DARWIN_UNIX03 0 +#define KERNEL +#define _DARWIN_USE_64_BIT_INODE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + sizeofPtr = sizeof(void*), +}; + +union sockaddr_all { + struct sockaddr s1; // this one gets used for fields + struct sockaddr_in s2; // these pad it out + struct sockaddr_in6 s3; + struct sockaddr_un s4; + struct sockaddr_dl s5; +}; + +struct sockaddr_any { + struct sockaddr addr; + char pad[sizeof(union sockaddr_all) - sizeof(struct sockaddr)]; +}; + +*/ +import "C" + +// Machine characteristics + +const ( + SizeofPtr = C.sizeofPtr + SizeofShort = C.sizeof_short + SizeofInt = C.sizeof_int + SizeofLong = C.sizeof_long + SizeofLongLong = C.sizeof_longlong +) + +// Basic types + +type ( + _C_short C.short + _C_int C.int + _C_long C.long + _C_long_long C.longlong +) + +// Time + +type Timespec C.struct_timespec + +type Timeval C.struct_timeval + +type Timeval32 C.struct_timeval32 + +// Processes + +type Rusage C.struct_rusage + +type Rlimit C.struct_rlimit + +type _Gid_t C.gid_t + +// Files + +type Stat_t C.struct_stat64 + +type Statfs_t C.struct_statfs64 + +type Flock_t C.struct_flock + +type Fstore_t C.struct_fstore + +type Radvisory_t C.struct_radvisory + +type Fbootstraptransfer_t C.struct_fbootstraptransfer + +type Log2phys_t C.struct_log2phys + +type Fsid C.struct_fsid + +type Dirent C.struct_dirent + +// Sockets + +type RawSockaddrInet4 C.struct_sockaddr_in + +type RawSockaddrInet6 C.struct_sockaddr_in6 + +type RawSockaddrUnix C.struct_sockaddr_un + +type RawSockaddrDatalink C.struct_sockaddr_dl + +type RawSockaddr C.struct_sockaddr + +type RawSockaddrAny C.struct_sockaddr_any + +type _Socklen C.socklen_t + +type Linger C.struct_linger + +type Iovec C.struct_iovec + +type IPMreq C.struct_ip_mreq + +type IPv6Mreq C.struct_ipv6_mreq + +type Msghdr C.struct_msghdr + +type Cmsghdr C.struct_cmsghdr + +type Inet4Pktinfo C.struct_in_pktinfo + +type Inet6Pktinfo C.struct_in6_pktinfo + +type IPv6MTUInfo C.struct_ip6_mtuinfo + +type ICMPv6Filter C.struct_icmp6_filter + +const ( + SizeofSockaddrInet4 = C.sizeof_struct_sockaddr_in + SizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 + SizeofSockaddrAny = C.sizeof_struct_sockaddr_any + SizeofSockaddrUnix = C.sizeof_struct_sockaddr_un + SizeofSockaddrDatalink = C.sizeof_struct_sockaddr_dl + SizeofLinger = C.sizeof_struct_linger + SizeofIPMreq = C.sizeof_struct_ip_mreq + SizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq + SizeofMsghdr = C.sizeof_struct_msghdr + SizeofCmsghdr = C.sizeof_struct_cmsghdr + SizeofInet4Pktinfo = C.sizeof_struct_in_pktinfo + SizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo + SizeofIPv6MTUInfo = C.sizeof_struct_ip6_mtuinfo + SizeofICMPv6Filter = C.sizeof_struct_icmp6_filter +) + +// Ptrace requests + +const ( + PTRACE_TRACEME = C.PT_TRACE_ME + PTRACE_CONT = C.PT_CONTINUE + PTRACE_KILL = C.PT_KILL +) + +// Events (kqueue, kevent) + +type Kevent_t C.struct_kevent + +// Select + +type FdSet C.fd_set + +// Routing and interface messages + +const ( + SizeofIfMsghdr = C.sizeof_struct_if_msghdr + SizeofIfData = C.sizeof_struct_if_data + SizeofIfaMsghdr = C.sizeof_struct_ifa_msghdr + SizeofIfmaMsghdr = C.sizeof_struct_ifma_msghdr + SizeofIfmaMsghdr2 = C.sizeof_struct_ifma_msghdr2 + SizeofRtMsghdr = C.sizeof_struct_rt_msghdr + SizeofRtMetrics = C.sizeof_struct_rt_metrics +) + +type IfMsghdr C.struct_if_msghdr + +type IfData C.struct_if_data + +type IfaMsghdr C.struct_ifa_msghdr + +type IfmaMsghdr C.struct_ifma_msghdr + +type IfmaMsghdr2 C.struct_ifma_msghdr2 + +type RtMsghdr C.struct_rt_msghdr + +type RtMetrics C.struct_rt_metrics + +// Berkeley packet filter + +const ( + SizeofBpfVersion = C.sizeof_struct_bpf_version + SizeofBpfStat = C.sizeof_struct_bpf_stat + SizeofBpfProgram = C.sizeof_struct_bpf_program + SizeofBpfInsn = C.sizeof_struct_bpf_insn + SizeofBpfHdr = C.sizeof_struct_bpf_hdr +) + +type BpfVersion C.struct_bpf_version + +type BpfStat C.struct_bpf_stat + +type BpfProgram C.struct_bpf_program + +type BpfInsn C.struct_bpf_insn + +type BpfHdr C.struct_bpf_hdr + +// Terminal handling + +type Termios C.struct_termios + +type Winsize C.struct_winsize + +// fchmodat-like syscalls. + +const ( + AT_FDCWD = C.AT_FDCWD + AT_REMOVEDIR = C.AT_REMOVEDIR + AT_SYMLINK_FOLLOW = C.AT_SYMLINK_FOLLOW + AT_SYMLINK_NOFOLLOW = C.AT_SYMLINK_NOFOLLOW +) + +// poll + +type PollFd C.struct_pollfd + +const ( + POLLERR = C.POLLERR + POLLHUP = C.POLLHUP + POLLIN = C.POLLIN + POLLNVAL = C.POLLNVAL + POLLOUT = C.POLLOUT + POLLPRI = C.POLLPRI + POLLRDBAND = C.POLLRDBAND + POLLRDNORM = C.POLLRDNORM + POLLWRBAND = C.POLLWRBAND + POLLWRNORM = C.POLLWRNORM +) + +// uname + +type Utsname C.struct_utsname + +// Clockinfo + +const SizeofClockinfo = C.sizeof_struct_clockinfo + +type Clockinfo C.struct_clockinfo diff --git a/vendor/golang.org/x/sys/unix/types_dragonfly.go b/vendor/golang.org/x/sys/unix/types_dragonfly.go new file mode 100644 index 0000000000..3365dd79d0 --- /dev/null +++ b/vendor/golang.org/x/sys/unix/types_dragonfly.go @@ -0,0 +1,263 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +/* +Input to cgo -godefs. See README.md +*/ + +// +godefs map struct_in_addr [4]byte /* in_addr */ +// +godefs map struct_in6_addr [16]byte /* in6_addr */ + +package unix + +/* +#define KERNEL +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + sizeofPtr = sizeof(void*), +}; + +union sockaddr_all { + struct sockaddr s1; // this one gets used for fields + struct sockaddr_in s2; // these pad it out + struct sockaddr_in6 s3; + struct sockaddr_un s4; + struct sockaddr_dl s5; +}; + +struct sockaddr_any { + struct sockaddr addr; + char pad[sizeof(union sockaddr_all) - sizeof(struct sockaddr)]; +}; + +*/ +import "C" + +// Machine characteristics + +const ( + SizeofPtr = C.sizeofPtr + SizeofShort = C.sizeof_short + SizeofInt = C.sizeof_int + SizeofLong = C.sizeof_long + SizeofLongLong = C.sizeof_longlong +) + +// Basic types + +type ( + _C_short C.short + _C_int C.int + _C_long C.long + _C_long_long C.longlong +) + +// Time + +type Timespec C.struct_timespec + +type Timeval C.struct_timeval + +// Processes + +type Rusage C.struct_rusage + +type Rlimit C.struct_rlimit + +type _Gid_t C.gid_t + +// Files + +type Stat_t C.struct_stat + +type Statfs_t C.struct_statfs + +type Flock_t C.struct_flock + +type Dirent C.struct_dirent + +type Fsid C.struct_fsid + +// File system limits + +const ( + PathMax = C.PATH_MAX +) + +// Sockets + +type RawSockaddrInet4 C.struct_sockaddr_in + +type RawSockaddrInet6 C.struct_sockaddr_in6 + +type RawSockaddrUnix C.struct_sockaddr_un + +type RawSockaddrDatalink C.struct_sockaddr_dl + +type RawSockaddr C.struct_sockaddr + +type RawSockaddrAny C.struct_sockaddr_any + +type _Socklen C.socklen_t + +type Linger C.struct_linger + +type Iovec C.struct_iovec + +type IPMreq C.struct_ip_mreq + +type IPv6Mreq C.struct_ipv6_mreq + +type Msghdr C.struct_msghdr + +type Cmsghdr C.struct_cmsghdr + +type Inet6Pktinfo C.struct_in6_pktinfo + +type IPv6MTUInfo C.struct_ip6_mtuinfo + +type ICMPv6Filter C.struct_icmp6_filter + +const ( + SizeofSockaddrInet4 = C.sizeof_struct_sockaddr_in + SizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 + SizeofSockaddrAny = C.sizeof_struct_sockaddr_any + SizeofSockaddrUnix = C.sizeof_struct_sockaddr_un + SizeofSockaddrDatalink = C.sizeof_struct_sockaddr_dl + SizeofLinger = C.sizeof_struct_linger + SizeofIPMreq = C.sizeof_struct_ip_mreq + SizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq + SizeofMsghdr = C.sizeof_struct_msghdr + SizeofCmsghdr = C.sizeof_struct_cmsghdr + SizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo + SizeofIPv6MTUInfo = C.sizeof_struct_ip6_mtuinfo + SizeofICMPv6Filter = C.sizeof_struct_icmp6_filter +) + +// Ptrace requests + +const ( + PTRACE_TRACEME = C.PT_TRACE_ME + PTRACE_CONT = C.PT_CONTINUE + PTRACE_KILL = C.PT_KILL +) + +// Events (kqueue, kevent) + +type Kevent_t C.struct_kevent + +// Select + +type FdSet C.fd_set + +// Routing and interface messages + +const ( + SizeofIfMsghdr = C.sizeof_struct_if_msghdr + SizeofIfData = C.sizeof_struct_if_data + SizeofIfaMsghdr = C.sizeof_struct_ifa_msghdr + SizeofIfmaMsghdr = C.sizeof_struct_ifma_msghdr + SizeofIfAnnounceMsghdr = C.sizeof_struct_if_announcemsghdr + SizeofRtMsghdr = C.sizeof_struct_rt_msghdr + SizeofRtMetrics = C.sizeof_struct_rt_metrics +) + +type IfMsghdr C.struct_if_msghdr + +type IfData C.struct_if_data + +type IfaMsghdr C.struct_ifa_msghdr + +type IfmaMsghdr C.struct_ifma_msghdr + +type IfAnnounceMsghdr C.struct_if_announcemsghdr + +type RtMsghdr C.struct_rt_msghdr + +type RtMetrics C.struct_rt_metrics + +// Berkeley packet filter + +const ( + SizeofBpfVersion = C.sizeof_struct_bpf_version + SizeofBpfStat = C.sizeof_struct_bpf_stat + SizeofBpfProgram = C.sizeof_struct_bpf_program + SizeofBpfInsn = C.sizeof_struct_bpf_insn + SizeofBpfHdr = C.sizeof_struct_bpf_hdr +) + +type BpfVersion C.struct_bpf_version + +type BpfStat C.struct_bpf_stat + +type BpfProgram C.struct_bpf_program + +type BpfInsn C.struct_bpf_insn + +type BpfHdr C.struct_bpf_hdr + +// Terminal handling + +type Termios C.struct_termios + +type Winsize C.struct_winsize + +// fchmodat-like syscalls. + +const ( + AT_FDCWD = C.AT_FDCWD + AT_SYMLINK_NOFOLLOW = C.AT_SYMLINK_NOFOLLOW +) + +// poll + +type PollFd C.struct_pollfd + +const ( + POLLERR = C.POLLERR + POLLHUP = C.POLLHUP + POLLIN = C.POLLIN + POLLNVAL = C.POLLNVAL + POLLOUT = C.POLLOUT + POLLPRI = C.POLLPRI + POLLRDBAND = C.POLLRDBAND + POLLRDNORM = C.POLLRDNORM + POLLWRBAND = C.POLLWRBAND + POLLWRNORM = C.POLLWRNORM +) + +// Uname + +type Utsname C.struct_utsname diff --git a/vendor/golang.org/x/sys/unix/types_freebsd.go b/vendor/golang.org/x/sys/unix/types_freebsd.go new file mode 100644 index 0000000000..a121dc3368 --- /dev/null +++ b/vendor/golang.org/x/sys/unix/types_freebsd.go @@ -0,0 +1,400 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +/* +Input to cgo -godefs. See README.md +*/ + +// +godefs map struct_in_addr [4]byte /* in_addr */ +// +godefs map struct_in6_addr [16]byte /* in6_addr */ + +package unix + +/* +#define _WANT_FREEBSD11_STAT 1 +#define _WANT_FREEBSD11_STATFS 1 +#define _WANT_FREEBSD11_DIRENT 1 +#define _WANT_FREEBSD11_KEVENT 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + sizeofPtr = sizeof(void*), +}; + +union sockaddr_all { + struct sockaddr s1; // this one gets used for fields + struct sockaddr_in s2; // these pad it out + struct sockaddr_in6 s3; + struct sockaddr_un s4; + struct sockaddr_dl s5; +}; + +struct sockaddr_any { + struct sockaddr addr; + char pad[sizeof(union sockaddr_all) - sizeof(struct sockaddr)]; +}; + +// This structure is a duplicate of if_data on FreeBSD 8-STABLE. +// See /usr/include/net/if.h. +struct if_data8 { + u_char ifi_type; + u_char ifi_physical; + u_char ifi_addrlen; + u_char ifi_hdrlen; + u_char ifi_link_state; + u_char ifi_spare_char1; + u_char ifi_spare_char2; + u_char ifi_datalen; + u_long ifi_mtu; + u_long ifi_metric; + u_long ifi_baudrate; + u_long ifi_ipackets; + u_long ifi_ierrors; + u_long ifi_opackets; + u_long ifi_oerrors; + u_long ifi_collisions; + u_long ifi_ibytes; + u_long ifi_obytes; + u_long ifi_imcasts; + u_long ifi_omcasts; + u_long ifi_iqdrops; + u_long ifi_noproto; + u_long ifi_hwassist; +// FIXME: these are now unions, so maybe need to change definitions? +#undef ifi_epoch + time_t ifi_epoch; +#undef ifi_lastchange + struct timeval ifi_lastchange; +}; + +// This structure is a duplicate of if_msghdr on FreeBSD 8-STABLE. +// See /usr/include/net/if.h. +struct if_msghdr8 { + u_short ifm_msglen; + u_char ifm_version; + u_char ifm_type; + int ifm_addrs; + int ifm_flags; + u_short ifm_index; + struct if_data8 ifm_data; +}; +*/ +import "C" + +// Machine characteristics + +const ( + SizeofPtr = C.sizeofPtr + SizeofShort = C.sizeof_short + SizeofInt = C.sizeof_int + SizeofLong = C.sizeof_long + SizeofLongLong = C.sizeof_longlong +) + +// Basic types + +type ( + _C_short C.short + _C_int C.int + _C_long C.long + _C_long_long C.longlong +) + +// Time + +type Timespec C.struct_timespec + +type Timeval C.struct_timeval + +// Processes + +type Rusage C.struct_rusage + +type Rlimit C.struct_rlimit + +type _Gid_t C.gid_t + +// Files + +const ( + _statfsVersion = C.STATFS_VERSION + _dirblksiz = C.DIRBLKSIZ +) + +type Stat_t C.struct_stat + +type stat_freebsd11_t C.struct_freebsd11_stat + +type Statfs_t C.struct_statfs + +type statfs_freebsd11_t C.struct_freebsd11_statfs + +type Flock_t C.struct_flock + +type Dirent C.struct_dirent + +type dirent_freebsd11 C.struct_freebsd11_dirent + +type Fsid C.struct_fsid + +// File system limits + +const ( + PathMax = C.PATH_MAX +) + +// Advice to Fadvise + +const ( + FADV_NORMAL = C.POSIX_FADV_NORMAL + FADV_RANDOM = C.POSIX_FADV_RANDOM + FADV_SEQUENTIAL = C.POSIX_FADV_SEQUENTIAL + FADV_WILLNEED = C.POSIX_FADV_WILLNEED + FADV_DONTNEED = C.POSIX_FADV_DONTNEED + FADV_NOREUSE = C.POSIX_FADV_NOREUSE +) + +// Sockets + +type RawSockaddrInet4 C.struct_sockaddr_in + +type RawSockaddrInet6 C.struct_sockaddr_in6 + +type RawSockaddrUnix C.struct_sockaddr_un + +type RawSockaddrDatalink C.struct_sockaddr_dl + +type RawSockaddr C.struct_sockaddr + +type RawSockaddrAny C.struct_sockaddr_any + +type _Socklen C.socklen_t + +type Linger C.struct_linger + +type Iovec C.struct_iovec + +type IPMreq C.struct_ip_mreq + +type IPMreqn C.struct_ip_mreqn + +type IPv6Mreq C.struct_ipv6_mreq + +type Msghdr C.struct_msghdr + +type Cmsghdr C.struct_cmsghdr + +type Inet6Pktinfo C.struct_in6_pktinfo + +type IPv6MTUInfo C.struct_ip6_mtuinfo + +type ICMPv6Filter C.struct_icmp6_filter + +const ( + SizeofSockaddrInet4 = C.sizeof_struct_sockaddr_in + SizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 + SizeofSockaddrAny = C.sizeof_struct_sockaddr_any + SizeofSockaddrUnix = C.sizeof_struct_sockaddr_un + SizeofSockaddrDatalink = C.sizeof_struct_sockaddr_dl + SizeofLinger = C.sizeof_struct_linger + SizeofIPMreq = C.sizeof_struct_ip_mreq + SizeofIPMreqn = C.sizeof_struct_ip_mreqn + SizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq + SizeofMsghdr = C.sizeof_struct_msghdr + SizeofCmsghdr = C.sizeof_struct_cmsghdr + SizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo + SizeofIPv6MTUInfo = C.sizeof_struct_ip6_mtuinfo + SizeofICMPv6Filter = C.sizeof_struct_icmp6_filter +) + +// Ptrace requests + +const ( + PTRACE_ATTACH = C.PT_ATTACH + PTRACE_CONT = C.PT_CONTINUE + PTRACE_DETACH = C.PT_DETACH + PTRACE_GETFPREGS = C.PT_GETFPREGS + PTRACE_GETFSBASE = C.PT_GETFSBASE + PTRACE_GETLWPLIST = C.PT_GETLWPLIST + PTRACE_GETNUMLWPS = C.PT_GETNUMLWPS + PTRACE_GETREGS = C.PT_GETREGS + PTRACE_GETXSTATE = C.PT_GETXSTATE + PTRACE_IO = C.PT_IO + PTRACE_KILL = C.PT_KILL + PTRACE_LWPEVENTS = C.PT_LWP_EVENTS + PTRACE_LWPINFO = C.PT_LWPINFO + PTRACE_SETFPREGS = C.PT_SETFPREGS + PTRACE_SETREGS = C.PT_SETREGS + PTRACE_SINGLESTEP = C.PT_STEP + PTRACE_TRACEME = C.PT_TRACE_ME +) + +const ( + PIOD_READ_D = C.PIOD_READ_D + PIOD_WRITE_D = C.PIOD_WRITE_D + PIOD_READ_I = C.PIOD_READ_I + PIOD_WRITE_I = C.PIOD_WRITE_I +) + +const ( + PL_FLAG_BORN = C.PL_FLAG_BORN + PL_FLAG_EXITED = C.PL_FLAG_EXITED + PL_FLAG_SI = C.PL_FLAG_SI +) + +const ( + TRAP_BRKPT = C.TRAP_BRKPT + TRAP_TRACE = C.TRAP_TRACE +) + +type PtraceLwpInfoStruct C.struct_ptrace_lwpinfo + +type __Siginfo C.struct___siginfo + +type Sigset_t C.sigset_t + +type Reg C.struct_reg + +type FpReg C.struct_fpreg + +type PtraceIoDesc C.struct_ptrace_io_desc + +// Events (kqueue, kevent) + +type Kevent_t C.struct_kevent_freebsd11 + +// Select + +type FdSet C.fd_set + +// Routing and interface messages + +const ( + sizeofIfMsghdr = C.sizeof_struct_if_msghdr + SizeofIfMsghdr = C.sizeof_struct_if_msghdr8 + sizeofIfData = C.sizeof_struct_if_data + SizeofIfData = C.sizeof_struct_if_data8 + SizeofIfaMsghdr = C.sizeof_struct_ifa_msghdr + SizeofIfmaMsghdr = C.sizeof_struct_ifma_msghdr + SizeofIfAnnounceMsghdr = C.sizeof_struct_if_announcemsghdr + SizeofRtMsghdr = C.sizeof_struct_rt_msghdr + SizeofRtMetrics = C.sizeof_struct_rt_metrics +) + +type ifMsghdr C.struct_if_msghdr + +type IfMsghdr C.struct_if_msghdr8 + +type ifData C.struct_if_data + +type IfData C.struct_if_data8 + +type IfaMsghdr C.struct_ifa_msghdr + +type IfmaMsghdr C.struct_ifma_msghdr + +type IfAnnounceMsghdr C.struct_if_announcemsghdr + +type RtMsghdr C.struct_rt_msghdr + +type RtMetrics C.struct_rt_metrics + +// Berkeley packet filter + +const ( + SizeofBpfVersion = C.sizeof_struct_bpf_version + SizeofBpfStat = C.sizeof_struct_bpf_stat + SizeofBpfZbuf = C.sizeof_struct_bpf_zbuf + SizeofBpfProgram = C.sizeof_struct_bpf_program + SizeofBpfInsn = C.sizeof_struct_bpf_insn + SizeofBpfHdr = C.sizeof_struct_bpf_hdr + SizeofBpfZbufHeader = C.sizeof_struct_bpf_zbuf_header +) + +type BpfVersion C.struct_bpf_version + +type BpfStat C.struct_bpf_stat + +type BpfZbuf C.struct_bpf_zbuf + +type BpfProgram C.struct_bpf_program + +type BpfInsn C.struct_bpf_insn + +type BpfHdr C.struct_bpf_hdr + +type BpfZbufHeader C.struct_bpf_zbuf_header + +// Terminal handling + +type Termios C.struct_termios + +type Winsize C.struct_winsize + +// fchmodat-like syscalls. + +const ( + AT_FDCWD = C.AT_FDCWD + AT_REMOVEDIR = C.AT_REMOVEDIR + AT_SYMLINK_FOLLOW = C.AT_SYMLINK_FOLLOW + AT_SYMLINK_NOFOLLOW = C.AT_SYMLINK_NOFOLLOW +) + +// poll + +type PollFd C.struct_pollfd + +const ( + POLLERR = C.POLLERR + POLLHUP = C.POLLHUP + POLLIN = C.POLLIN + POLLINIGNEOF = C.POLLINIGNEOF + POLLNVAL = C.POLLNVAL + POLLOUT = C.POLLOUT + POLLPRI = C.POLLPRI + POLLRDBAND = C.POLLRDBAND + POLLRDNORM = C.POLLRDNORM + POLLWRBAND = C.POLLWRBAND + POLLWRNORM = C.POLLWRNORM +) + +// Capabilities + +type CapRights C.struct_cap_rights + +// Uname + +type Utsname C.struct_utsname diff --git a/vendor/golang.org/x/sys/unix/types_netbsd.go b/vendor/golang.org/x/sys/unix/types_netbsd.go new file mode 100644 index 0000000000..4a96d72c37 --- /dev/null +++ b/vendor/golang.org/x/sys/unix/types_netbsd.go @@ -0,0 +1,290 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +/* +Input to cgo -godefs. See README.md +*/ + +// +godefs map struct_in_addr [4]byte /* in_addr */ +// +godefs map struct_in6_addr [16]byte /* in6_addr */ + +package unix + +/* +#define KERNEL +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + sizeofPtr = sizeof(void*), +}; + +union sockaddr_all { + struct sockaddr s1; // this one gets used for fields + struct sockaddr_in s2; // these pad it out + struct sockaddr_in6 s3; + struct sockaddr_un s4; + struct sockaddr_dl s5; +}; + +struct sockaddr_any { + struct sockaddr addr; + char pad[sizeof(union sockaddr_all) - sizeof(struct sockaddr)]; +}; + +*/ +import "C" + +// Machine characteristics + +const ( + SizeofPtr = C.sizeofPtr + SizeofShort = C.sizeof_short + SizeofInt = C.sizeof_int + SizeofLong = C.sizeof_long + SizeofLongLong = C.sizeof_longlong +) + +// Basic types + +type ( + _C_short C.short + _C_int C.int + _C_long C.long + _C_long_long C.longlong +) + +// Time + +type Timespec C.struct_timespec + +type Timeval C.struct_timeval + +// Processes + +type Rusage C.struct_rusage + +type Rlimit C.struct_rlimit + +type _Gid_t C.gid_t + +// Files + +type Stat_t C.struct_stat + +type Statfs_t C.struct_statfs + +type Flock_t C.struct_flock + +type Dirent C.struct_dirent + +type Fsid C.fsid_t + +// File system limits + +const ( + PathMax = C.PATH_MAX +) + +// Advice to Fadvise + +const ( + FADV_NORMAL = C.POSIX_FADV_NORMAL + FADV_RANDOM = C.POSIX_FADV_RANDOM + FADV_SEQUENTIAL = C.POSIX_FADV_SEQUENTIAL + FADV_WILLNEED = C.POSIX_FADV_WILLNEED + FADV_DONTNEED = C.POSIX_FADV_DONTNEED + FADV_NOREUSE = C.POSIX_FADV_NOREUSE +) + +// Sockets + +type RawSockaddrInet4 C.struct_sockaddr_in + +type RawSockaddrInet6 C.struct_sockaddr_in6 + +type RawSockaddrUnix C.struct_sockaddr_un + +type RawSockaddrDatalink C.struct_sockaddr_dl + +type RawSockaddr C.struct_sockaddr + +type RawSockaddrAny C.struct_sockaddr_any + +type _Socklen C.socklen_t + +type Linger C.struct_linger + +type Iovec C.struct_iovec + +type IPMreq C.struct_ip_mreq + +type IPv6Mreq C.struct_ipv6_mreq + +type Msghdr C.struct_msghdr + +type Cmsghdr C.struct_cmsghdr + +type Inet6Pktinfo C.struct_in6_pktinfo + +type IPv6MTUInfo C.struct_ip6_mtuinfo + +type ICMPv6Filter C.struct_icmp6_filter + +const ( + SizeofSockaddrInet4 = C.sizeof_struct_sockaddr_in + SizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 + SizeofSockaddrAny = C.sizeof_struct_sockaddr_any + SizeofSockaddrUnix = C.sizeof_struct_sockaddr_un + SizeofSockaddrDatalink = C.sizeof_struct_sockaddr_dl + SizeofLinger = C.sizeof_struct_linger + SizeofIPMreq = C.sizeof_struct_ip_mreq + SizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq + SizeofMsghdr = C.sizeof_struct_msghdr + SizeofCmsghdr = C.sizeof_struct_cmsghdr + SizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo + SizeofIPv6MTUInfo = C.sizeof_struct_ip6_mtuinfo + SizeofICMPv6Filter = C.sizeof_struct_icmp6_filter +) + +// Ptrace requests + +const ( + PTRACE_TRACEME = C.PT_TRACE_ME + PTRACE_CONT = C.PT_CONTINUE + PTRACE_KILL = C.PT_KILL +) + +// Events (kqueue, kevent) + +type Kevent_t C.struct_kevent + +// Select + +type FdSet C.fd_set + +// Routing and interface messages + +const ( + SizeofIfMsghdr = C.sizeof_struct_if_msghdr + SizeofIfData = C.sizeof_struct_if_data + SizeofIfaMsghdr = C.sizeof_struct_ifa_msghdr + SizeofIfAnnounceMsghdr = C.sizeof_struct_if_announcemsghdr + SizeofRtMsghdr = C.sizeof_struct_rt_msghdr + SizeofRtMetrics = C.sizeof_struct_rt_metrics +) + +type IfMsghdr C.struct_if_msghdr + +type IfData C.struct_if_data + +type IfaMsghdr C.struct_ifa_msghdr + +type IfAnnounceMsghdr C.struct_if_announcemsghdr + +type RtMsghdr C.struct_rt_msghdr + +type RtMetrics C.struct_rt_metrics + +type Mclpool C.struct_mclpool + +// Berkeley packet filter + +const ( + SizeofBpfVersion = C.sizeof_struct_bpf_version + SizeofBpfStat = C.sizeof_struct_bpf_stat + SizeofBpfProgram = C.sizeof_struct_bpf_program + SizeofBpfInsn = C.sizeof_struct_bpf_insn + SizeofBpfHdr = C.sizeof_struct_bpf_hdr +) + +type BpfVersion C.struct_bpf_version + +type BpfStat C.struct_bpf_stat + +type BpfProgram C.struct_bpf_program + +type BpfInsn C.struct_bpf_insn + +type BpfHdr C.struct_bpf_hdr + +type BpfTimeval C.struct_bpf_timeval + +// Terminal handling + +type Termios C.struct_termios + +type Winsize C.struct_winsize + +type Ptmget C.struct_ptmget + +// fchmodat-like syscalls. + +const ( + AT_FDCWD = C.AT_FDCWD + AT_SYMLINK_FOLLOW = C.AT_SYMLINK_FOLLOW + AT_SYMLINK_NOFOLLOW = C.AT_SYMLINK_NOFOLLOW +) + +// poll + +type PollFd C.struct_pollfd + +const ( + POLLERR = C.POLLERR + POLLHUP = C.POLLHUP + POLLIN = C.POLLIN + POLLNVAL = C.POLLNVAL + POLLOUT = C.POLLOUT + POLLPRI = C.POLLPRI + POLLRDBAND = C.POLLRDBAND + POLLRDNORM = C.POLLRDNORM + POLLWRBAND = C.POLLWRBAND + POLLWRNORM = C.POLLWRNORM +) + +// Sysctl + +type Sysctlnode C.struct_sysctlnode + +// Uname + +type Utsname C.struct_utsname + +// Clockinfo + +const SizeofClockinfo = C.sizeof_struct_clockinfo + +type Clockinfo C.struct_clockinfo diff --git a/vendor/golang.org/x/sys/unix/types_openbsd.go b/vendor/golang.org/x/sys/unix/types_openbsd.go new file mode 100644 index 0000000000..775cb57dc8 --- /dev/null +++ b/vendor/golang.org/x/sys/unix/types_openbsd.go @@ -0,0 +1,283 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +/* +Input to cgo -godefs. See README.md +*/ + +// +godefs map struct_in_addr [4]byte /* in_addr */ +// +godefs map struct_in6_addr [16]byte /* in6_addr */ + +package unix + +/* +#define KERNEL +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + sizeofPtr = sizeof(void*), +}; + +union sockaddr_all { + struct sockaddr s1; // this one gets used for fields + struct sockaddr_in s2; // these pad it out + struct sockaddr_in6 s3; + struct sockaddr_un s4; + struct sockaddr_dl s5; +}; + +struct sockaddr_any { + struct sockaddr addr; + char pad[sizeof(union sockaddr_all) - sizeof(struct sockaddr)]; +}; + +*/ +import "C" + +// Machine characteristics + +const ( + SizeofPtr = C.sizeofPtr + SizeofShort = C.sizeof_short + SizeofInt = C.sizeof_int + SizeofLong = C.sizeof_long + SizeofLongLong = C.sizeof_longlong +) + +// Basic types + +type ( + _C_short C.short + _C_int C.int + _C_long C.long + _C_long_long C.longlong +) + +// Time + +type Timespec C.struct_timespec + +type Timeval C.struct_timeval + +// Processes + +type Rusage C.struct_rusage + +type Rlimit C.struct_rlimit + +type _Gid_t C.gid_t + +// Files + +type Stat_t C.struct_stat + +type Statfs_t C.struct_statfs + +type Flock_t C.struct_flock + +type Dirent C.struct_dirent + +type Fsid C.fsid_t + +// File system limits + +const ( + PathMax = C.PATH_MAX +) + +// Sockets + +type RawSockaddrInet4 C.struct_sockaddr_in + +type RawSockaddrInet6 C.struct_sockaddr_in6 + +type RawSockaddrUnix C.struct_sockaddr_un + +type RawSockaddrDatalink C.struct_sockaddr_dl + +type RawSockaddr C.struct_sockaddr + +type RawSockaddrAny C.struct_sockaddr_any + +type _Socklen C.socklen_t + +type Linger C.struct_linger + +type Iovec C.struct_iovec + +type IPMreq C.struct_ip_mreq + +type IPv6Mreq C.struct_ipv6_mreq + +type Msghdr C.struct_msghdr + +type Cmsghdr C.struct_cmsghdr + +type Inet6Pktinfo C.struct_in6_pktinfo + +type IPv6MTUInfo C.struct_ip6_mtuinfo + +type ICMPv6Filter C.struct_icmp6_filter + +const ( + SizeofSockaddrInet4 = C.sizeof_struct_sockaddr_in + SizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 + SizeofSockaddrAny = C.sizeof_struct_sockaddr_any + SizeofSockaddrUnix = C.sizeof_struct_sockaddr_un + SizeofSockaddrDatalink = C.sizeof_struct_sockaddr_dl + SizeofLinger = C.sizeof_struct_linger + SizeofIPMreq = C.sizeof_struct_ip_mreq + SizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq + SizeofMsghdr = C.sizeof_struct_msghdr + SizeofCmsghdr = C.sizeof_struct_cmsghdr + SizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo + SizeofIPv6MTUInfo = C.sizeof_struct_ip6_mtuinfo + SizeofICMPv6Filter = C.sizeof_struct_icmp6_filter +) + +// Ptrace requests + +const ( + PTRACE_TRACEME = C.PT_TRACE_ME + PTRACE_CONT = C.PT_CONTINUE + PTRACE_KILL = C.PT_KILL +) + +// Events (kqueue, kevent) + +type Kevent_t C.struct_kevent + +// Select + +type FdSet C.fd_set + +// Routing and interface messages + +const ( + SizeofIfMsghdr = C.sizeof_struct_if_msghdr + SizeofIfData = C.sizeof_struct_if_data + SizeofIfaMsghdr = C.sizeof_struct_ifa_msghdr + SizeofIfAnnounceMsghdr = C.sizeof_struct_if_announcemsghdr + SizeofRtMsghdr = C.sizeof_struct_rt_msghdr + SizeofRtMetrics = C.sizeof_struct_rt_metrics +) + +type IfMsghdr C.struct_if_msghdr + +type IfData C.struct_if_data + +type IfaMsghdr C.struct_ifa_msghdr + +type IfAnnounceMsghdr C.struct_if_announcemsghdr + +type RtMsghdr C.struct_rt_msghdr + +type RtMetrics C.struct_rt_metrics + +type Mclpool C.struct_mclpool + +// Berkeley packet filter + +const ( + SizeofBpfVersion = C.sizeof_struct_bpf_version + SizeofBpfStat = C.sizeof_struct_bpf_stat + SizeofBpfProgram = C.sizeof_struct_bpf_program + SizeofBpfInsn = C.sizeof_struct_bpf_insn + SizeofBpfHdr = C.sizeof_struct_bpf_hdr +) + +type BpfVersion C.struct_bpf_version + +type BpfStat C.struct_bpf_stat + +type BpfProgram C.struct_bpf_program + +type BpfInsn C.struct_bpf_insn + +type BpfHdr C.struct_bpf_hdr + +type BpfTimeval C.struct_bpf_timeval + +// Terminal handling + +type Termios C.struct_termios + +type Winsize C.struct_winsize + +// fchmodat-like syscalls. + +const ( + AT_FDCWD = C.AT_FDCWD + AT_SYMLINK_FOLLOW = C.AT_SYMLINK_FOLLOW + AT_SYMLINK_NOFOLLOW = C.AT_SYMLINK_NOFOLLOW +) + +// poll + +type PollFd C.struct_pollfd + +const ( + POLLERR = C.POLLERR + POLLHUP = C.POLLHUP + POLLIN = C.POLLIN + POLLNVAL = C.POLLNVAL + POLLOUT = C.POLLOUT + POLLPRI = C.POLLPRI + POLLRDBAND = C.POLLRDBAND + POLLRDNORM = C.POLLRDNORM + POLLWRBAND = C.POLLWRBAND + POLLWRNORM = C.POLLWRNORM +) + +// Signal Sets + +type Sigset_t C.sigset_t + +// Uname + +type Utsname C.struct_utsname + +// Uvmexp + +const SizeofUvmexp = C.sizeof_struct_uvmexp + +type Uvmexp C.struct_uvmexp + +// Clockinfo + +const SizeofClockinfo = C.sizeof_struct_clockinfo + +type Clockinfo C.struct_clockinfo diff --git a/vendor/golang.org/x/sys/unix/types_solaris.go b/vendor/golang.org/x/sys/unix/types_solaris.go new file mode 100644 index 0000000000..2b716f9348 --- /dev/null +++ b/vendor/golang.org/x/sys/unix/types_solaris.go @@ -0,0 +1,266 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +/* +Input to cgo -godefs. See README.md +*/ + +// +godefs map struct_in_addr [4]byte /* in_addr */ +// +godefs map struct_in6_addr [16]byte /* in6_addr */ + +package unix + +/* +#define KERNEL +// These defines ensure that builds done on newer versions of Solaris are +// backwards-compatible with older versions of Solaris and +// OpenSolaris-based derivatives. +#define __USE_SUNOS_SOCKETS__ // msghdr +#define __USE_LEGACY_PROTOTYPES__ // iovec +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + sizeofPtr = sizeof(void*), +}; + +union sockaddr_all { + struct sockaddr s1; // this one gets used for fields + struct sockaddr_in s2; // these pad it out + struct sockaddr_in6 s3; + struct sockaddr_un s4; + struct sockaddr_dl s5; +}; + +struct sockaddr_any { + struct sockaddr addr; + char pad[sizeof(union sockaddr_all) - sizeof(struct sockaddr)]; +}; + +*/ +import "C" + +// Machine characteristics + +const ( + SizeofPtr = C.sizeofPtr + SizeofShort = C.sizeof_short + SizeofInt = C.sizeof_int + SizeofLong = C.sizeof_long + SizeofLongLong = C.sizeof_longlong + PathMax = C.PATH_MAX + MaxHostNameLen = C.MAXHOSTNAMELEN +) + +// Basic types + +type ( + _C_short C.short + _C_int C.int + _C_long C.long + _C_long_long C.longlong +) + +// Time + +type Timespec C.struct_timespec + +type Timeval C.struct_timeval + +type Timeval32 C.struct_timeval32 + +type Tms C.struct_tms + +type Utimbuf C.struct_utimbuf + +// Processes + +type Rusage C.struct_rusage + +type Rlimit C.struct_rlimit + +type _Gid_t C.gid_t + +// Files + +type Stat_t C.struct_stat + +type Flock_t C.struct_flock + +type Dirent C.struct_dirent + +// Filesystems + +type _Fsblkcnt_t C.fsblkcnt_t + +type Statvfs_t C.struct_statvfs + +// Sockets + +type RawSockaddrInet4 C.struct_sockaddr_in + +type RawSockaddrInet6 C.struct_sockaddr_in6 + +type RawSockaddrUnix C.struct_sockaddr_un + +type RawSockaddrDatalink C.struct_sockaddr_dl + +type RawSockaddr C.struct_sockaddr + +type RawSockaddrAny C.struct_sockaddr_any + +type _Socklen C.socklen_t + +type Linger C.struct_linger + +type Iovec C.struct_iovec + +type IPMreq C.struct_ip_mreq + +type IPv6Mreq C.struct_ipv6_mreq + +type Msghdr C.struct_msghdr + +type Cmsghdr C.struct_cmsghdr + +type Inet6Pktinfo C.struct_in6_pktinfo + +type IPv6MTUInfo C.struct_ip6_mtuinfo + +type ICMPv6Filter C.struct_icmp6_filter + +const ( + SizeofSockaddrInet4 = C.sizeof_struct_sockaddr_in + SizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 + SizeofSockaddrAny = C.sizeof_struct_sockaddr_any + SizeofSockaddrUnix = C.sizeof_struct_sockaddr_un + SizeofSockaddrDatalink = C.sizeof_struct_sockaddr_dl + SizeofLinger = C.sizeof_struct_linger + SizeofIPMreq = C.sizeof_struct_ip_mreq + SizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq + SizeofMsghdr = C.sizeof_struct_msghdr + SizeofCmsghdr = C.sizeof_struct_cmsghdr + SizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo + SizeofIPv6MTUInfo = C.sizeof_struct_ip6_mtuinfo + SizeofICMPv6Filter = C.sizeof_struct_icmp6_filter +) + +// Select + +type FdSet C.fd_set + +// Misc + +type Utsname C.struct_utsname + +type Ustat_t C.struct_ustat + +const ( + AT_FDCWD = C.AT_FDCWD + AT_SYMLINK_NOFOLLOW = C.AT_SYMLINK_NOFOLLOW + AT_SYMLINK_FOLLOW = C.AT_SYMLINK_FOLLOW + AT_REMOVEDIR = C.AT_REMOVEDIR + AT_EACCESS = C.AT_EACCESS +) + +// Routing and interface messages + +const ( + SizeofIfMsghdr = C.sizeof_struct_if_msghdr + SizeofIfData = C.sizeof_struct_if_data + SizeofIfaMsghdr = C.sizeof_struct_ifa_msghdr + SizeofRtMsghdr = C.sizeof_struct_rt_msghdr + SizeofRtMetrics = C.sizeof_struct_rt_metrics +) + +type IfMsghdr C.struct_if_msghdr + +type IfData C.struct_if_data + +type IfaMsghdr C.struct_ifa_msghdr + +type RtMsghdr C.struct_rt_msghdr + +type RtMetrics C.struct_rt_metrics + +// Berkeley packet filter + +const ( + SizeofBpfVersion = C.sizeof_struct_bpf_version + SizeofBpfStat = C.sizeof_struct_bpf_stat + SizeofBpfProgram = C.sizeof_struct_bpf_program + SizeofBpfInsn = C.sizeof_struct_bpf_insn + SizeofBpfHdr = C.sizeof_struct_bpf_hdr +) + +type BpfVersion C.struct_bpf_version + +type BpfStat C.struct_bpf_stat + +type BpfProgram C.struct_bpf_program + +type BpfInsn C.struct_bpf_insn + +type BpfTimeval C.struct_bpf_timeval + +type BpfHdr C.struct_bpf_hdr + +// Terminal handling + +type Termios C.struct_termios + +type Termio C.struct_termio + +type Winsize C.struct_winsize + +// poll + +type PollFd C.struct_pollfd + +const ( + POLLERR = C.POLLERR + POLLHUP = C.POLLHUP + POLLIN = C.POLLIN + POLLNVAL = C.POLLNVAL + POLLOUT = C.POLLOUT + POLLPRI = C.POLLPRI + POLLRDBAND = C.POLLRDBAND + POLLRDNORM = C.POLLRDNORM + POLLWRBAND = C.POLLWRBAND + POLLWRNORM = C.POLLWRNORM +) diff --git a/vendor/golang.org/x/text/unicode/bidi/gen.go b/vendor/golang.org/x/text/unicode/bidi/gen.go new file mode 100644 index 0000000000..987fc169cc --- /dev/null +++ b/vendor/golang.org/x/text/unicode/bidi/gen.go @@ -0,0 +1,133 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +package main + +import ( + "flag" + "log" + + "golang.org/x/text/internal/gen" + "golang.org/x/text/internal/triegen" + "golang.org/x/text/internal/ucd" +) + +var outputFile = flag.String("out", "tables.go", "output file") + +func main() { + gen.Init() + gen.Repackage("gen_trieval.go", "trieval.go", "bidi") + gen.Repackage("gen_ranges.go", "ranges_test.go", "bidi") + + genTables() +} + +// bidiClass names and codes taken from class "bc" in +// https://www.unicode.org/Public/8.0.0/ucd/PropertyValueAliases.txt +var bidiClass = map[string]Class{ + "AL": AL, // ArabicLetter + "AN": AN, // ArabicNumber + "B": B, // ParagraphSeparator + "BN": BN, // BoundaryNeutral + "CS": CS, // CommonSeparator + "EN": EN, // EuropeanNumber + "ES": ES, // EuropeanSeparator + "ET": ET, // EuropeanTerminator + "L": L, // LeftToRight + "NSM": NSM, // NonspacingMark + "ON": ON, // OtherNeutral + "R": R, // RightToLeft + "S": S, // SegmentSeparator + "WS": WS, // WhiteSpace + + "FSI": Control, + "PDF": Control, + "PDI": Control, + "LRE": Control, + "LRI": Control, + "LRO": Control, + "RLE": Control, + "RLI": Control, + "RLO": Control, +} + +func genTables() { + if numClass > 0x0F { + log.Fatalf("Too many Class constants (%#x > 0x0F).", numClass) + } + w := gen.NewCodeWriter() + defer w.WriteVersionedGoFile(*outputFile, "bidi") + + gen.WriteUnicodeVersion(w) + + t := triegen.NewTrie("bidi") + + // Build data about bracket mapping. These bits need to be or-ed with + // any other bits. + orMask := map[rune]uint64{} + + xorMap := map[rune]int{} + xorMasks := []rune{0} // First value is no-op. + + ucd.Parse(gen.OpenUCDFile("BidiBrackets.txt"), func(p *ucd.Parser) { + r1 := p.Rune(0) + r2 := p.Rune(1) + xor := r1 ^ r2 + if _, ok := xorMap[xor]; !ok { + xorMap[xor] = len(xorMasks) + xorMasks = append(xorMasks, xor) + } + entry := uint64(xorMap[xor]) << xorMaskShift + switch p.String(2) { + case "o": + entry |= openMask + case "c", "n": + default: + log.Fatalf("Unknown bracket class %q.", p.String(2)) + } + orMask[r1] = entry + }) + + w.WriteComment(` + xorMasks contains masks to be xor-ed with brackets to get the reverse + version.`) + w.WriteVar("xorMasks", xorMasks) + + done := map[rune]bool{} + + insert := func(r rune, c Class) { + if !done[r] { + t.Insert(r, orMask[r]|uint64(c)) + done[r] = true + } + } + + // Insert the derived BiDi properties. + ucd.Parse(gen.OpenUCDFile("extracted/DerivedBidiClass.txt"), func(p *ucd.Parser) { + r := p.Rune(0) + class, ok := bidiClass[p.String(1)] + if !ok { + log.Fatalf("%U: Unknown BiDi class %q", r, p.String(1)) + } + insert(r, class) + }) + visitDefaults(insert) + + // TODO: use sparse blocks. This would reduce table size considerably + // from the looks of it. + + sz, err := t.Gen(w) + if err != nil { + log.Fatal(err) + } + w.Size += sz +} + +// dummy values to make methods in gen_common compile. The real versions +// will be generated by this file to tables.go. +var ( + xorMasks []rune +) diff --git a/vendor/golang.org/x/text/unicode/bidi/gen_ranges.go b/vendor/golang.org/x/text/unicode/bidi/gen_ranges.go new file mode 100644 index 0000000000..02c3b505d6 --- /dev/null +++ b/vendor/golang.org/x/text/unicode/bidi/gen_ranges.go @@ -0,0 +1,57 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +package main + +import ( + "unicode" + + "golang.org/x/text/internal/gen" + "golang.org/x/text/internal/ucd" + "golang.org/x/text/unicode/rangetable" +) + +// These tables are hand-extracted from: +// https://www.unicode.org/Public/8.0.0/ucd/extracted/DerivedBidiClass.txt +func visitDefaults(fn func(r rune, c Class)) { + // first write default values for ranges listed above. + visitRunes(fn, AL, []rune{ + 0x0600, 0x07BF, // Arabic + 0x08A0, 0x08FF, // Arabic Extended-A + 0xFB50, 0xFDCF, // Arabic Presentation Forms + 0xFDF0, 0xFDFF, + 0xFE70, 0xFEFF, + 0x0001EE00, 0x0001EEFF, // Arabic Mathematical Alpha Symbols + }) + visitRunes(fn, R, []rune{ + 0x0590, 0x05FF, // Hebrew + 0x07C0, 0x089F, // Nko et al. + 0xFB1D, 0xFB4F, + 0x00010800, 0x00010FFF, // Cypriot Syllabary et. al. + 0x0001E800, 0x0001EDFF, + 0x0001EF00, 0x0001EFFF, + }) + visitRunes(fn, ET, []rune{ // European Terminator + 0x20A0, 0x20Cf, // Currency symbols + }) + rangetable.Visit(unicode.Noncharacter_Code_Point, func(r rune) { + fn(r, BN) // Boundary Neutral + }) + ucd.Parse(gen.OpenUCDFile("DerivedCoreProperties.txt"), func(p *ucd.Parser) { + if p.String(1) == "Default_Ignorable_Code_Point" { + fn(p.Rune(0), BN) // Boundary Neutral + } + }) +} + +func visitRunes(fn func(r rune, c Class), c Class, runes []rune) { + for i := 0; i < len(runes); i += 2 { + lo, hi := runes[i], runes[i+1] + for j := lo; j <= hi; j++ { + fn(j, c) + } + } +} diff --git a/vendor/golang.org/x/text/unicode/bidi/gen_trieval.go b/vendor/golang.org/x/text/unicode/bidi/gen_trieval.go new file mode 100644 index 0000000000..9cb9942894 --- /dev/null +++ b/vendor/golang.org/x/text/unicode/bidi/gen_trieval.go @@ -0,0 +1,64 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +package main + +// Class is the Unicode BiDi class. Each rune has a single class. +type Class uint + +const ( + L Class = iota // LeftToRight + R // RightToLeft + EN // EuropeanNumber + ES // EuropeanSeparator + ET // EuropeanTerminator + AN // ArabicNumber + CS // CommonSeparator + B // ParagraphSeparator + S // SegmentSeparator + WS // WhiteSpace + ON // OtherNeutral + BN // BoundaryNeutral + NSM // NonspacingMark + AL // ArabicLetter + Control // Control LRO - PDI + + numClass + + LRO // LeftToRightOverride + RLO // RightToLeftOverride + LRE // LeftToRightEmbedding + RLE // RightToLeftEmbedding + PDF // PopDirectionalFormat + LRI // LeftToRightIsolate + RLI // RightToLeftIsolate + FSI // FirstStrongIsolate + PDI // PopDirectionalIsolate + + unknownClass = ^Class(0) +) + +var controlToClass = map[rune]Class{ + 0x202D: LRO, // LeftToRightOverride, + 0x202E: RLO, // RightToLeftOverride, + 0x202A: LRE, // LeftToRightEmbedding, + 0x202B: RLE, // RightToLeftEmbedding, + 0x202C: PDF, // PopDirectionalFormat, + 0x2066: LRI, // LeftToRightIsolate, + 0x2067: RLI, // RightToLeftIsolate, + 0x2068: FSI, // FirstStrongIsolate, + 0x2069: PDI, // PopDirectionalIsolate, +} + +// A trie entry has the following bits: +// 7..5 XOR mask for brackets +// 4 1: Bracket open, 0: Bracket close +// 3..0 Class type + +const ( + openMask = 0x10 + xorMaskShift = 5 +) diff --git a/vendor/golang.org/x/text/unicode/norm/maketables.go b/vendor/golang.org/x/text/unicode/norm/maketables.go new file mode 100644 index 0000000000..30a3aa9334 --- /dev/null +++ b/vendor/golang.org/x/text/unicode/norm/maketables.go @@ -0,0 +1,986 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// Normalization table generator. +// Data read from the web. +// See forminfo.go for a description of the trie values associated with each rune. + +package main + +import ( + "bytes" + "encoding/binary" + "flag" + "fmt" + "io" + "log" + "sort" + "strconv" + "strings" + + "golang.org/x/text/internal/gen" + "golang.org/x/text/internal/triegen" + "golang.org/x/text/internal/ucd" +) + +func main() { + gen.Init() + loadUnicodeData() + compactCCC() + loadCompositionExclusions() + completeCharFields(FCanonical) + completeCharFields(FCompatibility) + computeNonStarterCounts() + verifyComputed() + printChars() + testDerived() + printTestdata() + makeTables() +} + +var ( + tablelist = flag.String("tables", + "all", + "comma-separated list of which tables to generate; "+ + "can be 'decomp', 'recomp', 'info' and 'all'") + test = flag.Bool("test", + false, + "test existing tables against DerivedNormalizationProps and generate test data for regression testing") + verbose = flag.Bool("verbose", + false, + "write data to stdout as it is parsed") +) + +const MaxChar = 0x10FFFF // anything above this shouldn't exist + +// Quick Check properties of runes allow us to quickly +// determine whether a rune may occur in a normal form. +// For a given normal form, a rune may be guaranteed to occur +// verbatim (QC=Yes), may or may not combine with another +// rune (QC=Maybe), or may not occur (QC=No). +type QCResult int + +const ( + QCUnknown QCResult = iota + QCYes + QCNo + QCMaybe +) + +func (r QCResult) String() string { + switch r { + case QCYes: + return "Yes" + case QCNo: + return "No" + case QCMaybe: + return "Maybe" + } + return "***UNKNOWN***" +} + +const ( + FCanonical = iota // NFC or NFD + FCompatibility // NFKC or NFKD + FNumberOfFormTypes +) + +const ( + MComposed = iota // NFC or NFKC + MDecomposed // NFD or NFKD + MNumberOfModes +) + +// This contains only the properties we're interested in. +type Char struct { + name string + codePoint rune // if zero, this index is not a valid code point. + ccc uint8 // canonical combining class + origCCC uint8 + excludeInComp bool // from CompositionExclusions.txt + compatDecomp bool // it has a compatibility expansion + + nTrailingNonStarters uint8 + nLeadingNonStarters uint8 // must be equal to trailing if non-zero + + forms [FNumberOfFormTypes]FormInfo // For FCanonical and FCompatibility + + state State +} + +var chars = make([]Char, MaxChar+1) +var cccMap = make(map[uint8]uint8) + +func (c Char) String() string { + buf := new(bytes.Buffer) + + fmt.Fprintf(buf, "%U [%s]:\n", c.codePoint, c.name) + fmt.Fprintf(buf, " ccc: %v\n", c.ccc) + fmt.Fprintf(buf, " excludeInComp: %v\n", c.excludeInComp) + fmt.Fprintf(buf, " compatDecomp: %v\n", c.compatDecomp) + fmt.Fprintf(buf, " state: %v\n", c.state) + fmt.Fprintf(buf, " NFC:\n") + fmt.Fprint(buf, c.forms[FCanonical]) + fmt.Fprintf(buf, " NFKC:\n") + fmt.Fprint(buf, c.forms[FCompatibility]) + + return buf.String() +} + +// In UnicodeData.txt, some ranges are marked like this: +// 3400;;Lo;0;L;;;;;N;;;;; +// 4DB5;;Lo;0;L;;;;;N;;;;; +// parseCharacter keeps a state variable indicating the weirdness. +type State int + +const ( + SNormal State = iota // known to be zero for the type + SFirst + SLast + SMissing +) + +var lastChar = rune('\u0000') + +func (c Char) isValid() bool { + return c.codePoint != 0 && c.state != SMissing +} + +type FormInfo struct { + quickCheck [MNumberOfModes]QCResult // index: MComposed or MDecomposed + verified [MNumberOfModes]bool // index: MComposed or MDecomposed + + combinesForward bool // May combine with rune on the right + combinesBackward bool // May combine with rune on the left + isOneWay bool // Never appears in result + inDecomp bool // Some decompositions result in this char. + decomp Decomposition + expandedDecomp Decomposition +} + +func (f FormInfo) String() string { + buf := bytes.NewBuffer(make([]byte, 0)) + + fmt.Fprintf(buf, " quickCheck[C]: %v\n", f.quickCheck[MComposed]) + fmt.Fprintf(buf, " quickCheck[D]: %v\n", f.quickCheck[MDecomposed]) + fmt.Fprintf(buf, " cmbForward: %v\n", f.combinesForward) + fmt.Fprintf(buf, " cmbBackward: %v\n", f.combinesBackward) + fmt.Fprintf(buf, " isOneWay: %v\n", f.isOneWay) + fmt.Fprintf(buf, " inDecomp: %v\n", f.inDecomp) + fmt.Fprintf(buf, " decomposition: %X\n", f.decomp) + fmt.Fprintf(buf, " expandedDecomp: %X\n", f.expandedDecomp) + + return buf.String() +} + +type Decomposition []rune + +func parseDecomposition(s string, skipfirst bool) (a []rune, err error) { + decomp := strings.Split(s, " ") + if len(decomp) > 0 && skipfirst { + decomp = decomp[1:] + } + for _, d := range decomp { + point, err := strconv.ParseUint(d, 16, 64) + if err != nil { + return a, err + } + a = append(a, rune(point)) + } + return a, nil +} + +func loadUnicodeData() { + f := gen.OpenUCDFile("UnicodeData.txt") + defer f.Close() + p := ucd.New(f) + for p.Next() { + r := p.Rune(ucd.CodePoint) + char := &chars[r] + + char.ccc = uint8(p.Uint(ucd.CanonicalCombiningClass)) + decmap := p.String(ucd.DecompMapping) + + exp, err := parseDecomposition(decmap, false) + isCompat := false + if err != nil { + if len(decmap) > 0 { + exp, err = parseDecomposition(decmap, true) + if err != nil { + log.Fatalf(`%U: bad decomp |%v|: "%s"`, r, decmap, err) + } + isCompat = true + } + } + + char.name = p.String(ucd.Name) + char.codePoint = r + char.forms[FCompatibility].decomp = exp + if !isCompat { + char.forms[FCanonical].decomp = exp + } else { + char.compatDecomp = true + } + if len(decmap) > 0 { + char.forms[FCompatibility].decomp = exp + } + } + if err := p.Err(); err != nil { + log.Fatal(err) + } +} + +// compactCCC converts the sparse set of CCC values to a continguous one, +// reducing the number of bits needed from 8 to 6. +func compactCCC() { + m := make(map[uint8]uint8) + for i := range chars { + c := &chars[i] + m[c.ccc] = 0 + } + cccs := []int{} + for v, _ := range m { + cccs = append(cccs, int(v)) + } + sort.Ints(cccs) + for i, c := range cccs { + cccMap[uint8(i)] = uint8(c) + m[uint8(c)] = uint8(i) + } + for i := range chars { + c := &chars[i] + c.origCCC = c.ccc + c.ccc = m[c.ccc] + } + if len(m) >= 1<<6 { + log.Fatalf("too many difference CCC values: %d >= 64", len(m)) + } +} + +// CompositionExclusions.txt has form: +// 0958 # ... +// See https://unicode.org/reports/tr44/ for full explanation +func loadCompositionExclusions() { + f := gen.OpenUCDFile("CompositionExclusions.txt") + defer f.Close() + p := ucd.New(f) + for p.Next() { + c := &chars[p.Rune(0)] + if c.excludeInComp { + log.Fatalf("%U: Duplicate entry in exclusions.", c.codePoint) + } + c.excludeInComp = true + } + if e := p.Err(); e != nil { + log.Fatal(e) + } +} + +// hasCompatDecomp returns true if any of the recursive +// decompositions contains a compatibility expansion. +// In this case, the character may not occur in NFK*. +func hasCompatDecomp(r rune) bool { + c := &chars[r] + if c.compatDecomp { + return true + } + for _, d := range c.forms[FCompatibility].decomp { + if hasCompatDecomp(d) { + return true + } + } + return false +} + +// Hangul related constants. +const ( + HangulBase = 0xAC00 + HangulEnd = 0xD7A4 // hangulBase + Jamo combinations (19 * 21 * 28) + + JamoLBase = 0x1100 + JamoLEnd = 0x1113 + JamoVBase = 0x1161 + JamoVEnd = 0x1176 + JamoTBase = 0x11A8 + JamoTEnd = 0x11C3 + + JamoLVTCount = 19 * 21 * 28 + JamoTCount = 28 +) + +func isHangul(r rune) bool { + return HangulBase <= r && r < HangulEnd +} + +func isHangulWithoutJamoT(r rune) bool { + if !isHangul(r) { + return false + } + r -= HangulBase + return r < JamoLVTCount && r%JamoTCount == 0 +} + +func ccc(r rune) uint8 { + return chars[r].ccc +} + +// Insert a rune in a buffer, ordered by Canonical Combining Class. +func insertOrdered(b Decomposition, r rune) Decomposition { + n := len(b) + b = append(b, 0) + cc := ccc(r) + if cc > 0 { + // Use bubble sort. + for ; n > 0; n-- { + if ccc(b[n-1]) <= cc { + break + } + b[n] = b[n-1] + } + } + b[n] = r + return b +} + +// Recursively decompose. +func decomposeRecursive(form int, r rune, d Decomposition) Decomposition { + dcomp := chars[r].forms[form].decomp + if len(dcomp) == 0 { + return insertOrdered(d, r) + } + for _, c := range dcomp { + d = decomposeRecursive(form, c, d) + } + return d +} + +func completeCharFields(form int) { + // Phase 0: pre-expand decomposition. + for i := range chars { + f := &chars[i].forms[form] + if len(f.decomp) == 0 { + continue + } + exp := make(Decomposition, 0) + for _, c := range f.decomp { + exp = decomposeRecursive(form, c, exp) + } + f.expandedDecomp = exp + } + + // Phase 1: composition exclusion, mark decomposition. + for i := range chars { + c := &chars[i] + f := &c.forms[form] + + // Marks script-specific exclusions and version restricted. + f.isOneWay = c.excludeInComp + + // Singletons + f.isOneWay = f.isOneWay || len(f.decomp) == 1 + + // Non-starter decompositions + if len(f.decomp) > 1 { + chk := c.ccc != 0 || chars[f.decomp[0]].ccc != 0 + f.isOneWay = f.isOneWay || chk + } + + // Runes that decompose into more than two runes. + f.isOneWay = f.isOneWay || len(f.decomp) > 2 + + if form == FCompatibility { + f.isOneWay = f.isOneWay || hasCompatDecomp(c.codePoint) + } + + for _, r := range f.decomp { + chars[r].forms[form].inDecomp = true + } + } + + // Phase 2: forward and backward combining. + for i := range chars { + c := &chars[i] + f := &c.forms[form] + + if !f.isOneWay && len(f.decomp) == 2 { + f0 := &chars[f.decomp[0]].forms[form] + f1 := &chars[f.decomp[1]].forms[form] + if !f0.isOneWay { + f0.combinesForward = true + } + if !f1.isOneWay { + f1.combinesBackward = true + } + } + if isHangulWithoutJamoT(rune(i)) { + f.combinesForward = true + } + } + + // Phase 3: quick check values. + for i := range chars { + c := &chars[i] + f := &c.forms[form] + + switch { + case len(f.decomp) > 0: + f.quickCheck[MDecomposed] = QCNo + case isHangul(rune(i)): + f.quickCheck[MDecomposed] = QCNo + default: + f.quickCheck[MDecomposed] = QCYes + } + switch { + case f.isOneWay: + f.quickCheck[MComposed] = QCNo + case (i & 0xffff00) == JamoLBase: + f.quickCheck[MComposed] = QCYes + if JamoLBase <= i && i < JamoLEnd { + f.combinesForward = true + } + if JamoVBase <= i && i < JamoVEnd { + f.quickCheck[MComposed] = QCMaybe + f.combinesBackward = true + f.combinesForward = true + } + if JamoTBase <= i && i < JamoTEnd { + f.quickCheck[MComposed] = QCMaybe + f.combinesBackward = true + } + case !f.combinesBackward: + f.quickCheck[MComposed] = QCYes + default: + f.quickCheck[MComposed] = QCMaybe + } + } +} + +func computeNonStarterCounts() { + // Phase 4: leading and trailing non-starter count + for i := range chars { + c := &chars[i] + + runes := []rune{rune(i)} + // We always use FCompatibility so that the CGJ insertion points do not + // change for repeated normalizations with different forms. + if exp := c.forms[FCompatibility].expandedDecomp; len(exp) > 0 { + runes = exp + } + // We consider runes that combine backwards to be non-starters for the + // purpose of Stream-Safe Text Processing. + for _, r := range runes { + if cr := &chars[r]; cr.ccc == 0 && !cr.forms[FCompatibility].combinesBackward { + break + } + c.nLeadingNonStarters++ + } + for i := len(runes) - 1; i >= 0; i-- { + if cr := &chars[runes[i]]; cr.ccc == 0 && !cr.forms[FCompatibility].combinesBackward { + break + } + c.nTrailingNonStarters++ + } + if c.nTrailingNonStarters > 3 { + log.Fatalf("%U: Decomposition with more than 3 (%d) trailing modifiers (%U)", i, c.nTrailingNonStarters, runes) + } + + if isHangul(rune(i)) { + c.nTrailingNonStarters = 2 + if isHangulWithoutJamoT(rune(i)) { + c.nTrailingNonStarters = 1 + } + } + + if l, t := c.nLeadingNonStarters, c.nTrailingNonStarters; l > 0 && l != t { + log.Fatalf("%U: number of leading and trailing non-starters should be equal (%d vs %d)", i, l, t) + } + if t := c.nTrailingNonStarters; t > 3 { + log.Fatalf("%U: number of trailing non-starters is %d > 3", t) + } + } +} + +func printBytes(w io.Writer, b []byte, name string) { + fmt.Fprintf(w, "// %s: %d bytes\n", name, len(b)) + fmt.Fprintf(w, "var %s = [...]byte {", name) + for i, c := range b { + switch { + case i%64 == 0: + fmt.Fprintf(w, "\n// Bytes %x - %x\n", i, i+63) + case i%8 == 0: + fmt.Fprintf(w, "\n") + } + fmt.Fprintf(w, "0x%.2X, ", c) + } + fmt.Fprint(w, "\n}\n\n") +} + +// See forminfo.go for format. +func makeEntry(f *FormInfo, c *Char) uint16 { + e := uint16(0) + if r := c.codePoint; HangulBase <= r && r < HangulEnd { + e |= 0x40 + } + if f.combinesForward { + e |= 0x20 + } + if f.quickCheck[MDecomposed] == QCNo { + e |= 0x4 + } + switch f.quickCheck[MComposed] { + case QCYes: + case QCNo: + e |= 0x10 + case QCMaybe: + e |= 0x18 + default: + log.Fatalf("Illegal quickcheck value %v.", f.quickCheck[MComposed]) + } + e |= uint16(c.nTrailingNonStarters) + return e +} + +// decompSet keeps track of unique decompositions, grouped by whether +// the decomposition is followed by a trailing and/or leading CCC. +type decompSet [7]map[string]bool + +const ( + normalDecomp = iota + firstMulti + firstCCC + endMulti + firstLeadingCCC + firstCCCZeroExcept + firstStarterWithNLead + lastDecomp +) + +var cname = []string{"firstMulti", "firstCCC", "endMulti", "firstLeadingCCC", "firstCCCZeroExcept", "firstStarterWithNLead", "lastDecomp"} + +func makeDecompSet() decompSet { + m := decompSet{} + for i := range m { + m[i] = make(map[string]bool) + } + return m +} +func (m *decompSet) insert(key int, s string) { + m[key][s] = true +} + +func printCharInfoTables(w io.Writer) int { + mkstr := func(r rune, f *FormInfo) (int, string) { + d := f.expandedDecomp + s := string([]rune(d)) + if max := 1 << 6; len(s) >= max { + const msg = "%U: too many bytes in decomposition: %d >= %d" + log.Fatalf(msg, r, len(s), max) + } + head := uint8(len(s)) + if f.quickCheck[MComposed] != QCYes { + head |= 0x40 + } + if f.combinesForward { + head |= 0x80 + } + s = string([]byte{head}) + s + + lccc := ccc(d[0]) + tccc := ccc(d[len(d)-1]) + cc := ccc(r) + if cc != 0 && lccc == 0 && tccc == 0 { + log.Fatalf("%U: trailing and leading ccc are 0 for non-zero ccc %d", r, cc) + } + if tccc < lccc && lccc != 0 { + const msg = "%U: lccc (%d) must be <= tcc (%d)" + log.Fatalf(msg, r, lccc, tccc) + } + index := normalDecomp + nTrail := chars[r].nTrailingNonStarters + nLead := chars[r].nLeadingNonStarters + if tccc > 0 || lccc > 0 || nTrail > 0 { + tccc <<= 2 + tccc |= nTrail + s += string([]byte{tccc}) + index = endMulti + for _, r := range d[1:] { + if ccc(r) == 0 { + index = firstCCC + } + } + if lccc > 0 || nLead > 0 { + s += string([]byte{lccc}) + if index == firstCCC { + log.Fatalf("%U: multi-segment decomposition not supported for decompositions with leading CCC != 0", r) + } + index = firstLeadingCCC + } + if cc != lccc { + if cc != 0 { + log.Fatalf("%U: for lccc != ccc, expected ccc to be 0; was %d", r, cc) + } + index = firstCCCZeroExcept + } + } else if len(d) > 1 { + index = firstMulti + } + return index, s + } + + decompSet := makeDecompSet() + const nLeadStr = "\x00\x01" // 0-byte length and tccc with nTrail. + decompSet.insert(firstStarterWithNLead, nLeadStr) + + // Store the uniqued decompositions in a byte buffer, + // preceded by their byte length. + for _, c := range chars { + for _, f := range c.forms { + if len(f.expandedDecomp) == 0 { + continue + } + if f.combinesBackward { + log.Fatalf("%U: combinesBackward and decompose", c.codePoint) + } + index, s := mkstr(c.codePoint, &f) + decompSet.insert(index, s) + } + } + + decompositions := bytes.NewBuffer(make([]byte, 0, 10000)) + size := 0 + positionMap := make(map[string]uint16) + decompositions.WriteString("\000") + fmt.Fprintln(w, "const (") + for i, m := range decompSet { + sa := []string{} + for s := range m { + sa = append(sa, s) + } + sort.Strings(sa) + for _, s := range sa { + p := decompositions.Len() + decompositions.WriteString(s) + positionMap[s] = uint16(p) + } + if cname[i] != "" { + fmt.Fprintf(w, "%s = 0x%X\n", cname[i], decompositions.Len()) + } + } + fmt.Fprintln(w, "maxDecomp = 0x8000") + fmt.Fprintln(w, ")") + b := decompositions.Bytes() + printBytes(w, b, "decomps") + size += len(b) + + varnames := []string{"nfc", "nfkc"} + for i := 0; i < FNumberOfFormTypes; i++ { + trie := triegen.NewTrie(varnames[i]) + + for r, c := range chars { + f := c.forms[i] + d := f.expandedDecomp + if len(d) != 0 { + _, key := mkstr(c.codePoint, &f) + trie.Insert(rune(r), uint64(positionMap[key])) + if c.ccc != ccc(d[0]) { + // We assume the lead ccc of a decomposition !=0 in this case. + if ccc(d[0]) == 0 { + log.Fatalf("Expected leading CCC to be non-zero; ccc is %d", c.ccc) + } + } + } else if c.nLeadingNonStarters > 0 && len(f.expandedDecomp) == 0 && c.ccc == 0 && !f.combinesBackward { + // Handle cases where it can't be detected that the nLead should be equal + // to nTrail. + trie.Insert(c.codePoint, uint64(positionMap[nLeadStr])) + } else if v := makeEntry(&f, &c)<<8 | uint16(c.ccc); v != 0 { + trie.Insert(c.codePoint, uint64(0x8000|v)) + } + } + sz, err := trie.Gen(w, triegen.Compact(&normCompacter{name: varnames[i]})) + if err != nil { + log.Fatal(err) + } + size += sz + } + return size +} + +func contains(sa []string, s string) bool { + for _, a := range sa { + if a == s { + return true + } + } + return false +} + +func makeTables() { + w := &bytes.Buffer{} + + size := 0 + if *tablelist == "" { + return + } + list := strings.Split(*tablelist, ",") + if *tablelist == "all" { + list = []string{"recomp", "info"} + } + + // Compute maximum decomposition size. + max := 0 + for _, c := range chars { + if n := len(string(c.forms[FCompatibility].expandedDecomp)); n > max { + max = n + } + } + fmt.Fprintln(w, `import "sync"`) + fmt.Fprintln(w) + + fmt.Fprintln(w, "const (") + fmt.Fprintln(w, "\t// Version is the Unicode edition from which the tables are derived.") + fmt.Fprintf(w, "\tVersion = %q\n", gen.UnicodeVersion()) + fmt.Fprintln(w) + fmt.Fprintln(w, "\t// MaxTransformChunkSize indicates the maximum number of bytes that Transform") + fmt.Fprintln(w, "\t// may need to write atomically for any Form. Making a destination buffer at") + fmt.Fprintln(w, "\t// least this size ensures that Transform can always make progress and that") + fmt.Fprintln(w, "\t// the user does not need to grow the buffer on an ErrShortDst.") + fmt.Fprintf(w, "\tMaxTransformChunkSize = %d+maxNonStarters*4\n", len(string(0x034F))+max) + fmt.Fprintln(w, ")\n") + + // Print the CCC remap table. + size += len(cccMap) + fmt.Fprintf(w, "var ccc = [%d]uint8{", len(cccMap)) + for i := 0; i < len(cccMap); i++ { + if i%8 == 0 { + fmt.Fprintln(w) + } + fmt.Fprintf(w, "%3d, ", cccMap[uint8(i)]) + } + fmt.Fprintln(w, "\n}\n") + + if contains(list, "info") { + size += printCharInfoTables(w) + } + + if contains(list, "recomp") { + // Note that we use 32 bit keys, instead of 64 bit. + // This clips the bits of three entries, but we know + // this won't cause a collision. The compiler will catch + // any changes made to UnicodeData.txt that introduces + // a collision. + // Note that the recomposition map for NFC and NFKC + // are identical. + + // Recomposition map + nrentries := 0 + for _, c := range chars { + f := c.forms[FCanonical] + if !f.isOneWay && len(f.decomp) > 0 { + nrentries++ + } + } + sz := nrentries * 8 + size += sz + fmt.Fprintf(w, "// recompMap: %d bytes (entries only)\n", sz) + fmt.Fprintln(w, "var recompMap map[uint32]rune") + fmt.Fprintln(w, "var recompMapOnce sync.Once\n") + fmt.Fprintln(w, `const recompMapPacked = "" +`) + var buf [8]byte + for i, c := range chars { + f := c.forms[FCanonical] + d := f.decomp + if !f.isOneWay && len(d) > 0 { + key := uint32(uint16(d[0]))<<16 + uint32(uint16(d[1])) + binary.BigEndian.PutUint32(buf[:4], key) + binary.BigEndian.PutUint32(buf[4:], uint32(i)) + fmt.Fprintf(w, "\t\t%q + // 0x%.8X: 0x%.8X\n", string(buf[:]), key, uint32(i)) + } + } + // hack so we don't have to special case the trailing plus sign + fmt.Fprintf(w, ` ""`) + fmt.Fprintln(w) + } + + fmt.Fprintf(w, "// Total size of tables: %dKB (%d bytes)\n", (size+512)/1024, size) + gen.WriteVersionedGoFile("tables.go", "norm", w.Bytes()) +} + +func printChars() { + if *verbose { + for _, c := range chars { + if !c.isValid() || c.state == SMissing { + continue + } + fmt.Println(c) + } + } +} + +// verifyComputed does various consistency tests. +func verifyComputed() { + for i, c := range chars { + for _, f := range c.forms { + isNo := (f.quickCheck[MDecomposed] == QCNo) + if (len(f.decomp) > 0) != isNo && !isHangul(rune(i)) { + log.Fatalf("%U: NF*D QC must be No if rune decomposes", i) + } + + isMaybe := f.quickCheck[MComposed] == QCMaybe + if f.combinesBackward != isMaybe { + log.Fatalf("%U: NF*C QC must be Maybe if combinesBackward", i) + } + if len(f.decomp) > 0 && f.combinesForward && isMaybe { + log.Fatalf("%U: NF*C QC must be Yes or No if combinesForward and decomposes", i) + } + + if len(f.expandedDecomp) != 0 { + continue + } + if a, b := c.nLeadingNonStarters > 0, (c.ccc > 0 || f.combinesBackward); a != b { + // We accept these runes to be treated differently (it only affects + // segment breaking in iteration, most likely on improper use), but + // reconsider if more characters are added. + // U+FF9E HALFWIDTH KATAKANA VOICED SOUND MARK;Lm;0;L; 3099;;;;N;;;;; + // U+FF9F HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK;Lm;0;L; 309A;;;;N;;;;; + // U+3133 HANGUL LETTER KIYEOK-SIOS;Lo;0;L; 11AA;;;;N;HANGUL LETTER GIYEOG SIOS;;;; + // U+318E HANGUL LETTER ARAEAE;Lo;0;L; 11A1;;;;N;HANGUL LETTER ALAE AE;;;; + // U+FFA3 HALFWIDTH HANGUL LETTER KIYEOK-SIOS;Lo;0;L; 3133;;;;N;HALFWIDTH HANGUL LETTER GIYEOG SIOS;;;; + // U+FFDC HALFWIDTH HANGUL LETTER I;Lo;0;L; 3163;;;;N;;;;; + if i != 0xFF9E && i != 0xFF9F && !(0x3133 <= i && i <= 0x318E) && !(0xFFA3 <= i && i <= 0xFFDC) { + log.Fatalf("%U: nLead was %v; want %v", i, a, b) + } + } + } + nfc := c.forms[FCanonical] + nfkc := c.forms[FCompatibility] + if nfc.combinesBackward != nfkc.combinesBackward { + log.Fatalf("%U: Cannot combine combinesBackward\n", c.codePoint) + } + } +} + +// Use values in DerivedNormalizationProps.txt to compare against the +// values we computed. +// DerivedNormalizationProps.txt has form: +// 00C0..00C5 ; NFD_QC; N # ... +// 0374 ; NFD_QC; N # ... +// See https://unicode.org/reports/tr44/ for full explanation +func testDerived() { + f := gen.OpenUCDFile("DerivedNormalizationProps.txt") + defer f.Close() + p := ucd.New(f) + for p.Next() { + r := p.Rune(0) + c := &chars[r] + + var ftype, mode int + qt := p.String(1) + switch qt { + case "NFC_QC": + ftype, mode = FCanonical, MComposed + case "NFD_QC": + ftype, mode = FCanonical, MDecomposed + case "NFKC_QC": + ftype, mode = FCompatibility, MComposed + case "NFKD_QC": + ftype, mode = FCompatibility, MDecomposed + default: + continue + } + var qr QCResult + switch p.String(2) { + case "Y": + qr = QCYes + case "N": + qr = QCNo + case "M": + qr = QCMaybe + default: + log.Fatalf(`Unexpected quick check value "%s"`, p.String(2)) + } + if got := c.forms[ftype].quickCheck[mode]; got != qr { + log.Printf("%U: FAILED %s (was %v need %v)\n", r, qt, got, qr) + } + c.forms[ftype].verified[mode] = true + } + if err := p.Err(); err != nil { + log.Fatal(err) + } + // Any unspecified value must be QCYes. Verify this. + for i, c := range chars { + for j, fd := range c.forms { + for k, qr := range fd.quickCheck { + if !fd.verified[k] && qr != QCYes { + m := "%U: FAIL F:%d M:%d (was %v need Yes) %s\n" + log.Printf(m, i, j, k, qr, c.name) + } + } + } + } +} + +var testHeader = `const ( + Yes = iota + No + Maybe +) + +type formData struct { + qc uint8 + combinesForward bool + decomposition string +} + +type runeData struct { + r rune + ccc uint8 + nLead uint8 + nTrail uint8 + f [2]formData // 0: canonical; 1: compatibility +} + +func f(qc uint8, cf bool, dec string) [2]formData { + return [2]formData{{qc, cf, dec}, {qc, cf, dec}} +} + +func g(qc, qck uint8, cf, cfk bool, d, dk string) [2]formData { + return [2]formData{{qc, cf, d}, {qck, cfk, dk}} +} + +var testData = []runeData{ +` + +func printTestdata() { + type lastInfo struct { + ccc uint8 + nLead uint8 + nTrail uint8 + f string + } + + last := lastInfo{} + w := &bytes.Buffer{} + fmt.Fprintf(w, testHeader) + for r, c := range chars { + f := c.forms[FCanonical] + qc, cf, d := f.quickCheck[MComposed], f.combinesForward, string(f.expandedDecomp) + f = c.forms[FCompatibility] + qck, cfk, dk := f.quickCheck[MComposed], f.combinesForward, string(f.expandedDecomp) + s := "" + if d == dk && qc == qck && cf == cfk { + s = fmt.Sprintf("f(%s, %v, %q)", qc, cf, d) + } else { + s = fmt.Sprintf("g(%s, %s, %v, %v, %q, %q)", qc, qck, cf, cfk, d, dk) + } + current := lastInfo{c.ccc, c.nLeadingNonStarters, c.nTrailingNonStarters, s} + if last != current { + fmt.Fprintf(w, "\t{0x%x, %d, %d, %d, %s},\n", r, c.origCCC, c.nLeadingNonStarters, c.nTrailingNonStarters, s) + last = current + } + } + fmt.Fprintln(w, "}") + gen.WriteVersionedGoFile("data_test.go", "norm", w.Bytes()) +} diff --git a/vendor/golang.org/x/text/unicode/norm/triegen.go b/vendor/golang.org/x/text/unicode/norm/triegen.go new file mode 100644 index 0000000000..45d711900d --- /dev/null +++ b/vendor/golang.org/x/text/unicode/norm/triegen.go @@ -0,0 +1,117 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// Trie table generator. +// Used by make*tables tools to generate a go file with trie data structures +// for mapping UTF-8 to a 16-bit value. All but the last byte in a UTF-8 byte +// sequence are used to lookup offsets in the index table to be used for the +// next byte. The last byte is used to index into a table with 16-bit values. + +package main + +import ( + "fmt" + "io" +) + +const maxSparseEntries = 16 + +type normCompacter struct { + sparseBlocks [][]uint64 + sparseOffset []uint16 + sparseCount int + name string +} + +func mostFrequentStride(a []uint64) int { + counts := make(map[int]int) + var v int + for _, x := range a { + if stride := int(x) - v; v != 0 && stride >= 0 { + counts[stride]++ + } + v = int(x) + } + var maxs, maxc int + for stride, cnt := range counts { + if cnt > maxc || (cnt == maxc && stride < maxs) { + maxs, maxc = stride, cnt + } + } + return maxs +} + +func countSparseEntries(a []uint64) int { + stride := mostFrequentStride(a) + var v, count int + for _, tv := range a { + if int(tv)-v != stride { + if tv != 0 { + count++ + } + } + v = int(tv) + } + return count +} + +func (c *normCompacter) Size(v []uint64) (sz int, ok bool) { + if n := countSparseEntries(v); n <= maxSparseEntries { + return (n+1)*4 + 2, true + } + return 0, false +} + +func (c *normCompacter) Store(v []uint64) uint32 { + h := uint32(len(c.sparseOffset)) + c.sparseBlocks = append(c.sparseBlocks, v) + c.sparseOffset = append(c.sparseOffset, uint16(c.sparseCount)) + c.sparseCount += countSparseEntries(v) + 1 + return h +} + +func (c *normCompacter) Handler() string { + return c.name + "Sparse.lookup" +} + +func (c *normCompacter) Print(w io.Writer) (retErr error) { + p := func(f string, x ...interface{}) { + if _, err := fmt.Fprintf(w, f, x...); retErr == nil && err != nil { + retErr = err + } + } + + ls := len(c.sparseBlocks) + p("// %sSparseOffset: %d entries, %d bytes\n", c.name, ls, ls*2) + p("var %sSparseOffset = %#v\n\n", c.name, c.sparseOffset) + + ns := c.sparseCount + p("// %sSparseValues: %d entries, %d bytes\n", c.name, ns, ns*4) + p("var %sSparseValues = [%d]valueRange {", c.name, ns) + for i, b := range c.sparseBlocks { + p("\n// Block %#x, offset %#x", i, c.sparseOffset[i]) + var v int + stride := mostFrequentStride(b) + n := countSparseEntries(b) + p("\n{value:%#04x,lo:%#02x},", stride, uint8(n)) + for i, nv := range b { + if int(nv)-v != stride { + if v != 0 { + p(",hi:%#02x},", 0x80+i-1) + } + if nv != 0 { + p("\n{value:%#04x,lo:%#02x", nv, 0x80+i) + } + } + v = int(nv) + } + if v != 0 { + p(",hi:%#02x},", 0x80+len(b)-1) + } + } + p("\n}\n\n") + return +} diff --git a/vendor/golang.org/x/text/width/gen.go b/vendor/golang.org/x/text/width/gen.go new file mode 100644 index 0000000000..092277e1f6 --- /dev/null +++ b/vendor/golang.org/x/text/width/gen.go @@ -0,0 +1,115 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// This program generates the trie for width operations. The generated table +// includes width category information as well as the normalization mappings. +package main + +import ( + "bytes" + "fmt" + "io" + "log" + "math" + "unicode/utf8" + + "golang.org/x/text/internal/gen" + "golang.org/x/text/internal/triegen" +) + +// See gen_common.go for flags. + +func main() { + gen.Init() + genTables() + genTests() + gen.Repackage("gen_trieval.go", "trieval.go", "width") + gen.Repackage("gen_common.go", "common_test.go", "width") +} + +func genTables() { + t := triegen.NewTrie("width") + // fold and inverse mappings. See mapComment for a description of the format + // of each entry. Add dummy value to make an index of 0 mean no mapping. + inverse := [][4]byte{{}} + mapping := map[[4]byte]int{[4]byte{}: 0} + + getWidthData(func(r rune, tag elem, alt rune) { + idx := 0 + if alt != 0 { + var buf [4]byte + buf[0] = byte(utf8.EncodeRune(buf[1:], alt)) + s := string(r) + buf[buf[0]] ^= s[len(s)-1] + var ok bool + if idx, ok = mapping[buf]; !ok { + idx = len(mapping) + if idx > math.MaxUint8 { + log.Fatalf("Index %d does not fit in a byte.", idx) + } + mapping[buf] = idx + inverse = append(inverse, buf) + } + } + t.Insert(r, uint64(tag|elem(idx))) + }) + + w := &bytes.Buffer{} + gen.WriteUnicodeVersion(w) + + sz, err := t.Gen(w) + if err != nil { + log.Fatal(err) + } + + sz += writeMappings(w, inverse) + + fmt.Fprintf(w, "// Total table size %d bytes (%dKiB)\n", sz, sz/1024) + + gen.WriteVersionedGoFile(*outputFile, "width", w.Bytes()) +} + +const inverseDataComment = ` +// inverseData contains 4-byte entries of the following format: +// <0 padding> +// The last byte of the UTF-8-encoded rune is xor-ed with the last byte of the +// UTF-8 encoding of the original rune. Mappings often have the following +// pattern: +// A -> A (U+FF21 -> U+0041) +// B -> B (U+FF22 -> U+0042) +// ... +// By xor-ing the last byte the same entry can be shared by many mappings. This +// reduces the total number of distinct entries by about two thirds. +// The resulting entry for the aforementioned mappings is +// { 0x01, 0xE0, 0x00, 0x00 } +// Using this entry to map U+FF21 (UTF-8 [EF BC A1]), we get +// E0 ^ A1 = 41. +// Similarly, for U+FF22 (UTF-8 [EF BC A2]), we get +// E0 ^ A2 = 42. +// Note that because of the xor-ing, the byte sequence stored in the entry is +// not valid UTF-8.` + +func writeMappings(w io.Writer, data [][4]byte) int { + fmt.Fprintln(w, inverseDataComment) + fmt.Fprintf(w, "var inverseData = [%d][4]byte{\n", len(data)) + for _, x := range data { + fmt.Fprintf(w, "{ 0x%02x, 0x%02x, 0x%02x, 0x%02x },\n", x[0], x[1], x[2], x[3]) + } + fmt.Fprintln(w, "}") + return len(data) * 4 +} + +func genTests() { + w := &bytes.Buffer{} + fmt.Fprintf(w, "\nvar mapRunes = map[rune]struct{r rune; e elem}{\n") + getWidthData(func(r rune, tag elem, alt rune) { + if alt != 0 { + fmt.Fprintf(w, "\t0x%X: {0x%X, 0x%X},\n", r, alt, tag) + } + }) + fmt.Fprintln(w, "}") + gen.WriteGoFile("runes_test.go", "width", w.Bytes()) +} diff --git a/vendor/golang.org/x/text/width/gen_common.go b/vendor/golang.org/x/text/width/gen_common.go new file mode 100644 index 0000000000..601e752684 --- /dev/null +++ b/vendor/golang.org/x/text/width/gen_common.go @@ -0,0 +1,96 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +package main + +// This code is shared between the main code generator and the test code. + +import ( + "flag" + "log" + "strconv" + "strings" + + "golang.org/x/text/internal/gen" + "golang.org/x/text/internal/ucd" +) + +var ( + outputFile = flag.String("out", "tables.go", "output file") +) + +var typeMap = map[string]elem{ + "A": tagAmbiguous, + "N": tagNeutral, + "Na": tagNarrow, + "W": tagWide, + "F": tagFullwidth, + "H": tagHalfwidth, +} + +// getWidthData calls f for every entry for which it is defined. +// +// f may be called multiple times for the same rune. The last call to f is the +// correct value. f is not called for all runes. The default tag type is +// Neutral. +func getWidthData(f func(r rune, tag elem, alt rune)) { + // Set the default values for Unified Ideographs. In line with Annex 11, + // we encode full ranges instead of the defined runes in Unified_Ideograph. + for _, b := range []struct{ lo, hi rune }{ + {0x4E00, 0x9FFF}, // the CJK Unified Ideographs block, + {0x3400, 0x4DBF}, // the CJK Unified Ideographs Externsion A block, + {0xF900, 0xFAFF}, // the CJK Compatibility Ideographs block, + {0x20000, 0x2FFFF}, // the Supplementary Ideographic Plane, + {0x30000, 0x3FFFF}, // the Tertiary Ideographic Plane, + } { + for r := b.lo; r <= b.hi; r++ { + f(r, tagWide, 0) + } + } + + inverse := map[rune]rune{} + maps := map[string]bool{ + "": true, + "": true, + } + + // We cannot reuse package norm's decomposition, as we need an unexpanded + // decomposition. We make use of the opportunity to verify that the + // decomposition type is as expected. + ucd.Parse(gen.OpenUCDFile("UnicodeData.txt"), func(p *ucd.Parser) { + r := p.Rune(0) + s := strings.SplitN(p.String(ucd.DecompMapping), " ", 2) + if !maps[s[0]] { + return + } + x, err := strconv.ParseUint(s[1], 16, 32) + if err != nil { + log.Fatalf("Error parsing rune %q", s[1]) + } + if inverse[r] != 0 || inverse[rune(x)] != 0 { + log.Fatalf("Circular dependency in mapping between %U and %U", r, x) + } + inverse[r] = rune(x) + inverse[rune(x)] = r + }) + + // ; + ucd.Parse(gen.OpenUCDFile("EastAsianWidth.txt"), func(p *ucd.Parser) { + tag, ok := typeMap[p.String(1)] + if !ok { + log.Fatalf("Unknown width type %q", p.String(1)) + } + r := p.Rune(0) + alt, ok := inverse[r] + if tag == tagFullwidth || tag == tagHalfwidth && r != wonSign { + tag |= tagNeedsFold + if !ok { + log.Fatalf("Narrow or wide rune %U has no decomposition", r) + } + } + f(r, tag, alt) + }) +} diff --git a/vendor/golang.org/x/text/width/gen_trieval.go b/vendor/golang.org/x/text/width/gen_trieval.go new file mode 100644 index 0000000000..c17334aa61 --- /dev/null +++ b/vendor/golang.org/x/text/width/gen_trieval.go @@ -0,0 +1,34 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +package main + +// elem is an entry of the width trie. The high byte is used to encode the type +// of the rune. The low byte is used to store the index to a mapping entry in +// the inverseData array. +type elem uint16 + +const ( + tagNeutral elem = iota << typeShift + tagAmbiguous + tagWide + tagNarrow + tagFullwidth + tagHalfwidth +) + +const ( + numTypeBits = 3 + typeShift = 16 - numTypeBits + + // tagNeedsFold is true for all fullwidth and halfwidth runes except for + // the Won sign U+20A9. + tagNeedsFold = 0x1000 + + // The Korean Won sign is halfwidth, but SHOULD NOT be mapped to a wide + // variant. + wonSign rune = 0x20A9 +) diff --git a/vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go b/vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go index 98b3987b97..f8363d8faa 100644 --- a/vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go +++ b/vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go @@ -100,7 +100,7 @@ func Read(in io.Reader, fset *token.FileSet, imports map[string]*types.Package, // Write writes encoded type information for the specified package to out. // The FileSet provides file position information for named objects. func Write(out io.Writer, fset *token.FileSet, pkg *types.Package) error { - b, err := gcimporter.BExportData(fset, pkg) + b, err := gcimporter.IExportData(fset, pkg) if err != nil { return err } diff --git a/vendor/golang.org/x/tools/go/gcexportdata/main.go b/vendor/golang.org/x/tools/go/gcexportdata/main.go new file mode 100644 index 0000000000..2713dce64a --- /dev/null +++ b/vendor/golang.org/x/tools/go/gcexportdata/main.go @@ -0,0 +1,99 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// The gcexportdata command is a diagnostic tool that displays the +// contents of gc export data files. +package main + +import ( + "flag" + "fmt" + "go/token" + "go/types" + "log" + "os" + + "golang.org/x/tools/go/gcexportdata" + "golang.org/x/tools/go/types/typeutil" +) + +var packageFlag = flag.String("package", "", "alternative package to print") + +func main() { + log.SetPrefix("gcexportdata: ") + log.SetFlags(0) + flag.Usage = func() { + fmt.Fprintln(os.Stderr, "usage: gcexportdata [-package path] file.a") + } + flag.Parse() + if flag.NArg() != 1 { + flag.Usage() + os.Exit(2) + } + filename := flag.Args()[0] + + f, err := os.Open(filename) + if err != nil { + log.Fatal(err) + } + + r, err := gcexportdata.NewReader(f) + if err != nil { + log.Fatalf("%s: %s", filename, err) + } + + // Decode the package. + const primary = "" + imports := make(map[string]*types.Package) + fset := token.NewFileSet() + pkg, err := gcexportdata.Read(r, fset, imports, primary) + if err != nil { + log.Fatalf("%s: %s", filename, err) + } + + // Optionally select an indirectly mentioned package. + if *packageFlag != "" { + pkg = imports[*packageFlag] + if pkg == nil { + fmt.Fprintf(os.Stderr, "export data file %s does not mention %s; has:\n", + filename, *packageFlag) + for p := range imports { + if p != primary { + fmt.Fprintf(os.Stderr, "\t%s\n", p) + } + } + os.Exit(1) + } + } + + // Print all package-level declarations, including non-exported ones. + fmt.Printf("package %s\n", pkg.Name()) + for _, imp := range pkg.Imports() { + fmt.Printf("import %q\n", imp.Path()) + } + qual := func(p *types.Package) string { + if pkg == p { + return "" + } + return p.Name() + } + scope := pkg.Scope() + for _, name := range scope.Names() { + obj := scope.Lookup(name) + fmt.Printf("%s: %s\n", + fset.Position(obj.Pos()), + types.ObjectString(obj, qual)) + + // For types, print each method. + if _, ok := obj.(*types.TypeName); ok { + for _, method := range typeutil.IntuitiveMethodSet(obj.Type(), nil) { + fmt.Printf("%s: %s\n", + fset.Position(method.Obj().Pos()), + types.SelectionString(method, qual)) + } + } + } +} diff --git a/vendor/golang.org/x/tools/go/internal/gcimporter/bimport.go b/vendor/golang.org/x/tools/go/internal/gcimporter/bimport.go index e3c3107825..e9f73d14a1 100644 --- a/vendor/golang.org/x/tools/go/internal/gcimporter/bimport.go +++ b/vendor/golang.org/x/tools/go/internal/gcimporter/bimport.go @@ -332,7 +332,7 @@ func (p *importer) pos() token.Pos { p.prevFile = file p.prevLine = line - return p.fake.pos(file, line) + return p.fake.pos(file, line, 0) } // Synthesize a token.Pos @@ -341,7 +341,9 @@ type fakeFileSet struct { files map[string]*token.File } -func (s *fakeFileSet) pos(file string, line int) token.Pos { +func (s *fakeFileSet) pos(file string, line, column int) token.Pos { + // TODO(mdempsky): Make use of column. + // Since we don't know the set of needed file positions, we // reserve maxlines positions per file. const maxlines = 64 * 1024 @@ -976,10 +978,11 @@ const ( aliasTag ) +var predeclOnce sync.Once var predecl []types.Type // initialized lazily func predeclared() []types.Type { - if predecl == nil { + predeclOnce.Do(func() { // initialize lazily to be sure that all // elements have been initialized before predecl = []types.Type{ // basic types @@ -1026,7 +1029,7 @@ func predeclared() []types.Type { // used internally by gc; never used by this package or in .a files anyType{}, } - } + }) return predecl } diff --git a/vendor/golang.org/x/tools/go/internal/gcimporter/iexport.go b/vendor/golang.org/x/tools/go/internal/gcimporter/iexport.go index be671c79b7..4be32a2e55 100644 --- a/vendor/golang.org/x/tools/go/internal/gcimporter/iexport.go +++ b/vendor/golang.org/x/tools/go/internal/gcimporter/iexport.go @@ -6,8 +6,6 @@ // This file was derived from $GOROOT/src/cmd/compile/internal/gc/iexport.go; // see that file for specification of the format. -// +build go1.11 - package gcimporter import ( @@ -28,7 +26,10 @@ import ( const iexportVersion = 0 // IExportData returns the binary export data for pkg. +// // If no file set is provided, position info will be missing. +// The package path of the top-level package will not be recorded, +// so that calls to IImportData can override with a provided package path. func IExportData(fset *token.FileSet, pkg *types.Package) (b []byte, err error) { defer func() { if e := recover(); e != nil { @@ -48,6 +49,7 @@ func IExportData(fset *token.FileSet, pkg *types.Package) (b []byte, err error) stringIndex: map[string]uint64{}, declIndex: map[types.Object]uint64{}, typIndex: map[types.Type]uint64{}, + localpkg: pkg, } for i, pt := range predeclared() { @@ -73,7 +75,7 @@ func IExportData(fset *token.FileSet, pkg *types.Package) (b []byte, err error) // Append indices to data0 section. dataLen := uint64(p.data0.Len()) w := p.newWriter() - w.writeIndex(p.declIndex, pkg) + w.writeIndex(p.declIndex) w.flush() // Assemble header. @@ -95,14 +97,14 @@ func IExportData(fset *token.FileSet, pkg *types.Package) (b []byte, err error) // we're writing out the main index, which is also read by // non-compiler tools and includes a complete package description // (i.e., name and height). -func (w *exportWriter) writeIndex(index map[types.Object]uint64, localpkg *types.Package) { +func (w *exportWriter) writeIndex(index map[types.Object]uint64) { // Build a map from packages to objects from that package. pkgObjs := map[*types.Package][]types.Object{} // For the main index, make sure to include every package that // we reference, even if we're not exporting (or reexporting) // any symbols from it. - pkgObjs[localpkg] = nil + pkgObjs[w.p.localpkg] = nil for pkg := range w.p.allPkgs { pkgObjs[pkg] = nil } @@ -121,12 +123,12 @@ func (w *exportWriter) writeIndex(index map[types.Object]uint64, localpkg *types } sort.Slice(pkgs, func(i, j int) bool { - return pkgs[i].Path() < pkgs[j].Path() + return w.exportPath(pkgs[i]) < w.exportPath(pkgs[j]) }) w.uint64(uint64(len(pkgs))) for _, pkg := range pkgs { - w.string(pkg.Path()) + w.string(w.exportPath(pkg)) w.string(pkg.Name()) w.uint64(uint64(0)) // package height is not needed for go/types @@ -143,6 +145,8 @@ type iexporter struct { fset *token.FileSet out *bytes.Buffer + localpkg *types.Package + // allPkgs tracks all packages that have been referenced by // the export data, so we can ensure to include them in the // main index. @@ -195,6 +199,13 @@ type exportWriter struct { prevLine int64 } +func (w *exportWriter) exportPath(pkg *types.Package) string { + if pkg == w.p.localpkg { + return "" + } + return pkg.Path() +} + func (p *iexporter) doDecl(obj types.Object) { w := p.newWriter() w.setPkg(obj.Pkg(), false) @@ -267,6 +278,11 @@ func (w *exportWriter) tag(tag byte) { } func (w *exportWriter) pos(pos token.Pos) { + if w.p.fset == nil { + w.int64(0) + return + } + p := w.p.fset.Position(pos) file := p.Filename line := int64(p.Line) @@ -299,7 +315,7 @@ func (w *exportWriter) pkg(pkg *types.Package) { // Ensure any referenced packages are declared in the main index. w.p.allPkgs[pkg] = true - w.string(pkg.Path()) + w.string(w.exportPath(pkg)) } func (w *exportWriter) qualifiedIdent(obj types.Object) { @@ -394,7 +410,7 @@ func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) { w.pos(f.Pos()) w.string(f.Name()) w.typ(f.Type(), pkg) - w.bool(f.Embedded()) + w.bool(f.Anonymous()) w.string(t.Tag(i)) // note (or tag) } diff --git a/vendor/golang.org/x/tools/go/internal/gcimporter/iimport.go b/vendor/golang.org/x/tools/go/internal/gcimporter/iimport.go index 3cb7ae5b9e..a31a880263 100644 --- a/vendor/golang.org/x/tools/go/internal/gcimporter/iimport.go +++ b/vendor/golang.org/x/tools/go/internal/gcimporter/iimport.go @@ -63,8 +63,8 @@ const ( // If the export data version is not recognized or the format is otherwise // compromised, an error is returned. func IImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) { - const currentVersion = 0 - version := -1 + const currentVersion = 1 + version := int64(-1) defer func() { if e := recover(); e != nil { if version > currentVersion { @@ -77,9 +77,9 @@ func IImportData(fset *token.FileSet, imports map[string]*types.Package, data [] r := &intReader{bytes.NewReader(data), path} - version = int(r.uint64()) + version = int64(r.uint64()) switch version { - case currentVersion: + case currentVersion, 0: default: errorf("unknown iexport format version %d", version) } @@ -93,7 +93,8 @@ func IImportData(fset *token.FileSet, imports map[string]*types.Package, data [] r.Seek(sLen+dLen, io.SeekCurrent) p := iimporter{ - ipath: path, + ipath: path, + version: int(version), stringData: stringData, stringCache: make(map[uint64]string), @@ -142,20 +143,18 @@ func IImportData(fset *token.FileSet, imports map[string]*types.Package, data [] p.pkgIndex[pkg] = nameIndex pkgList[i] = pkg } - var localpkg *types.Package - for _, pkg := range pkgList { - if pkg.Path() == path { - localpkg = pkg - } + if len(pkgList) == 0 { + errorf("no packages found for %s", path) + panic("unreachable") } - - names := make([]string, 0, len(p.pkgIndex[localpkg])) - for name := range p.pkgIndex[localpkg] { + p.ipkg = pkgList[0] + names := make([]string, 0, len(p.pkgIndex[p.ipkg])) + for name := range p.pkgIndex[p.ipkg] { names = append(names, name) } sort.Strings(names) for _, name := range names { - p.doDecl(localpkg, name) + p.doDecl(p.ipkg, name) } for _, typ := range p.interfaceList { @@ -165,17 +164,19 @@ func IImportData(fset *token.FileSet, imports map[string]*types.Package, data [] // record all referenced packages as imports list := append(([]*types.Package)(nil), pkgList[1:]...) sort.Sort(byPath(list)) - localpkg.SetImports(list) + p.ipkg.SetImports(list) // package was imported completely and without errors - localpkg.MarkComplete() + p.ipkg.MarkComplete() consumed, _ := r.Seek(0, io.SeekCurrent) - return int(consumed), localpkg, nil + return int(consumed), p.ipkg, nil } type iimporter struct { - ipath string + ipath string + ipkg *types.Package + version int stringData []byte stringCache map[uint64]string @@ -226,6 +227,9 @@ func (p *iimporter) pkgAt(off uint64) *types.Package { return pkg } path := p.stringAt(off) + if path == p.ipath { + return p.ipkg + } errorf("missing package %q in %q", path, p.ipath) return nil } @@ -255,6 +259,7 @@ type importReader struct { currPkg *types.Package prevFile string prevLine int64 + prevColumn int64 } func (r *importReader) obj(name string) { @@ -448,6 +453,19 @@ func (r *importReader) qualifiedIdent() (*types.Package, string) { } func (r *importReader) pos() token.Pos { + if r.p.version >= 1 { + r.posv1() + } else { + r.posv0() + } + + if r.prevFile == "" && r.prevLine == 0 && r.prevColumn == 0 { + return token.NoPos + } + return r.p.fake.pos(r.prevFile, int(r.prevLine), int(r.prevColumn)) +} + +func (r *importReader) posv0() { delta := r.int64() if delta != deltaNewFile { r.prevLine += delta @@ -457,12 +475,18 @@ func (r *importReader) pos() token.Pos { r.prevFile = r.string() r.prevLine = l } +} - if r.prevFile == "" && r.prevLine == 0 { - return token.NoPos +func (r *importReader) posv1() { + delta := r.int64() + r.prevColumn += delta >> 1 + if delta&1 != 0 { + delta = r.int64() + r.prevLine += delta >> 1 + if delta&1 != 0 { + r.prevFile = r.string() + } } - - return r.p.fake.pos(r.prevFile, int(r.prevLine)) } func (r *importReader) typ() types.Type { diff --git a/vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go b/vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go index fdc7da0568..ea15d57be1 100644 --- a/vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go +++ b/vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go @@ -82,15 +82,28 @@ func GetSizesGolist(ctx context.Context, buildFlags, env []string, dir string, u args = append(args, buildFlags...) args = append(args, "--", "unsafe") stdout, err := InvokeGo(ctx, env, dir, usesExportData, args...) + var goarch, compiler string if err != nil { - return nil, err - } - fields := strings.Fields(stdout.String()) - if len(fields) < 2 { - return nil, fmt.Errorf("could not determine GOARCH and Go compiler") + if strings.Contains(err.Error(), "cannot find main module") { + // User's running outside of a module. All bets are off. Get GOARCH and guess compiler is gc. + // TODO(matloob): Is this a problem in practice? + envout, enverr := InvokeGo(ctx, env, dir, usesExportData, "env", "GOARCH") + if enverr != nil { + return nil, err + } + goarch = strings.TrimSpace(envout.String()) + compiler = "gc" + } else { + return nil, err + } + } else { + fields := strings.Fields(stdout.String()) + if len(fields) < 2 { + return nil, fmt.Errorf("could not determine GOARCH and Go compiler") + } + goarch = fields[0] + compiler = fields[1] } - goarch := fields[0] - compiler := fields[1] return types.SizesFor(compiler, goarch), nil } diff --git a/vendor/golang.org/x/tools/go/packages/external.go b/vendor/golang.org/x/tools/go/packages/external.go index 22ff769ef2..6ac3e4f5b5 100644 --- a/vendor/golang.org/x/tools/go/packages/external.go +++ b/vendor/golang.org/x/tools/go/packages/external.go @@ -12,18 +12,34 @@ import ( "bytes" "encoding/json" "fmt" + "os" "os/exec" "strings" ) -// Driver +// The Driver Protocol +// +// The driver, given the inputs to a call to Load, returns metadata about the packages specified. +// This allows for different build systems to support go/packages by telling go/packages how the +// packages' source is organized. +// The driver is a binary, either specified by the GOPACKAGESDRIVER environment variable or in +// the path as gopackagesdriver. It's given the inputs to load in its argv. See the package +// documentation in doc.go for the full description of the patterns that need to be supported. +// A driver receives as a JSON-serialized driverRequest struct in standard input and will +// produce a JSON-serialized driverResponse (see definition in packages.go) in its standard output. + +// driverRequest is used to provide the portion of Load's Config that is needed by a driver. type driverRequest struct { - Command string `json:"command"` - Mode LoadMode `json:"mode"` - Env []string `json:"env"` - BuildFlags []string `json:"build_flags"` - Tests bool `json:"tests"` - Overlay map[string][]byte `json:"overlay"` + Mode LoadMode `json:"mode"` + // Env specifies the environment the underlying build system should be run in. + Env []string `json:"env"` + // BuildFlags are flags that should be passed to the underlying build system. + BuildFlags []string `json:"build_flags"` + // Tests specifies whether the patterns should also return test packages. + Tests bool `json:"tests"` + // Overlay maps file paths (relative to the driver's working directory) to the byte contents + // of overlay files. + Overlay map[string][]byte `json:"overlay"` } // findExternalDriver returns the file path of a tool that supplies @@ -61,12 +77,17 @@ func findExternalDriver(cfg *Config) driver { } buf := new(bytes.Buffer) + stderr := new(bytes.Buffer) cmd := exec.CommandContext(cfg.Context, tool, words...) cmd.Dir = cfg.Dir cmd.Env = cfg.Env cmd.Stdin = bytes.NewReader(req) cmd.Stdout = buf - cmd.Stderr = new(bytes.Buffer) + cmd.Stderr = stderr + if len(stderr.Bytes()) != 0 && os.Getenv("GOPACKAGESPRINTDRIVERERRORS") != "" { + fmt.Fprintf(os.Stderr, "%s stderr: <<%s>>\n", cmdDebugStr(cmd, words...), stderr) + } + if err := cmd.Run(); err != nil { return nil, fmt.Errorf("%v: %v: %s", tool, err, cmd.Stderr) } diff --git a/vendor/golang.org/x/tools/go/packages/golist.go b/vendor/golang.org/x/tools/go/packages/golist.go index 00e21a755c..c581bce976 100644 --- a/vendor/golang.org/x/tools/go/packages/golist.go +++ b/vendor/golang.org/x/tools/go/packages/golist.go @@ -13,6 +13,7 @@ import ( "log" "os" "os/exec" + "path" "path/filepath" "reflect" "regexp" @@ -20,10 +21,12 @@ import ( "strings" "sync" "time" + "unicode" "golang.org/x/tools/go/internal/packagesdriver" "golang.org/x/tools/internal/gopathwalk" "golang.org/x/tools/internal/semver" + "golang.org/x/tools/internal/span" ) // debug controls verbose logging. @@ -71,6 +74,28 @@ func (r *responseDeduper) addRoot(id string) { r.dr.Roots = append(r.dr.Roots, id) } +// goInfo contains global information from the go tool. +type goInfo struct { + rootDirs map[string]string + env goEnv +} + +type goEnv struct { + modulesOn bool +} + +func determineEnv(cfg *Config) goEnv { + buf, err := invokeGo(cfg, "env", "GOMOD") + if err != nil { + return goEnv{} + } + gomod := bytes.TrimSpace(buf.Bytes()) + + env := goEnv{} + env.modulesOn = len(gomod) > 0 + return env +} + // goListDriver uses the go list command to interpret the patterns and produce // the build system package structure. // See driver for more details. @@ -85,6 +110,33 @@ func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) { sizeswg.Done() }() } + defer sizeswg.Wait() + + // start fetching rootDirs + var info goInfo + var rootDirsReady, envReady = make(chan struct{}), make(chan struct{}) + go func() { + info.rootDirs = determineRootDirs(cfg) + close(rootDirsReady) + }() + go func() { + info.env = determineEnv(cfg) + close(envReady) + }() + getGoInfo := func() *goInfo { + <-rootDirsReady + <-envReady + return &info + } + + // Ensure that we don't leak goroutines: Load is synchronous, so callers will + // not expect it to access the fields of cfg after the call returns. + defer getGoInfo() + + // always pass getGoInfo to golistDriver + golistDriver := func(cfg *Config, patterns ...string) (*driverResponse, error) { + return golistDriver(cfg, getGoInfo, patterns...) + } // Determine files requested in contains patterns var containFiles []string @@ -147,7 +199,7 @@ extractQueries: var containsCandidates []string if len(containFiles) != 0 { - if err := runContainsQueries(cfg, golistDriver, response, containFiles); err != nil { + if err := runContainsQueries(cfg, golistDriver, response, containFiles, getGoInfo); err != nil { return nil, err } } @@ -158,7 +210,7 @@ extractQueries: } } - modifiedPkgs, needPkgs, err := processGolistOverlay(cfg, response) + modifiedPkgs, needPkgs, err := processGolistOverlay(cfg, response, getGoInfo) if err != nil { return nil, err } @@ -166,7 +218,7 @@ extractQueries: containsCandidates = append(containsCandidates, modifiedPkgs...) containsCandidates = append(containsCandidates, needPkgs...) } - if err := addNeededOverlayPackages(cfg, golistDriver, response, needPkgs); err != nil { + if err := addNeededOverlayPackages(cfg, golistDriver, response, needPkgs, getGoInfo); err != nil { return nil, err } // Check candidate packages for containFiles. @@ -198,28 +250,33 @@ extractQueries: return response.dr, nil } -func addNeededOverlayPackages(cfg *Config, driver driver, response *responseDeduper, pkgs []string) error { +func addNeededOverlayPackages(cfg *Config, driver driver, response *responseDeduper, pkgs []string, getGoInfo func() *goInfo) error { if len(pkgs) == 0 { return nil } - dr, err := driver(cfg, pkgs...) + drivercfg := *cfg + if getGoInfo().env.modulesOn { + drivercfg.BuildFlags = append(drivercfg.BuildFlags, "-mod=readonly") + } + dr, err := driver(&drivercfg, pkgs...) + if err != nil { return err } for _, pkg := range dr.Packages { response.addPackage(pkg) } - _, needPkgs, err := processGolistOverlay(cfg, response) + _, needPkgs, err := processGolistOverlay(cfg, response, getGoInfo) if err != nil { return err } - if err := addNeededOverlayPackages(cfg, driver, response, needPkgs); err != nil { + if err := addNeededOverlayPackages(cfg, driver, response, needPkgs, getGoInfo); err != nil { return err } return nil } -func runContainsQueries(cfg *Config, driver driver, response *responseDeduper, queries []string) error { +func runContainsQueries(cfg *Config, driver driver, response *responseDeduper, queries []string, goInfo func() *goInfo) error { for _, query := range queries { // TODO(matloob): Do only one query per directory. fdir := filepath.Dir(query) @@ -230,15 +287,42 @@ func runContainsQueries(cfg *Config, driver driver, response *responseDeduper, q return fmt.Errorf("could not determine absolute path of file= query path %q: %v", query, err) } dirResponse, err := driver(cfg, pattern) - if err != nil || (len(dirResponse.Packages) == 1 && len(dirResponse.Packages[0].Errors) == 1) { - // There was an error loading the package. Try to load the file as an ad-hoc package. - // Usually the error will appear in a returned package, but may not if we're in modules mode - // and the ad-hoc is located outside a module. + if err != nil { var queryErr error - dirResponse, queryErr = driver(cfg, query) - if queryErr != nil { - // Return the original error if the attempt to fall back failed. - return err + if dirResponse, queryErr = adHocPackage(cfg, driver, pattern, query); queryErr != nil { + return err // return the original error + } + } + // `go list` can report errors for files that are not listed as part of a package's GoFiles. + // In the case of an invalid Go file, we should assume that it is part of package if only + // one package is in the response. The file may have valid contents in an overlay. + if len(dirResponse.Packages) == 1 { + pkg := dirResponse.Packages[0] + for i, err := range pkg.Errors { + s := errorSpan(err) + if !s.IsValid() { + break + } + if len(pkg.CompiledGoFiles) == 0 { + break + } + dir := filepath.Dir(pkg.CompiledGoFiles[0]) + filename := filepath.Join(dir, filepath.Base(s.URI().Filename())) + if info, err := os.Stat(filename); err != nil || info.IsDir() { + break + } + if !contains(pkg.CompiledGoFiles, filename) { + pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, filename) + pkg.GoFiles = append(pkg.GoFiles, filename) + pkg.Errors = append(pkg.Errors[:i], pkg.Errors[i+1:]...) + } + } + } + // A final attempt to construct an ad-hoc package. + if len(dirResponse.Packages) == 1 && len(dirResponse.Packages[0].Errors) == 1 { + var queryErr error + if dirResponse, queryErr = adHocPackage(cfg, driver, pattern, query); queryErr != nil { + return err // return the original error } } isRoot := make(map[string]bool, len(dirResponse.Roots)) @@ -266,6 +350,74 @@ func runContainsQueries(cfg *Config, driver driver, response *responseDeduper, q return nil } +// adHocPackage attempts to construct an ad-hoc package given a query that failed. +func adHocPackage(cfg *Config, driver driver, pattern, query string) (*driverResponse, error) { + // There was an error loading the package. Try to load the file as an ad-hoc package. + // Usually the error will appear in a returned package, but may not if we're in modules mode + // and the ad-hoc is located outside a module. + dirResponse, err := driver(cfg, query) + if err != nil { + return nil, err + } + // If we get nothing back from `go list`, try to make this file into its own ad-hoc package. + if len(dirResponse.Packages) == 0 && err == nil { + dirResponse.Packages = append(dirResponse.Packages, &Package{ + ID: "command-line-arguments", + PkgPath: query, + GoFiles: []string{query}, + CompiledGoFiles: []string{query}, + Imports: make(map[string]*Package), + }) + dirResponse.Roots = append(dirResponse.Roots, "command-line-arguments") + } + // Special case to handle issue #33482: + // If this is a file= query for ad-hoc packages where the file only exists on an overlay, + // and exists outside of a module, add the file in for the package. + if len(dirResponse.Packages) == 1 && (dirResponse.Packages[0].ID == "command-line-arguments" || dirResponse.Packages[0].PkgPath == filepath.ToSlash(query)) { + if len(dirResponse.Packages[0].GoFiles) == 0 { + filename := filepath.Join(pattern, filepath.Base(query)) // avoid recomputing abspath + // TODO(matloob): check if the file is outside of a root dir? + for path := range cfg.Overlay { + if path == filename { + dirResponse.Packages[0].Errors = nil + dirResponse.Packages[0].GoFiles = []string{path} + dirResponse.Packages[0].CompiledGoFiles = []string{path} + } + } + } + } + return dirResponse, nil +} + +func contains(files []string, filename string) bool { + for _, f := range files { + if f == filename { + return true + } + } + return false +} + +// errorSpan attempts to parse a standard `go list` error message +// by stripping off the trailing error message. +// +// It works only on errors whose message is prefixed by colon, +// followed by a space (": "). For example: +// +// attributes.go:13:1: expected 'package', found 'type' +// +func errorSpan(err Error) span.Span { + if err.Pos == "" { + input := strings.TrimSpace(err.Msg) + msgIndex := strings.Index(input, ": ") + if msgIndex < 0 { + return span.Parse(input) + } + return span.Parse(input[:msgIndex]) + } + return span.Parse(err.Pos) +} + // modCacheRegexp splits a path in a module cache into module, module version, and package. var modCacheRegexp = regexp.MustCompile(`(.*)@([^/\\]*)(.*)`) @@ -316,9 +468,7 @@ func runNamedQueries(cfg *Config, driver driver, response *responseDeduper, quer startWalk := time.Now() gopathwalk.Walk(roots, add, gopathwalk.Options{ModulesEnabled: modRoot != "", Debug: debug}) - if debug { - log.Printf("%v for walk", time.Since(startWalk)) - } + cfg.Logf("%v for walk", time.Since(startWalk)) // Weird special case: the top-level package in a module will be in // whatever directory the user checked the repository out into. It's @@ -331,6 +481,10 @@ func runNamedQueries(cfg *Config, driver driver, response *responseDeduper, quer } files, err := ioutil.ReadDir(modRoot) + if err != nil { + panic(err) // See above. + } + for _, f := range files { if strings.HasSuffix(f.Name(), ".go") { simpleMatches = append(simpleMatches, rel) @@ -398,7 +552,7 @@ func runNamedQueries(cfg *Config, driver driver, response *responseDeduper, quer // We're only trying to look at stuff in the module cache, so // disable the network. This should speed things up, and has // prevented errors in at least one case, #28518. - tmpCfg.Env = append(append([]string{"GOPROXY=off"}, cfg.Env...)) + tmpCfg.Env = append([]string{"GOPROXY=off"}, cfg.Env...) var err error tmpCfg.Dir, err = ioutil.TempDir("", "gopackages-modquery") @@ -446,17 +600,29 @@ func roots(cfg *Config) ([]gopathwalk.Root, string, error) { var roots []gopathwalk.Root // Always add GOROOT. - roots = append(roots, gopathwalk.Root{filepath.Join(goroot, "/src"), gopathwalk.RootGOROOT}) + roots = append(roots, gopathwalk.Root{ + Path: filepath.Join(goroot, "/src"), + Type: gopathwalk.RootGOROOT, + }) // If modules are enabled, scan the module dir. if modDir != "" { - roots = append(roots, gopathwalk.Root{modDir, gopathwalk.RootCurrentModule}) + roots = append(roots, gopathwalk.Root{ + Path: modDir, + Type: gopathwalk.RootCurrentModule, + }) } // Add either GOPATH/src or GOPATH/pkg/mod, depending on module mode. for _, p := range gopath { if modDir != "" { - roots = append(roots, gopathwalk.Root{filepath.Join(p, "/pkg/mod"), gopathwalk.RootModuleCache}) + roots = append(roots, gopathwalk.Root{ + Path: filepath.Join(p, "/pkg/mod"), + Type: gopathwalk.RootModuleCache, + }) } else { - roots = append(roots, gopathwalk.Root{filepath.Join(p, "/src"), gopathwalk.RootGOPATH}) + roots = append(roots, gopathwalk.Root{ + Path: filepath.Join(p, "/src"), + Type: gopathwalk.RootGOPATH, + }) } } @@ -569,13 +735,13 @@ func otherFiles(p *jsonPackage) [][]string { // golistDriver uses the "go list" command to expand the pattern // words and return metadata for the specified packages. dir may be // "" and env may be nil, as per os/exec.Command. -func golistDriver(cfg *Config, words ...string) (*driverResponse, error) { +func golistDriver(cfg *Config, rootsDirs func() *goInfo, words ...string) (*driverResponse, error) { // go list uses the following identifiers in ImportPath and Imports: // // "p" -- importable package or main (command) - // "q.test" -- q's test executable + // "q.test" -- q's test executable // "p [q.test]" -- variant of p as built for q's test executable - // "q_test [q.test]" -- q's external test package + // "q_test [q.test]" -- q's external test package // // The packages p that are built differently for a test q.test // are q itself, plus any helpers used by the external test q_test, @@ -610,6 +776,20 @@ func golistDriver(cfg *Config, words ...string) (*driverResponse, error) { return nil, fmt.Errorf("package missing import path: %+v", p) } + // Work around https://golang.org/issue/33157: + // go list -e, when given an absolute path, will find the package contained at + // that directory. But when no package exists there, it will return a fake package + // with an error and the ImportPath set to the absolute path provided to go list. + // Try to convert that absolute path to what its package path would be if it's + // contained in a known module or GOPATH entry. This will allow the package to be + // properly "reclaimed" when overlays are processed. + if filepath.IsAbs(p.ImportPath) && p.Error != nil { + pkgPath, ok := getPkgPath(cfg, p.ImportPath, rootsDirs) + if ok { + p.ImportPath = pkgPath + } + } + if old, found := seen[p.ImportPath]; found { if !reflect.DeepEqual(p, old) { return nil, fmt.Errorf("internal error: go list gives conflicting information for package %v", p.ImportPath) @@ -713,6 +893,44 @@ func golistDriver(cfg *Config, words ...string) (*driverResponse, error) { return &response, nil } +// getPkgPath finds the package path of a directory if it's relative to a root directory. +func getPkgPath(cfg *Config, dir string, goInfo func() *goInfo) (string, bool) { + absDir, err := filepath.Abs(dir) + if err != nil { + cfg.Logf("error getting absolute path of %s: %v", dir, err) + return "", false + } + for rdir, rpath := range goInfo().rootDirs { + absRdir, err := filepath.Abs(rdir) + if err != nil { + cfg.Logf("error getting absolute path of %s: %v", rdir, err) + continue + } + // Make sure that the directory is in the module, + // to avoid creating a path relative to another module. + if !strings.HasPrefix(absDir, absRdir) { + cfg.Logf("%s does not have prefix %s", absDir, absRdir) + continue + } + // TODO(matloob): This doesn't properly handle symlinks. + r, err := filepath.Rel(rdir, dir) + if err != nil { + continue + } + if rpath != "" { + // We choose only one root even though the directory even it can belong in multiple modules + // or GOPATH entries. This is okay because we only need to work with absolute dirs when a + // file is missing from disk, for instance when gopls calls go/packages in an overlay. + // Once the file is saved, gopls, or the next invocation of the tool will get the correct + // result straight from golist. + // TODO(matloob): Implement module tiebreaking? + return path.Join(rpath, filepath.ToSlash(r)), true + } + return filepath.ToSlash(r), true + } + return "", false +} + // absJoin absolutizes and flattens the lists of files. func absJoin(dir string, fileses ...[]string) (res []string) { for _, files := range fileses { @@ -733,7 +951,7 @@ func golistargs(cfg *Config, words []string) []string { fmt.Sprintf("-compiled=%t", cfg.Mode&(NeedCompiledGoFiles|NeedSyntax|NeedTypesInfo|NeedTypesSizes) != 0), fmt.Sprintf("-test=%t", cfg.Tests), fmt.Sprintf("-export=%t", usesExportData(cfg)), - fmt.Sprintf("-deps=%t", cfg.Mode&NeedDeps != 0), + fmt.Sprintf("-deps=%t", cfg.Mode&NeedImports != 0), // go list doesn't let you pass -test and -find together, // probably because you'd just get the TestMain. fmt.Sprintf("-find=%t", !cfg.Tests && cfg.Mode&findFlags == 0), @@ -759,11 +977,9 @@ func invokeGo(cfg *Config, args ...string) (*bytes.Buffer, error) { cmd.Dir = cfg.Dir cmd.Stdout = stdout cmd.Stderr = stderr - if debug { - defer func(start time.Time) { - log.Printf("%s for %v, stderr: <<%s>>\n", time.Since(start), cmdDebugStr(cmd, args...), stderr) - }(time.Now()) - } + defer func(start time.Time) { + cfg.Logf("%s for %v, stderr: <<%s>> stdout: <<%s>>\n", time.Since(start), cmdDebugStr(cmd, args...), stderr, stdout) + }(time.Now()) if err := cmd.Run(); err != nil { // Check for 'go' executable not being found. @@ -783,6 +999,30 @@ func invokeGo(cfg *Config, args ...string) (*bytes.Buffer, error) { return nil, goTooOldError{fmt.Errorf("unsupported version of go: %s: %s", exitErr, stderr)} } + // Related to #24854 + if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "unexpected directory layout") { + return nil, fmt.Errorf("%s", stderr.String()) + } + + // Is there an error running the C compiler in cgo? This will be reported in the "Error" field + // and should be suppressed by go list -e. + // + // This condition is not perfect yet because the error message can include other error messages than runtime/cgo. + isPkgPathRune := func(r rune) bool { + // From https://golang.org/ref/spec#Import_declarations: + // Implementation restriction: A compiler may restrict ImportPaths to non-empty strings + // using only characters belonging to Unicode's L, M, N, P, and S general categories + // (the Graphic characters without spaces) and may also exclude the + // characters !"#$%&'()*,:;<=>?[\]^`{|} and the Unicode replacement character U+FFFD. + return unicode.IsOneOf([]*unicode.RangeTable{unicode.L, unicode.M, unicode.N, unicode.P, unicode.S}, r) && + !strings.ContainsRune("!\"#$%&'()*,:;<=>?[\\]^`{|}\uFFFD", r) + } + if len(stderr.String()) > 0 && strings.HasPrefix(stderr.String(), "# ") { + if strings.HasPrefix(strings.TrimLeftFunc(stderr.String()[len("# "):], isPkgPathRune), "\n") { + return stdout, nil + } + } + // This error only appears in stderr. See golang.org/cl/166398 for a fix in go list to show // the error in the Err section of stdout in case -e option is provided. // This fix is provided for backwards compatibility. @@ -792,17 +1032,58 @@ func invokeGo(cfg *Config, args ...string) (*bytes.Buffer, error) { return bytes.NewBufferString(output), nil } + // Similar to the previous error, but currently lacks a fix in Go. + if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "named files must all be in one directory") { + output := fmt.Sprintf(`{"ImportPath": "command-line-arguments","Incomplete": true,"Error": {"Pos": "","Err": %q}}`, + strings.Trim(stderr.String(), "\n")) + return bytes.NewBufferString(output), nil + } + + // Backwards compatibility for Go 1.11 because 1.12 and 1.13 put the directory in the ImportPath. + // If the package doesn't exist, put the absolute path of the directory into the error message, + // as Go 1.13 list does. + const noSuchDirectory = "no such directory" + if len(stderr.String()) > 0 && strings.Contains(stderr.String(), noSuchDirectory) { + errstr := stderr.String() + abspath := strings.TrimSpace(errstr[strings.Index(errstr, noSuchDirectory)+len(noSuchDirectory):]) + output := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`, + abspath, strings.Trim(stderr.String(), "\n")) + return bytes.NewBufferString(output), nil + } + // Workaround for #29280: go list -e has incorrect behavior when an ad-hoc package doesn't exist. + // Note that the error message we look for in this case is different that the one looked for above. if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "no such file or directory") { output := fmt.Sprintf(`{"ImportPath": "command-line-arguments","Incomplete": true,"Error": {"Pos": "","Err": %q}}`, strings.Trim(stderr.String(), "\n")) return bytes.NewBufferString(output), nil } + // Workaround for #34273. go list -e with GO111MODULE=on has incorrect behavior when listing a + // directory outside any module. + if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "outside available modules") { + output := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`, + // TODO(matloob): command-line-arguments isn't correct here. + "command-line-arguments", strings.Trim(stderr.String(), "\n")) + return bytes.NewBufferString(output), nil + } + + // Another variation of the previous error + if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "outside module root") { + output := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`, + // TODO(matloob): command-line-arguments isn't correct here. + "command-line-arguments", strings.Trim(stderr.String(), "\n")) + return bytes.NewBufferString(output), nil + } + // Workaround for an instance of golang.org/issue/26755: go list -e will return a non-zero exit // status if there's a dependency on a package that doesn't exist. But it should return // a zero exit status and set an error on that package. if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "no Go files in") { + // Don't clobber stdout if `go list` actually returned something. + if len(stdout.String()) > 0 { + return stdout, nil + } // try to extract package name from string stderrStr := stderr.String() var importPath string @@ -836,12 +1117,6 @@ func invokeGo(cfg *Config, args ...string) (*bytes.Buffer, error) { if len(stderr.Bytes()) != 0 && os.Getenv("GOPACKAGESPRINTGOLISTERRORS") != "" { fmt.Fprintf(os.Stderr, "%s stderr: <<%s>>\n", cmdDebugStr(cmd, args...), stderr) } - - // debugging - if false { - fmt.Fprintf(os.Stderr, "%s stdout: <<%s>>\n", cmdDebugStr(cmd, args...), stdout) - } - return stdout, nil } diff --git a/vendor/golang.org/x/tools/go/packages/golist_overlay.go b/vendor/golang.org/x/tools/go/packages/golist_overlay.go index ffc7a367f6..a7de62299d 100644 --- a/vendor/golang.org/x/tools/go/packages/golist_overlay.go +++ b/vendor/golang.org/x/tools/go/packages/golist_overlay.go @@ -6,11 +6,9 @@ import ( "fmt" "go/parser" "go/token" - "path" "path/filepath" "strconv" "strings" - "sync" ) // processGolistOverlay provides rudimentary support for adding @@ -18,7 +16,7 @@ import ( // sometimes incorrect. // TODO(matloob): Handle unsupported cases, including the following: // - determining the correct package to add given a new import path -func processGolistOverlay(cfg *Config, response *responseDeduper) (modifiedPkgs, needPkgs []string, err error) { +func processGolistOverlay(cfg *Config, response *responseDeduper, rootDirs func() *goInfo) (modifiedPkgs, needPkgs []string, err error) { havePkgs := make(map[string]string) // importPath -> non-test package ID needPkgsSet := make(map[string]bool) modifiedPkgsSet := make(map[string]bool) @@ -29,9 +27,6 @@ func processGolistOverlay(cfg *Config, response *responseDeduper) (modifiedPkgs, havePkgs[pkg.PkgPath] = pkg.ID } - var rootDirs map[string]string - var onceGetRootDirs sync.Once - // If no new imports are added, it is safe to avoid loading any needPkgs. // Otherwise, it's hard to tell which package is actually being loaded // (due to vendoring) and whether any modified package will show up @@ -42,10 +37,10 @@ func processGolistOverlay(cfg *Config, response *responseDeduper) (modifiedPkgs, for opath, contents := range cfg.Overlay { base := filepath.Base(opath) dir := filepath.Dir(opath) - var pkg *Package + var pkg *Package // if opath belongs to both a package and its test variant, this will be the test variant var testVariantOf *Package // if opath is a test file, this is the package it is testing var fileExists bool - isTest := strings.HasSuffix(opath, "_test.go") + isTestFile := strings.HasSuffix(opath, "_test.go") pkgName, ok := extractPackageName(opath, contents) if !ok { // Don't bother adding a file that doesn't even have a parsable package statement @@ -54,20 +49,33 @@ func processGolistOverlay(cfg *Config, response *responseDeduper) (modifiedPkgs, } nextPackage: for _, p := range response.dr.Packages { - if pkgName != p.Name { + if pkgName != p.Name && p.ID != "command-line-arguments" { continue } for _, f := range p.GoFiles { if !sameFile(filepath.Dir(f), dir) { continue } - if isTest && !hasTestFiles(p) { + // Make sure to capture information on the package's test variant, if needed. + if isTestFile && !hasTestFiles(p) { // TODO(matloob): Are there packages other than the 'production' variant // of a package that this can match? This shouldn't match the test main package // because the file is generated in another directory. testVariantOf = p continue nextPackage } + if pkg != nil && p != pkg && pkg.PkgPath == p.PkgPath { + // If we've already seen the test variant, + // make sure to label which package it is a test variant of. + if hasTestFiles(pkg) { + testVariantOf = p + continue nextPackage + } + // If we have already seen the package of which this is a test variant. + if hasTestFiles(p) { + testVariantOf = pkg + } + } pkg = p if filepath.Base(f) == base { fileExists = true @@ -76,37 +84,18 @@ func processGolistOverlay(cfg *Config, response *responseDeduper) (modifiedPkgs, } // The overlay could have included an entirely new package. if pkg == nil { - onceGetRootDirs.Do(func() { - rootDirs = determineRootDirs(cfg) - }) // Try to find the module or gopath dir the file is contained in. // Then for modules, add the module opath to the beginning. - var pkgPath string - for rdir, rpath := range rootDirs { - // TODO(matloob): This doesn't properly handle symlinks. - r, err := filepath.Rel(rdir, dir) - if err != nil { - continue - } - pkgPath = filepath.ToSlash(r) - if rpath != "" { - pkgPath = path.Join(rpath, pkgPath) - } - // We only create one new package even it can belong in multiple modules or GOPATH entries. - // This is okay because tools (such as the LSP) that use overlays will recompute the overlay - // once the file is saved, and golist will do the right thing. - // TODO(matloob): Implement module tiebreaking? + pkgPath, ok := getPkgPath(cfg, dir, rootDirs) + if !ok { break } - if pkgPath == "" { - continue - } isXTest := strings.HasSuffix(pkgName, "_test") if isXTest { pkgPath += "_test" } id := pkgPath - if isTest && !isXTest { + if isTestFile && !isXTest { id = fmt.Sprintf("%s [%s.test]", pkgPath, pkgPath) } // Try to reclaim a package with the same id if it exists in the response. @@ -122,7 +111,7 @@ func processGolistOverlay(cfg *Config, response *responseDeduper) (modifiedPkgs, response.addPackage(pkg) havePkgs[pkg.PkgPath] = id // Add the production package's sources for a test variant. - if isTest && !isXTest && testVariantOf != nil { + if isTestFile && !isXTest && testVariantOf != nil { pkg.GoFiles = append(pkg.GoFiles, testVariantOf.GoFiles...) pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, testVariantOf.CompiledGoFiles...) } @@ -145,12 +134,16 @@ func processGolistOverlay(cfg *Config, response *responseDeduper) (modifiedPkgs, if !found { overlayAddsImports = true // TODO(matloob): Handle cases when the following block isn't correct. - // These include imports of test variants, imports of vendored packages, etc. + // These include imports of vendored packages, etc. id, ok := havePkgs[imp] if !ok { id = imp } pkg.Imports[imp] = &Package{ID: id} + // Add dependencies to the non-test variant version of this package as wel. + if testVariantOf != nil { + testVariantOf.Imports[imp] = &Package{ID: id} + } } } continue diff --git a/vendor/golang.org/x/tools/go/packages/packages.go b/vendor/golang.org/x/tools/go/packages/packages.go index f20e444f4d..b29c91369e 100644 --- a/vendor/golang.org/x/tools/go/packages/packages.go +++ b/vendor/golang.org/x/tools/go/packages/packages.go @@ -48,8 +48,7 @@ const ( // "placeholder" Packages with only the ID set. NeedImports - // NeedDeps adds the fields requested by the LoadMode in the packages in Imports. If NeedImports - // is not set NeedDeps has no effect. + // NeedDeps adds the fields requested by the LoadMode in the packages in Imports. NeedDeps // NeedExportsFile adds ExportsFile. @@ -75,7 +74,7 @@ const ( // Deprecated: LoadImports exists for historical compatibility // and should not be used. Please directly specify the needed fields using the Need values. - LoadImports = LoadFiles | NeedImports | NeedDeps + LoadImports = LoadFiles | NeedImports // Deprecated: LoadTypes exists for historical compatibility // and should not be used. Please directly specify the needed fields using the Need values. @@ -87,7 +86,7 @@ const ( // Deprecated: LoadAllSyntax exists for historical compatibility // and should not be used. Please directly specify the needed fields using the Need values. - LoadAllSyntax = LoadSyntax + LoadAllSyntax = LoadSyntax | NeedDeps ) // A Config specifies details about how packages should be loaded. @@ -103,6 +102,12 @@ type Config struct { // If Context is nil, the load cannot be cancelled. Context context.Context + // Logf is the logger for the config. + // If the user provides a logger, debug logging is enabled. + // If the GOPACKAGESDEBUG environment variable is set to true, + // but the logger is nil, default to log.Printf. + Logf func(format string, args ...interface{}) + // Dir is the directory in which to run the build system's query tool // that provides information about the packages. // If Dir is empty, the tool is run in the current directory. @@ -410,11 +415,13 @@ type loader struct { parseCacheMu sync.Mutex exportMu sync.Mutex // enforces mutual exclusion of exportdata operations - // TODO(matloob): Add an implied mode here and use that instead of mode. - // Implied mode would contain all the fields we need the data for so we can - // get the actually requested fields. We'll zero them out before returning - // packages to the user. This will make it easier for us to get the conditions - // where we need certain modes right. + // Config.Mode contains the implied mode (see impliedLoadMode). + // Implied mode contains all the fields we need the data for. + // In requestedMode there are the actually requested fields. + // We'll zero them out before returning packages to the user. + // This makes it easier for us to get the conditions where + // we need certain modes right. + requestedMode LoadMode } type parseValue struct { @@ -429,6 +436,17 @@ func newLoader(cfg *Config) *loader { } if cfg != nil { ld.Config = *cfg + // If the user has provided a logger, use it. + ld.Config.Logf = cfg.Logf + } + if ld.Config.Logf == nil { + // If the GOPACKAGESDEBUG environment variable is set to true, + // but the user has not provided a logger, default to log.Printf. + if debug { + ld.Config.Logf = log.Printf + } else { + ld.Config.Logf = func(format string, args ...interface{}) {} + } } if ld.Config.Mode == 0 { ld.Config.Mode = NeedName | NeedFiles | NeedCompiledGoFiles // Preserve zero behavior of Mode for backwards compatibility. @@ -445,6 +463,10 @@ func newLoader(cfg *Config) *loader { } } + // Save the actually requested fields. We'll zero them out before returning packages to the user. + ld.requestedMode = ld.Mode + ld.Mode = impliedLoadMode(ld.Mode) + if ld.Mode&NeedTypes != 0 { if ld.Fset == nil { ld.Fset = token.NewFileSet() @@ -459,6 +481,7 @@ func newLoader(cfg *Config) *loader { } } } + return ld } @@ -479,8 +502,8 @@ func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) { } lpkg := &loaderPackage{ Package: pkg, - needtypes: (ld.Mode&(NeedTypes|NeedTypesInfo) != 0 && rootIndex < 0) || rootIndex >= 0, - needsrc: (ld.Mode&(NeedSyntax|NeedTypesInfo) != 0 && rootIndex < 0) || rootIndex >= 0 || + needtypes: (ld.Mode&(NeedTypes|NeedTypesInfo) != 0 && ld.Mode&NeedDeps != 0 && rootIndex < 0) || rootIndex >= 0, + needsrc: (ld.Mode&(NeedSyntax|NeedTypesInfo) != 0 && ld.Mode&NeedDeps != 0 && rootIndex < 0) || rootIndex >= 0 || len(ld.Overlay) > 0 || // Overlays can invalidate export data. TODO(matloob): make this check fine-grained based on dependencies on overlaid files pkg.ExportFile == "" && pkg.PkgPath != "unsafe", } @@ -528,8 +551,7 @@ func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) { stack = append(stack, lpkg) // push stubs := lpkg.Imports // the structure form has only stubs with the ID in the Imports // If NeedImports isn't set, the imports fields will all be zeroed out. - // If NeedDeps isn't also set we want to keep the stubs. - if ld.Mode&NeedImports != 0 && ld.Mode&NeedDeps != 0 { + if ld.Mode&NeedImports != 0 { lpkg.Imports = make(map[string]*Package, len(stubs)) for importPath, ipkg := range stubs { var importErr error @@ -566,7 +588,7 @@ func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) { return lpkg.needsrc } - if ld.Mode&(NeedImports|NeedDeps) == 0 { + if ld.Mode&NeedImports == 0 { // We do this to drop the stub import packages that we are not even going to try to resolve. for _, lpkg := range initial { lpkg.Imports = nil @@ -577,7 +599,7 @@ func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) { visit(lpkg) } } - if ld.Mode&NeedDeps != 0 { // TODO(matloob): This is only the case if NeedTypes is also set, right? + if ld.Mode&NeedImports != 0 && ld.Mode&NeedTypes != 0 { for _, lpkg := range srcPkgs { // Complete type information is required for the // immediate dependencies of each source package. @@ -602,54 +624,44 @@ func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) { } result := make([]*Package, len(initial)) - importPlaceholders := make(map[string]*Package) for i, lpkg := range initial { result[i] = lpkg.Package } for i := range ld.pkgs { // Clear all unrequested fields, for extra de-Hyrum-ization. - if ld.Mode&NeedName == 0 { + if ld.requestedMode&NeedName == 0 { ld.pkgs[i].Name = "" ld.pkgs[i].PkgPath = "" } - if ld.Mode&NeedFiles == 0 { + if ld.requestedMode&NeedFiles == 0 { ld.pkgs[i].GoFiles = nil ld.pkgs[i].OtherFiles = nil } - if ld.Mode&NeedCompiledGoFiles == 0 { + if ld.requestedMode&NeedCompiledGoFiles == 0 { ld.pkgs[i].CompiledGoFiles = nil } - if ld.Mode&NeedImports == 0 { + if ld.requestedMode&NeedImports == 0 { ld.pkgs[i].Imports = nil } - if ld.Mode&NeedExportsFile == 0 { + if ld.requestedMode&NeedExportsFile == 0 { ld.pkgs[i].ExportFile = "" } - if ld.Mode&NeedTypes == 0 { + if ld.requestedMode&NeedTypes == 0 { ld.pkgs[i].Types = nil ld.pkgs[i].Fset = nil ld.pkgs[i].IllTyped = false } - if ld.Mode&NeedSyntax == 0 { + if ld.requestedMode&NeedSyntax == 0 { ld.pkgs[i].Syntax = nil } - if ld.Mode&NeedTypesInfo == 0 { + if ld.requestedMode&NeedTypesInfo == 0 { ld.pkgs[i].TypesInfo = nil } - if ld.Mode&NeedTypesSizes == 0 { + if ld.requestedMode&NeedTypesSizes == 0 { ld.pkgs[i].TypesSizes = nil } - if ld.Mode&NeedDeps == 0 { - for j, pkg := range ld.pkgs[i].Imports { - ph, ok := importPlaceholders[pkg.ID] - if !ok { - ph = &Package{ID: pkg.ID} - importPlaceholders[pkg.ID] = ph - } - ld.pkgs[i].Imports[j] = ph - } - } } + return result, nil } @@ -670,7 +682,6 @@ func (ld *loader) loadRecursive(lpkg *loaderPackage) { }(imp) } wg.Wait() - ld.loadPackage(lpkg) }) } @@ -759,6 +770,14 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) { lpkg.Errors = append(lpkg.Errors, errs...) } + if len(lpkg.CompiledGoFiles) == 0 && lpkg.ExportFile != "" { + // The config requested loading sources and types, but sources are missing. + // Add an error to the package and fall back to loading from export data. + appendError(Error{"-", fmt.Sprintf("sources missing for package %s", lpkg.ID), ParseError}) + ld.loadFromExportData(lpkg) + return // can't get syntax trees for this package + } + files, errs := ld.parseFiles(lpkg.CompiledGoFiles) for _, err := range errs { appendError(err) @@ -797,7 +816,7 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) { if ipkg.Types != nil && ipkg.Types.Complete() { return ipkg.Types, nil } - log.Fatalf("internal error: nil Pkg importing %q from %q", path, lpkg) + log.Fatalf("internal error: package %q without types was imported from %q", path, lpkg) panic("unreachable") }) @@ -808,7 +827,7 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) { // Type-check bodies of functions only in non-initial packages. // Example: for import graph A->B->C and initial packages {A,C}, // we can ignore function bodies in B. - IgnoreFuncBodies: (ld.Mode&(NeedDeps|NeedTypesInfo) == 0) && !lpkg.initial, + IgnoreFuncBodies: ld.Mode&NeedDeps == 0 && !lpkg.initial, Error: appendError, Sizes: ld.sizes, @@ -1070,6 +1089,25 @@ func (ld *loader) loadFromExportData(lpkg *loaderPackage) (*types.Package, error return tpkg, nil } +// impliedLoadMode returns loadMode with its dependencies. +func impliedLoadMode(loadMode LoadMode) LoadMode { + if loadMode&NeedTypesInfo != 0 && loadMode&NeedImports == 0 { + // If NeedTypesInfo, go/packages needs to do typechecking itself so it can + // associate type info with the AST. To do so, we need the export data + // for dependencies, which means we need to ask for the direct dependencies. + // NeedImports is used to ask for the direct dependencies. + loadMode |= NeedImports + } + + if loadMode&NeedDeps != 0 && loadMode&NeedImports == 0 { + // With NeedDeps we need to load at least direct dependencies. + // NeedImports is used to ask for the direct dependencies. + loadMode |= NeedImports + } + + return loadMode +} + func usesExportData(cfg *Config) bool { - return cfg.Mode&NeedExportsFile != 0 || cfg.Mode&NeedTypes != 0 && cfg.Mode&NeedTypesInfo == 0 + return cfg.Mode&NeedExportsFile != 0 || cfg.Mode&NeedTypes != 0 && cfg.Mode&NeedDeps == 0 } diff --git a/vendor/golang.org/x/tools/internal/gopathwalk/walk.go b/vendor/golang.org/x/tools/internal/gopathwalk/walk.go index 04bb96a362..9a61bdbf5d 100644 --- a/vendor/golang.org/x/tools/internal/gopathwalk/walk.go +++ b/vendor/golang.org/x/tools/internal/gopathwalk/walk.go @@ -16,6 +16,7 @@ import ( "os" "path/filepath" "strings" + "time" "golang.org/x/tools/internal/fastwalk" ) @@ -59,24 +60,38 @@ func SrcDirsRoots(ctx *build.Context) []Root { // paths of the containing source directory and the package directory. // add will be called concurrently. func Walk(roots []Root, add func(root Root, dir string), opts Options) { + WalkSkip(roots, add, func(Root, string) bool { return false }, opts) +} + +// WalkSkip walks Go source directories ($GOROOT, $GOPATH, etc) to find packages. +// For each package found, add will be called (concurrently) with the absolute +// paths of the containing source directory and the package directory. +// For each directory that will be scanned, skip will be called (concurrently) +// with the absolute paths of the containing source directory and the directory. +// If skip returns false on a directory it will be processed. +// add will be called concurrently. +// skip will be called concurrently. +func WalkSkip(roots []Root, add func(root Root, dir string), skip func(root Root, dir string) bool, opts Options) { for _, root := range roots { - walkDir(root, add, opts) + walkDir(root, add, skip, opts) } } -func walkDir(root Root, add func(Root, string), opts Options) { +func walkDir(root Root, add func(Root, string), skip func(root Root, dir string) bool, opts Options) { if _, err := os.Stat(root.Path); os.IsNotExist(err) { if opts.Debug { - log.Printf("skipping nonexistant directory: %v", root.Path) + log.Printf("skipping nonexistent directory: %v", root.Path) } return } + start := time.Now() if opts.Debug { - log.Printf("scanning %s", root.Path) + log.Printf("gopathwalk: scanning %s", root.Path) } w := &walker{ root: root, add: add, + skip: skip, opts: opts, } w.init() @@ -85,15 +100,16 @@ func walkDir(root Root, add func(Root, string), opts Options) { } if opts.Debug { - log.Printf("scanned %s", root.Path) + log.Printf("gopathwalk: scanned %s in %v", root.Path, time.Since(start)) } } // walker is the callback for fastwalk.Walk. type walker struct { - root Root // The source directory to scan. - add func(Root, string) // The callback that will be invoked for every possible Go package dir. - opts Options // Options passed to Walk by the user. + root Root // The source directory to scan. + add func(Root, string) // The callback that will be invoked for every possible Go package dir. + skip func(Root, string) bool // The callback that will be invoked for every dir. dir is skipped if it returns true. + opts Options // Options passed to Walk by the user. ignoredDirs []os.FileInfo // The ignored directories, loaded from .goimportsignore files. } @@ -151,12 +167,16 @@ func (w *walker) getIgnoredDirs(path string) []string { return ignoredDirs } -func (w *walker) shouldSkipDir(fi os.FileInfo) bool { +func (w *walker) shouldSkipDir(fi os.FileInfo, dir string) bool { for _, ignoredDir := range w.ignoredDirs { if os.SameFile(fi, ignoredDir) { return true } } + if w.skip != nil { + // Check with the user specified callback. + return w.skip(w.root, dir) + } return false } @@ -184,7 +204,7 @@ func (w *walker) walk(path string, typ os.FileMode) error { return filepath.SkipDir } fi, err := os.Lstat(path) - if err == nil && w.shouldSkipDir(fi) { + if err == nil && w.shouldSkipDir(fi, path) { return filepath.SkipDir } return nil @@ -224,7 +244,7 @@ func (w *walker) shouldTraverse(dir string, fi os.FileInfo) bool { if !ts.IsDir() { return false } - if w.shouldSkipDir(ts) { + if w.shouldSkipDir(ts, dir) { return false } // Check for symlink loops by statting each directory component diff --git a/vendor/golang.org/x/tools/internal/imports/fix.go b/vendor/golang.org/x/tools/internal/imports/fix.go index 2c58d18bec..3e6e56df1e 100644 --- a/vendor/golang.org/x/tools/internal/imports/fix.go +++ b/vendor/golang.org/x/tools/internal/imports/fix.go @@ -17,6 +17,7 @@ import ( "os/exec" "path" "path/filepath" + "reflect" "sort" "strconv" "strings" @@ -67,23 +68,27 @@ func importGroup(env *ProcessEnv, importPath string) int { return 0 } -type importFixType int +type ImportFixType int const ( - addImport importFixType = iota - deleteImport - setImportName + AddImport ImportFixType = iota + DeleteImport + SetImportName ) -type importFix struct { - info importInfo - fixType importFixType +type ImportFix struct { + // StmtInfo represents the import statement this fix will add, remove, or change. + StmtInfo ImportInfo + // IdentName is the identifier that this fix will add or remove. + IdentName string + // FixType is the type of fix this is (AddImport, DeleteImport, SetImportName). + FixType ImportFixType } -// An importInfo represents a single import statement. -type importInfo struct { - importPath string // import path, e.g. "crypto/rand". - name string // import name, e.g. "crand", or "" if none. +// An ImportInfo represents a single import statement. +type ImportInfo struct { + ImportPath string // import path, e.g. "crypto/rand". + Name string // import name, e.g. "crand", or "" if none. } // A packageInfo represents what's known about a package. @@ -181,10 +186,10 @@ func collectReferences(f *ast.File) references { return refs } -// collectImports returns all the imports in f, keyed by their package name as -// determined by pathToName. Unnamed imports (., _) and "C" are ignored. -func collectImports(f *ast.File) []*importInfo { - var imports []*importInfo +// collectImports returns all the imports in f. +// Unnamed imports (., _) and "C" are ignored. +func collectImports(f *ast.File) []*ImportInfo { + var imports []*ImportInfo for _, imp := range f.Imports { var name string if imp.Name != nil { @@ -194,9 +199,9 @@ func collectImports(f *ast.File) []*importInfo { continue } path := strings.Trim(imp.Path.Value, `"`) - imports = append(imports, &importInfo{ - name: name, - importPath: path, + imports = append(imports, &ImportInfo{ + Name: name, + ImportPath: path, }) } return imports @@ -204,9 +209,9 @@ func collectImports(f *ast.File) []*importInfo { // findMissingImport searches pass's candidates for an import that provides // pkg, containing all of syms. -func (p *pass) findMissingImport(pkg string, syms map[string]bool) *importInfo { +func (p *pass) findMissingImport(pkg string, syms map[string]bool) *ImportInfo { for _, candidate := range p.candidates { - pkgInfo, ok := p.knownPackages[candidate.importPath] + pkgInfo, ok := p.knownPackages[candidate.ImportPath] if !ok { continue } @@ -246,18 +251,18 @@ type pass struct { otherFiles []*ast.File // sibling files. // Intermediate state, generated by load. - existingImports map[string]*importInfo + existingImports map[string]*ImportInfo allRefs references missingRefs references // Inputs to fix. These can be augmented between successive fix calls. lastTry bool // indicates that this is the last call and fix should clean up as best it can. - candidates []*importInfo // candidate imports in priority order. + candidates []*ImportInfo // candidate imports in priority order. knownPackages map[string]*packageInfo // information about all known packages. } // loadPackageNames saves the package names for everything referenced by imports. -func (p *pass) loadPackageNames(imports []*importInfo) error { +func (p *pass) loadPackageNames(imports []*ImportInfo) error { if p.env.Debug { p.env.Logf("loading package names for %v packages", len(imports)) defer func() { @@ -266,13 +271,13 @@ func (p *pass) loadPackageNames(imports []*importInfo) error { } var unknown []string for _, imp := range imports { - if _, ok := p.knownPackages[imp.importPath]; ok { + if _, ok := p.knownPackages[imp.ImportPath]; ok { continue } - unknown = append(unknown, imp.importPath) + unknown = append(unknown, imp.ImportPath) } - names, err := p.env.getResolver().loadPackageNames(unknown, p.srcDir) + names, err := p.env.GetResolver().loadPackageNames(unknown, p.srcDir) if err != nil { return err } @@ -289,24 +294,24 @@ func (p *pass) loadPackageNames(imports []*importInfo) error { // importIdentifier returns the identifier that imp will introduce. It will // guess if the package name has not been loaded, e.g. because the source // is not available. -func (p *pass) importIdentifier(imp *importInfo) string { - if imp.name != "" { - return imp.name +func (p *pass) importIdentifier(imp *ImportInfo) string { + if imp.Name != "" { + return imp.Name } - known := p.knownPackages[imp.importPath] + known := p.knownPackages[imp.ImportPath] if known != nil && known.name != "" { return known.name } - return importPathToAssumedName(imp.importPath) + return importPathToAssumedName(imp.ImportPath) } // load reads in everything necessary to run a pass, and reports whether the // file already has all the imports it needs. It fills in p.missingRefs with the // file's missing symbols, if any, or removes unused imports if not. -func (p *pass) load() ([]*importFix, bool) { +func (p *pass) load() ([]*ImportFix, bool) { p.knownPackages = map[string]*packageInfo{} p.missingRefs = references{} - p.existingImports = map[string]*importInfo{} + p.existingImports = map[string]*ImportInfo{} // Load basic information about the file in question. p.allRefs = collectReferences(p.f) @@ -361,9 +366,9 @@ func (p *pass) load() ([]*importFix, bool) { // fix attempts to satisfy missing imports using p.candidates. If it finds // everything, or if p.lastTry is true, it updates fixes to add the imports it found, // delete anything unused, and update import names, and returns true. -func (p *pass) fix() ([]*importFix, bool) { +func (p *pass) fix() ([]*ImportFix, bool) { // Find missing imports. - var selected []*importInfo + var selected []*ImportInfo for left, rights := range p.missingRefs { if imp := p.findMissingImport(left, rights); imp != nil { selected = append(selected, imp) @@ -375,7 +380,7 @@ func (p *pass) fix() ([]*importFix, bool) { } // Found everything, or giving up. Add the new imports and remove any unused. - var fixes []*importFix + var fixes []*ImportFix for _, imp := range p.existingImports { // We deliberately ignore globals here, because we can't be sure // they're in the same package. People do things like put multiple @@ -383,32 +388,35 @@ func (p *pass) fix() ([]*importFix, bool) { // remove imports if they happen to have the same name as a var in // a different package. if _, ok := p.allRefs[p.importIdentifier(imp)]; !ok { - fixes = append(fixes, &importFix{ - info: *imp, - fixType: deleteImport, + fixes = append(fixes, &ImportFix{ + StmtInfo: *imp, + IdentName: p.importIdentifier(imp), + FixType: DeleteImport, }) continue } // An existing import may need to update its import name to be correct. - if name := p.importSpecName(imp); name != imp.name { - fixes = append(fixes, &importFix{ - info: importInfo{ - name: name, - importPath: imp.importPath, + if name := p.importSpecName(imp); name != imp.Name { + fixes = append(fixes, &ImportFix{ + StmtInfo: ImportInfo{ + Name: name, + ImportPath: imp.ImportPath, }, - fixType: setImportName, + IdentName: p.importIdentifier(imp), + FixType: SetImportName, }) } } for _, imp := range selected { - fixes = append(fixes, &importFix{ - info: importInfo{ - name: p.importSpecName(imp), - importPath: imp.importPath, + fixes = append(fixes, &ImportFix{ + StmtInfo: ImportInfo{ + Name: p.importSpecName(imp), + ImportPath: imp.ImportPath, }, - fixType: addImport, + IdentName: p.importIdentifier(imp), + FixType: AddImport, }) } @@ -419,42 +427,41 @@ func (p *pass) fix() ([]*importFix, bool) { // // When the import identifier matches the assumed import name, the import name does // not appear in the import spec. -func (p *pass) importSpecName(imp *importInfo) string { +func (p *pass) importSpecName(imp *ImportInfo) string { // If we did not load the real package names, or the name is already set, // we just return the existing name. - if !p.loadRealPackageNames || imp.name != "" { - return imp.name + if !p.loadRealPackageNames || imp.Name != "" { + return imp.Name } ident := p.importIdentifier(imp) - if ident == importPathToAssumedName(imp.importPath) { + if ident == importPathToAssumedName(imp.ImportPath) { return "" // ident not needed since the assumed and real names are the same. } return ident } // apply will perform the fixes on f in order. -func apply(fset *token.FileSet, f *ast.File, fixes []*importFix) bool { +func apply(fset *token.FileSet, f *ast.File, fixes []*ImportFix) { for _, fix := range fixes { - switch fix.fixType { - case deleteImport: - astutil.DeleteNamedImport(fset, f, fix.info.name, fix.info.importPath) - case addImport: - astutil.AddNamedImport(fset, f, fix.info.name, fix.info.importPath) - case setImportName: + switch fix.FixType { + case DeleteImport: + astutil.DeleteNamedImport(fset, f, fix.StmtInfo.Name, fix.StmtInfo.ImportPath) + case AddImport: + astutil.AddNamedImport(fset, f, fix.StmtInfo.Name, fix.StmtInfo.ImportPath) + case SetImportName: // Find the matching import path and change the name. for _, spec := range f.Imports { - path := strings.Trim(spec.Path.Value, `""`) - if path == fix.info.importPath { + path := strings.Trim(spec.Path.Value, `"`) + if path == fix.StmtInfo.ImportPath { spec.Name = &ast.Ident{ - Name: fix.info.name, + Name: fix.StmtInfo.Name, NamePos: spec.Pos(), } } } } } - return true } // assumeSiblingImportsValid assumes that siblings' use of packages is valid, @@ -463,15 +470,15 @@ func (p *pass) assumeSiblingImportsValid() { for _, f := range p.otherFiles { refs := collectReferences(f) imports := collectImports(f) - importsByName := map[string]*importInfo{} + importsByName := map[string]*ImportInfo{} for _, imp := range imports { importsByName[p.importIdentifier(imp)] = imp } for left, rights := range refs { if imp, ok := importsByName[left]; ok { - if _, ok := stdlib[imp.importPath]; ok { + if m, ok := stdlib[imp.ImportPath]; ok { // We have the stdlib in memory; no need to guess. - rights = stdlib[imp.importPath] + rights = copyExports(m) } p.addCandidate(imp, &packageInfo{ // no name; we already know it. @@ -484,9 +491,9 @@ func (p *pass) assumeSiblingImportsValid() { // addCandidate adds a candidate import to p, and merges in the information // in pkg. -func (p *pass) addCandidate(imp *importInfo, pkg *packageInfo) { +func (p *pass) addCandidate(imp *ImportInfo, pkg *packageInfo) { p.candidates = append(p.candidates, imp) - if existing, ok := p.knownPackages[imp.importPath]; ok { + if existing, ok := p.knownPackages[imp.ImportPath]; ok { if existing.name == "" { existing.name = pkg.name } @@ -494,7 +501,7 @@ func (p *pass) addCandidate(imp *importInfo, pkg *packageInfo) { existing.exports[export] = true } } else { - p.knownPackages[imp.importPath] = pkg + p.knownPackages[imp.ImportPath] = pkg } } @@ -514,9 +521,9 @@ func fixImportsDefault(fset *token.FileSet, f *ast.File, filename string, env *P return err } -// getFixes gets the getFixes that need to be made to f in order to fix the imports. +// getFixes gets the import fixes that need to be made to f in order to fix the imports. // It does not modify the ast. -func getFixes(fset *token.FileSet, f *ast.File, filename string, env *ProcessEnv) ([]*importFix, error) { +func getFixes(fset *token.FileSet, f *ast.File, filename string, env *ProcessEnv) ([]*ImportFix, error) { abs, err := filepath.Abs(filename) if err != nil { return nil, err @@ -578,6 +585,54 @@ func getFixes(fset *token.FileSet, f *ast.File, filename string, env *ProcessEnv return fixes, nil } +// getAllCandidates gets all of the candidates to be imported, regardless of if they are needed. +func getAllCandidates(filename string, env *ProcessEnv) ([]ImportFix, error) { + // TODO(heschi): filter out current package. (Don't forget x_test can import x.) + + // Exclude goroot results -- getting them is relatively expensive, not cached, + // and generally redundant with the in-memory version. + exclude := []gopathwalk.RootType{gopathwalk.RootGOROOT} + // Only the go/packages resolver uses the first argument, and nobody uses that resolver. + pkgs, err := env.GetResolver().scan(nil, true, exclude) + if err != nil { + return nil, err + } + + // Start off with the standard library. + var imports []ImportFix + for importPath := range stdlib { + imports = append(imports, ImportFix{ + StmtInfo: ImportInfo{ + ImportPath: importPath, + }, + IdentName: path.Base(importPath), + FixType: AddImport, + }) + } + + dupCheck := map[string]struct{}{} + for _, pkg := range pkgs { + if !canUse(filename, pkg.dir) { + continue + } + if _, ok := dupCheck[pkg.importPathShort]; ok { + continue + } + dupCheck[pkg.importPathShort] = struct{}{} + imports = append(imports, ImportFix{ + StmtInfo: ImportInfo{ + ImportPath: pkg.importPathShort, + }, + IdentName: pkg.packageName, + FixType: AddImport, + }) + } + sort.Slice(imports, func(i int, j int) bool { + return imports[i].StmtInfo.ImportPath < imports[j].StmtInfo.ImportPath + }) + return imports, nil +} + // ProcessEnv contains environment variables and settings that affect the use of // the go command, the go/build package, etc. type ProcessEnv struct { @@ -595,7 +650,7 @@ type ProcessEnv struct { // Logf is the default logger for the ProcessEnv. Logf func(format string, args ...interface{}) - resolver resolver + resolver Resolver } func (e *ProcessEnv) env() []string { @@ -617,7 +672,7 @@ func (e *ProcessEnv) env() []string { return env } -func (e *ProcessEnv) getResolver() resolver { +func (e *ProcessEnv) GetResolver() Resolver { if e.resolver != nil { return e.resolver } @@ -631,7 +686,7 @@ func (e *ProcessEnv) getResolver() resolver { e.resolver = &gopathResolver{env: e} return e.resolver } - e.resolver = &moduleResolver{env: e} + e.resolver = &ModuleResolver{env: e} return e.resolver } @@ -647,6 +702,13 @@ func (e *ProcessEnv) buildContext() *build.Context { ctx := build.Default ctx.GOROOT = e.GOROOT ctx.GOPATH = e.GOPATH + + // As of Go 1.14, build.Context has a WorkingDir field + // (see golang.org/issue/34860). + // Populate it only if present. + if wd := reflect.ValueOf(&ctx).Elem().FieldByName("WorkingDir"); wd.IsValid() && wd.Kind() == reflect.String { + wd.SetString(e.WorkingDir) + } return &ctx } @@ -681,9 +743,10 @@ func cmdDebugStr(cmd *exec.Cmd) string { func addStdlibCandidates(pass *pass, refs references) { add := func(pkg string) { + exports := copyExports(stdlib[pkg]) pass.addCandidate( - &importInfo{importPath: pkg}, - &packageInfo{name: path.Base(pkg), exports: stdlib[pkg]}) + &ImportInfo{ImportPath: pkg}, + &packageInfo{name: path.Base(pkg), exports: exports}) } for left := range refs { if left == "rand" { @@ -700,20 +763,29 @@ func addStdlibCandidates(pass *pass, refs references) { } } -// A resolver does the build-system-specific parts of goimports. -type resolver interface { +// A Resolver does the build-system-specific parts of goimports. +type Resolver interface { // loadPackageNames loads the package names in importPaths. loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) - // scan finds (at least) the packages satisfying refs. The returned slice is unordered. - scan(refs references) ([]*pkg, error) + // scan finds (at least) the packages satisfying refs. If loadNames is true, + // package names will be set on the results, and dirs whose package name + // could not be determined will be excluded. + scan(refs references, loadNames bool, exclude []gopathwalk.RootType) ([]*pkg, error) + // loadExports returns the set of exported symbols in the package at dir. + // It returns an error if the package name in dir does not match expectPackage. + // loadExports may be called concurrently. + loadExports(ctx context.Context, expectPackage string, pkg *pkg) (map[string]bool, error) } -// gopathResolver implements resolver for GOPATH and module workspaces using go/packages. +// gopackagesResolver implements resolver for GOPATH and module workspaces using go/packages. type goPackagesResolver struct { env *ProcessEnv } func (r *goPackagesResolver) loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) { + if len(importPaths) == 0 { + return nil, nil + } cfg := r.env.newPackagesConfig(packages.LoadFiles) pkgs, err := packages.Load(cfg, importPaths...) if err != nil { @@ -734,7 +806,7 @@ func (r *goPackagesResolver) loadPackageNames(importPaths []string, srcDir strin } -func (r *goPackagesResolver) scan(refs references) ([]*pkg, error) { +func (r *goPackagesResolver) scan(refs references, _ bool, _ []gopathwalk.RootType) ([]*pkg, error) { var loadQueries []string for pkgName := range refs { loadQueries = append(loadQueries, "iamashamedtousethedisabledqueryname="+pkgName) @@ -752,20 +824,41 @@ func (r *goPackagesResolver) scan(refs references) ([]*pkg, error) { dir: filepath.Dir(goPackage.CompiledGoFiles[0]), importPathShort: VendorlessPath(goPackage.PkgPath), goPackage: goPackage, + packageName: goPackage.Name, }) } return scan, nil } +func (r *goPackagesResolver) loadExports(ctx context.Context, expectPackage string, pkg *pkg) (map[string]bool, error) { + if pkg.goPackage == nil { + return nil, fmt.Errorf("goPackage not set") + } + exports := map[string]bool{} + fset := token.NewFileSet() + for _, fname := range pkg.goPackage.CompiledGoFiles { + f, err := parser.ParseFile(fset, fname, nil, 0) + if err != nil { + return nil, fmt.Errorf("parsing %s: %v", fname, err) + } + for name := range f.Scope.Objects { + if ast.IsExported(name) { + exports[name] = true + } + } + } + return exports, nil +} + func addExternalCandidates(pass *pass, refs references, filename string) error { - dirScan, err := pass.env.getResolver().scan(refs) + dirScan, err := pass.env.GetResolver().scan(refs, false, nil) if err != nil { return err } // Search for imports matching potential package references. type result struct { - imp *importInfo + imp *ImportInfo pkg *packageInfo } results := make(chan result, len(refs)) @@ -799,8 +892,8 @@ func addExternalCandidates(pass *pass, refs references, filename string) error { return // No matching package. } - imp := &importInfo{ - importPath: found.importPathShort, + imp := &ImportInfo{ + ImportPath: found.importPathShort, } pkg := &packageInfo{ @@ -867,7 +960,7 @@ func (r *gopathResolver) loadPackageNames(importPaths []string, srcDir string) ( return names, nil } -// importPathToNameGoPath finds out the actual package name, as declared in its .go files. +// importPathToName finds out the actual package name, as declared in its .go files. // If there's a problem, it returns "". func importPathToName(env *ProcessEnv, importPath, srcDir string) (packageName string) { // Fast path for standard library without going to disk. @@ -887,8 +980,8 @@ func importPathToName(env *ProcessEnv, importPath, srcDir string) (packageName s } // packageDirToName is a faster version of build.Import if -// the only thing desired is the package name. It uses build.FindOnly -// to find the directory and then only parses one file in the package, +// the only thing desired is the package name. Given a directory, +// packageDirToName then only parses one file in the package, // trusting that the files in the directory are consistent. func packageDirToName(dir string) (packageName string, err error) { d, err := os.Open(dir) @@ -942,6 +1035,7 @@ type pkg struct { goPackage *packages.Package dir string // absolute file path to pkg directory ("/usr/lib/go/src/net/http") importPathShort string // vendorless import path ("net/http", "a/b") + packageName string // package name loaded from source if requested } type pkgDistance struct { @@ -985,7 +1079,7 @@ func distance(basepath, targetpath string) int { return strings.Count(p, string(filepath.Separator)) + 1 } -func (r *gopathResolver) scan(_ references) ([]*pkg, error) { +func (r *gopathResolver) scan(_ references, loadNames bool, exclude []gopathwalk.RootType) ([]*pkg, error) { dupCheck := make(map[string]bool) var result []*pkg @@ -1000,15 +1094,42 @@ func (r *gopathResolver) scan(_ references) ([]*pkg, error) { } dupCheck[dir] = true importpath := filepath.ToSlash(dir[len(root.Path)+len("/"):]) - result = append(result, &pkg{ + p := &pkg{ importPathShort: VendorlessPath(importpath), dir: dir, - }) + } + if loadNames { + var err error + p.packageName, err = packageDirToName(dir) + if err != nil { + return // Typically an unimportable package like main. + } + } + result = append(result, p) } - gopathwalk.Walk(gopathwalk.SrcDirsRoots(r.env.buildContext()), add, gopathwalk.Options{Debug: r.env.Debug, ModulesEnabled: false}) + roots := filterRoots(gopathwalk.SrcDirsRoots(r.env.buildContext()), exclude) + gopathwalk.Walk(roots, add, gopathwalk.Options{Debug: r.env.Debug, ModulesEnabled: false}) return result, nil } +func filterRoots(roots []gopathwalk.Root, exclude []gopathwalk.RootType) []gopathwalk.Root { + var result []gopathwalk.Root +outer: + for _, root := range roots { + for _, i := range exclude { + if i == root.Type { + continue outer + } + } + result = append(result, root) + } + return result +} + +func (r *gopathResolver) loadExports(ctx context.Context, expectPackage string, pkg *pkg) (map[string]bool, error) { + return loadExportsFromFiles(ctx, r.env, expectPackage, pkg.dir) +} + // VendorlessPath returns the devendorized version of the import path ipath. // For example, VendorlessPath("foo/bar/vendor/a/b") returns "a/b". func VendorlessPath(ipath string) string { @@ -1022,33 +1143,11 @@ func VendorlessPath(ipath string) string { return ipath } -// loadExports returns the set of exported symbols in the package at dir. -// It returns nil on error or if the package name in dir does not match expectPackage. -func loadExports(ctx context.Context, env *ProcessEnv, expectPackage string, pkg *pkg) (map[string]bool, error) { - if env.Debug { - env.Logf("loading exports in dir %s (seeking package %s)", pkg.dir, expectPackage) - } - if pkg.goPackage != nil { - exports := map[string]bool{} - fset := token.NewFileSet() - for _, fname := range pkg.goPackage.CompiledGoFiles { - f, err := parser.ParseFile(fset, fname, nil, 0) - if err != nil { - return nil, fmt.Errorf("parsing %s: %v", fname, err) - } - for name := range f.Scope.Objects { - if ast.IsExported(name) { - exports[name] = true - } - } - } - return exports, nil - } - +func loadExportsFromFiles(ctx context.Context, env *ProcessEnv, expectPackage string, dir string) (map[string]bool, error) { exports := make(map[string]bool) // Look for non-test, buildable .go files which could provide exports. - all, err := ioutil.ReadDir(pkg.dir) + all, err := ioutil.ReadDir(dir) if err != nil { return nil, err } @@ -1058,7 +1157,7 @@ func loadExports(ctx context.Context, env *ProcessEnv, expectPackage string, pkg if !strings.HasSuffix(name, ".go") || strings.HasSuffix(name, "_test.go") { continue } - match, err := env.buildContext().MatchFile(pkg.dir, fi.Name()) + match, err := env.buildContext().MatchFile(dir, fi.Name()) if err != nil || !match { continue } @@ -1066,7 +1165,7 @@ func loadExports(ctx context.Context, env *ProcessEnv, expectPackage string, pkg } if len(files) == 0 { - return nil, fmt.Errorf("dir %v contains no buildable, non-test .go files", pkg.dir) + return nil, fmt.Errorf("dir %v contains no buildable, non-test .go files", dir) } fset := token.NewFileSet() @@ -1077,7 +1176,7 @@ func loadExports(ctx context.Context, env *ProcessEnv, expectPackage string, pkg default: } - fullFile := filepath.Join(pkg.dir, fi.Name()) + fullFile := filepath.Join(dir, fi.Name()) f, err := parser.ParseFile(fset, fullFile, nil, 0) if err != nil { return nil, fmt.Errorf("parsing %s: %v", fullFile, err) @@ -1089,7 +1188,7 @@ func loadExports(ctx context.Context, env *ProcessEnv, expectPackage string, pkg continue } if pkgName != expectPackage { - return nil, fmt.Errorf("scan of dir %v is not expected package %v (actually %v)", pkg.dir, expectPackage, pkgName) + return nil, fmt.Errorf("scan of dir %v is not expected package %v (actually %v)", dir, expectPackage, pkgName) } for name := range f.Scope.Objects { if ast.IsExported(name) { @@ -1104,7 +1203,7 @@ func loadExports(ctx context.Context, env *ProcessEnv, expectPackage string, pkg exportList = append(exportList, k) } sort.Strings(exportList) - env.Logf("loaded exports in dir %v (package %v): %v", pkg.dir, expectPackage, strings.Join(exportList, ", ")) + env.Logf("loaded exports in dir %v (package %v): %v", dir, expectPackage, strings.Join(exportList, ", ")) } return exports, nil } @@ -1178,7 +1277,10 @@ func findImport(ctx context.Context, pass *pass, dirScan []*pkg, pkgName string, wg.Done() }() - exports, err := loadExports(ctx, pass.env, pkgName, c.pkg) + if pass.env.Debug { + pass.env.Logf("loading exports in dir %s (seeking package %s)", c.pkg.dir, pkgName) + } + exports, err := pass.env.GetResolver().loadExports(ctx, pkgName, c.pkg) if err != nil { if pass.env.Debug { pass.env.Logf("loading exports in dir %s (seeking package %s): %v", c.pkg.dir, pkgName, err) @@ -1340,3 +1442,11 @@ type visitFn func(node ast.Node) ast.Visitor func (fn visitFn) Visit(node ast.Node) ast.Visitor { return fn(node) } + +func copyExports(pkg map[string]bool) map[string]bool { + m := make(map[string]bool, len(pkg)) + for k, v := range pkg { + m[k] = v + } + return m +} diff --git a/vendor/golang.org/x/tools/internal/imports/imports.go b/vendor/golang.org/x/tools/internal/imports/imports.go index a47a815f58..2c074cb2db 100644 --- a/vendor/golang.org/x/tools/internal/imports/imports.go +++ b/vendor/golang.org/x/tools/internal/imports/imports.go @@ -13,6 +13,7 @@ import ( "bytes" "fmt" "go/ast" + "go/build" "go/format" "go/parser" "go/printer" @@ -42,18 +43,51 @@ type Options struct { } // Process implements golang.org/x/tools/imports.Process with explicit context in env. -func Process(filename string, src []byte, opt *Options) ([]byte, error) { - if src == nil { - b, err := ioutil.ReadFile(filename) - if err != nil { +func Process(filename string, src []byte, opt *Options) (formatted []byte, err error) { + src, opt, err = initialize(filename, src, opt) + if err != nil { + return nil, err + } + + fileSet := token.NewFileSet() + file, adjust, err := parse(fileSet, filename, src, opt) + if err != nil { + return nil, err + } + + if !opt.FormatOnly { + if err := fixImports(fileSet, file, filename, opt.Env); err != nil { return nil, err } - src = b } + return formatFile(fileSet, file, src, adjust, opt) +} - // Set the logger if the user has not provided it. - if opt.Env.Logf == nil { - opt.Env.Logf = log.Printf +// FixImports returns a list of fixes to the imports that, when applied, +// will leave the imports in the same state as Process. +// +// Note that filename's directory influences which imports can be chosen, +// so it is important that filename be accurate. +func FixImports(filename string, src []byte, opt *Options) (fixes []*ImportFix, err error) { + src, opt, err = initialize(filename, src, opt) + if err != nil { + return nil, err + } + + fileSet := token.NewFileSet() + file, _, err := parse(fileSet, filename, src, opt) + if err != nil { + return nil, err + } + + return getFixes(fileSet, file, filename, opt.Env) +} + +// ApplyFix will apply all of the fixes to the file and format it. +func ApplyFixes(fixes []*ImportFix, filename string, src []byte, opt *Options) (formatted []byte, err error) { + src, opt, err = initialize(filename, src, opt) + if err != nil { + return nil, err } fileSet := token.NewFileSet() @@ -62,12 +96,57 @@ func Process(filename string, src []byte, opt *Options) ([]byte, error) { return nil, err } - if !opt.FormatOnly { - if err := fixImports(fileSet, file, filename, opt.Env); err != nil { - return nil, err + // Apply the fixes to the file. + apply(fileSet, file, fixes) + + return formatFile(fileSet, file, src, adjust, opt) +} + +// GetAllCandidates gets all of the standard library candidate packages to import in +// sorted order on import path. +func GetAllCandidates(filename string, opt *Options) (pkgs []ImportFix, err error) { + _, opt, err = initialize(filename, []byte{}, opt) + if err != nil { + return nil, err + } + return getAllCandidates(filename, opt.Env) +} + +// initialize sets the values for opt and src. +// If they are provided, they are not changed. Otherwise opt is set to the +// default values and src is read from the file system. +func initialize(filename string, src []byte, opt *Options) ([]byte, *Options, error) { + // Use defaults if opt is nil. + if opt == nil { + opt = &Options{Comments: true, TabIndent: true, TabWidth: 8} + } + + // Set the env if the user has not provided it. + if opt.Env == nil { + opt.Env = &ProcessEnv{ + GOPATH: build.Default.GOPATH, + GOROOT: build.Default.GOROOT, } } + // Set the logger if the user has not provided it. + if opt.Env.Logf == nil { + opt.Env.Logf = log.Printf + } + + if src == nil { + b, err := ioutil.ReadFile(filename) + if err != nil { + return nil, nil, err + } + src = b + } + + return src, opt, nil +} + +func formatFile(fileSet *token.FileSet, file *ast.File, src []byte, adjust func(orig []byte, src []byte) []byte, opt *Options) ([]byte, error) { + mergeImports(opt.Env, fileSet, file) sortImports(opt.Env, fileSet, file) imps := astutil.Imports(fileSet, file) var spacesBefore []string // import paths we need spaces before @@ -95,7 +174,7 @@ func Process(filename string, src []byte, opt *Options) ([]byte, error) { printConfig := &printer.Config{Mode: printerMode, Tabwidth: opt.TabWidth} var buf bytes.Buffer - err = printConfig.Fprint(&buf, fileSet, file) + err := printConfig.Fprint(&buf, fileSet, file) if err != nil { return nil, err } diff --git a/vendor/golang.org/x/tools/internal/imports/mkindex.go b/vendor/golang.org/x/tools/internal/imports/mkindex.go new file mode 100644 index 0000000000..ef8c0d2871 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/imports/mkindex.go @@ -0,0 +1,173 @@ +// +build ignore + +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Command mkindex creates the file "pkgindex.go" containing an index of the Go +// standard library. The file is intended to be built as part of the imports +// package, so that the package may be used in environments where a GOROOT is +// not available (such as App Engine). +package imports + +import ( + "bytes" + "fmt" + "go/ast" + "go/build" + "go/format" + "go/parser" + "go/token" + "io/ioutil" + "log" + "os" + "path" + "path/filepath" + "strings" +) + +var ( + pkgIndex = make(map[string][]pkg) + exports = make(map[string]map[string]bool) +) + +func main() { + // Don't use GOPATH. + ctx := build.Default + ctx.GOPATH = "" + + // Populate pkgIndex global from GOROOT. + for _, path := range ctx.SrcDirs() { + f, err := os.Open(path) + if err != nil { + log.Print(err) + continue + } + children, err := f.Readdir(-1) + f.Close() + if err != nil { + log.Print(err) + continue + } + for _, child := range children { + if child.IsDir() { + loadPkg(path, child.Name()) + } + } + } + // Populate exports global. + for _, ps := range pkgIndex { + for _, p := range ps { + e := loadExports(p.dir) + if e != nil { + exports[p.dir] = e + } + } + } + + // Construct source file. + var buf bytes.Buffer + fmt.Fprint(&buf, pkgIndexHead) + fmt.Fprintf(&buf, "var pkgIndexMaster = %#v\n", pkgIndex) + fmt.Fprintf(&buf, "var exportsMaster = %#v\n", exports) + src := buf.Bytes() + + // Replace main.pkg type name with pkg. + src = bytes.Replace(src, []byte("main.pkg"), []byte("pkg"), -1) + // Replace actual GOROOT with "/go". + src = bytes.Replace(src, []byte(ctx.GOROOT), []byte("/go"), -1) + // Add some line wrapping. + src = bytes.Replace(src, []byte("}, "), []byte("},\n"), -1) + src = bytes.Replace(src, []byte("true, "), []byte("true,\n"), -1) + + var err error + src, err = format.Source(src) + if err != nil { + log.Fatal(err) + } + + // Write out source file. + err = ioutil.WriteFile("pkgindex.go", src, 0644) + if err != nil { + log.Fatal(err) + } +} + +const pkgIndexHead = `package imports + +func init() { + pkgIndexOnce.Do(func() { + pkgIndex.m = pkgIndexMaster + }) + loadExports = func(dir string) map[string]bool { + return exportsMaster[dir] + } +} +` + +type pkg struct { + importpath string // full pkg import path, e.g. "net/http" + dir string // absolute file path to pkg directory e.g. "/usr/lib/go/src/fmt" +} + +var fset = token.NewFileSet() + +func loadPkg(root, importpath string) { + shortName := path.Base(importpath) + if shortName == "testdata" { + return + } + + dir := filepath.Join(root, importpath) + pkgIndex[shortName] = append(pkgIndex[shortName], pkg{ + importpath: importpath, + dir: dir, + }) + + pkgDir, err := os.Open(dir) + if err != nil { + return + } + children, err := pkgDir.Readdir(-1) + pkgDir.Close() + if err != nil { + return + } + for _, child := range children { + name := child.Name() + if name == "" { + continue + } + if c := name[0]; c == '.' || ('0' <= c && c <= '9') { + continue + } + if child.IsDir() { + loadPkg(root, filepath.Join(importpath, name)) + } + } +} + +func loadExports(dir string) map[string]bool { + exports := make(map[string]bool) + buildPkg, err := build.ImportDir(dir, 0) + if err != nil { + if strings.Contains(err.Error(), "no buildable Go source files in") { + return nil + } + log.Printf("could not import %q: %v", dir, err) + return nil + } + for _, file := range buildPkg.GoFiles { + f, err := parser.ParseFile(fset, filepath.Join(dir, file), nil, 0) + if err != nil { + log.Printf("could not parse %q: %v", file, err) + continue + } + for name := range f.Scope.Objects { + if ast.IsExported(name) { + exports[name] = true + } + } + } + return exports +} diff --git a/vendor/golang.org/x/tools/internal/imports/mkstdlib.go b/vendor/golang.org/x/tools/internal/imports/mkstdlib.go new file mode 100644 index 0000000000..fa69eebb7e --- /dev/null +++ b/vendor/golang.org/x/tools/internal/imports/mkstdlib.go @@ -0,0 +1,128 @@ +// +build ignore + +// mkstdlib generates the zstdlib.go file, containing the Go standard +// library API symbols. It's baked into the binary to avoid scanning +// GOPATH in the common case. +package main + +import ( + "bufio" + "bytes" + "fmt" + "go/format" + "io" + "io/ioutil" + "log" + "os" + "os/exec" + "path/filepath" + "regexp" + "runtime" + "sort" +) + +func mustOpen(name string) io.Reader { + f, err := os.Open(name) + if err != nil { + log.Fatal(err) + } + return f +} + +func api(base string) string { + return filepath.Join(runtime.GOROOT(), "api", base) +} + +var sym = regexp.MustCompile(`^pkg (\S+).*?, (?:var|func|type|const) ([A-Z]\w*)`) + +var unsafeSyms = map[string]bool{"Alignof": true, "ArbitraryType": true, "Offsetof": true, "Pointer": true, "Sizeof": true} + +func main() { + var buf bytes.Buffer + outf := func(format string, args ...interface{}) { + fmt.Fprintf(&buf, format, args...) + } + outf("// Code generated by mkstdlib.go. DO NOT EDIT.\n\n") + outf("package imports\n") + outf("var stdlib = map[string]map[string]bool{\n") + f := io.MultiReader( + mustOpen(api("go1.txt")), + mustOpen(api("go1.1.txt")), + mustOpen(api("go1.2.txt")), + mustOpen(api("go1.3.txt")), + mustOpen(api("go1.4.txt")), + mustOpen(api("go1.5.txt")), + mustOpen(api("go1.6.txt")), + mustOpen(api("go1.7.txt")), + mustOpen(api("go1.8.txt")), + mustOpen(api("go1.9.txt")), + mustOpen(api("go1.10.txt")), + mustOpen(api("go1.11.txt")), + mustOpen(api("go1.12.txt")), + mustOpen(api("go1.13.txt")), + + // The API of the syscall/js package needs to be computed explicitly, + // because it's not included in the GOROOT/api/go1.*.txt files at this time. + syscallJSAPI(), + ) + sc := bufio.NewScanner(f) + + pkgs := map[string]map[string]bool{ + "unsafe": unsafeSyms, + } + paths := []string{"unsafe"} + + for sc.Scan() { + l := sc.Text() + if m := sym.FindStringSubmatch(l); m != nil { + path, sym := m[1], m[2] + + if _, ok := pkgs[path]; !ok { + pkgs[path] = map[string]bool{} + paths = append(paths, path) + } + pkgs[path][sym] = true + } + } + if err := sc.Err(); err != nil { + log.Fatal(err) + } + sort.Strings(paths) + for _, path := range paths { + outf("\t%q: map[string]bool{\n", path) + pkg := pkgs[path] + var syms []string + for sym := range pkg { + syms = append(syms, sym) + } + sort.Strings(syms) + for _, sym := range syms { + outf("\t\t%q: true,\n", sym) + } + outf("},\n") + } + outf("}\n") + fmtbuf, err := format.Source(buf.Bytes()) + if err != nil { + log.Fatal(err) + } + err = ioutil.WriteFile("zstdlib.go", fmtbuf, 0666) + if err != nil { + log.Fatal(err) + } +} + +// syscallJSAPI returns the API of the syscall/js package. +// It's computed from the contents of $(go env GOROOT)/src/syscall/js. +func syscallJSAPI() io.Reader { + var exeSuffix string + if runtime.GOOS == "windows" { + exeSuffix = ".exe" + } + cmd := exec.Command("go"+exeSuffix, "run", "cmd/api", "-contexts", "js-wasm", "syscall/js") + out, err := cmd.Output() + if err != nil { + log.Fatalln(err) + } + return bytes.NewReader(out) +} diff --git a/vendor/golang.org/x/tools/internal/imports/mod.go b/vendor/golang.org/x/tools/internal/imports/mod.go index a0ebd0737c..2d394abb4f 100644 --- a/vendor/golang.org/x/tools/internal/imports/mod.go +++ b/vendor/golang.org/x/tools/internal/imports/mod.go @@ -2,7 +2,9 @@ package imports import ( "bytes" + "context" "encoding/json" + "fmt" "io/ioutil" "os" "path" @@ -12,51 +14,98 @@ import ( "strconv" "strings" "sync" - "time" "golang.org/x/tools/internal/gopathwalk" "golang.org/x/tools/internal/module" + "golang.org/x/tools/internal/semver" ) -// moduleResolver implements resolver for modules using the go command as little +// ModuleResolver implements resolver for modules using the go command as little // as feasible. -type moduleResolver struct { - env *ProcessEnv - - initialized bool - main *moduleJSON - modsByModPath []*moduleJSON // All modules, ordered by # of path components in module Path... - modsByDir []*moduleJSON // ...or Dir. -} - -type moduleJSON struct { - Path string // module path - Version string // module version - Versions []string // available module versions (with -versions) - Replace *moduleJSON // replaced by this module - Time *time.Time // time version was created - Update *moduleJSON // available update, if any (with -u) - Main bool // is this the main module? - Indirect bool // is this module only an indirect dependency of main module? - Dir string // directory holding files for this module, if any - GoMod string // path to go.mod file for this module, if any - Error *moduleErrorJSON // error loading module +type ModuleResolver struct { + env *ProcessEnv + moduleCacheDir string + dummyVendorMod *ModuleJSON // If vendoring is enabled, the pseudo-module that represents the /vendor directory. + + Initialized bool + Main *ModuleJSON + ModsByModPath []*ModuleJSON // All modules, ordered by # of path components in module Path... + ModsByDir []*ModuleJSON // ...or Dir. + + // moduleCacheCache stores information about the module cache. + moduleCacheCache *dirInfoCache + otherCache *dirInfoCache } -type moduleErrorJSON struct { - Err string // the error itself +type ModuleJSON struct { + Path string // module path + Replace *ModuleJSON // replaced by this module + Main bool // is this the main module? + Dir string // directory holding files for this module, if any + GoMod string // path to go.mod file for this module, if any + GoVersion string // go version used in module } -func (r *moduleResolver) init() error { - if r.initialized { +func (r *ModuleResolver) init() error { + if r.Initialized { return nil } + mainMod, vendorEnabled, err := vendorEnabled(r.env) + if err != nil { + return err + } + + if mainMod != nil && vendorEnabled { + // Vendor mode is on, so all the non-Main modules are irrelevant, + // and we need to search /vendor for everything. + r.Main = mainMod + r.dummyVendorMod = &ModuleJSON{ + Path: "", + Dir: filepath.Join(mainMod.Dir, "vendor"), + } + r.ModsByModPath = []*ModuleJSON{mainMod, r.dummyVendorMod} + r.ModsByDir = []*ModuleJSON{mainMod, r.dummyVendorMod} + } else { + // Vendor mode is off, so run go list -m ... to find everything. + r.initAllMods() + } + + r.moduleCacheDir = filepath.Join(filepath.SplitList(r.env.GOPATH)[0], "/pkg/mod") + + sort.Slice(r.ModsByModPath, func(i, j int) bool { + count := func(x int) int { + return strings.Count(r.ModsByModPath[x].Path, "/") + } + return count(j) < count(i) // descending order + }) + sort.Slice(r.ModsByDir, func(i, j int) bool { + count := func(x int) int { + return strings.Count(r.ModsByDir[x].Dir, "/") + } + return count(j) < count(i) // descending order + }) + + if r.moduleCacheCache == nil { + r.moduleCacheCache = &dirInfoCache{ + dirs: map[string]*directoryPackageInfo{}, + } + } + if r.otherCache == nil { + r.otherCache = &dirInfoCache{ + dirs: map[string]*directoryPackageInfo{}, + } + } + r.Initialized = true + return nil +} + +func (r *ModuleResolver) initAllMods() error { stdout, err := r.env.invokeGo("list", "-m", "-json", "...") if err != nil { return err } for dec := json.NewDecoder(stdout); dec.More(); { - mod := &moduleJSON{} + mod := &ModuleJSON{} if err := dec.Decode(mod); err != nil { return err } @@ -67,48 +116,69 @@ func (r *moduleResolver) init() error { // Can't do anything with a module that's not downloaded. continue } - r.modsByModPath = append(r.modsByModPath, mod) - r.modsByDir = append(r.modsByDir, mod) + r.ModsByModPath = append(r.ModsByModPath, mod) + r.ModsByDir = append(r.ModsByDir, mod) if mod.Main { - r.main = mod + r.Main = mod } } + return nil +} - sort.Slice(r.modsByModPath, func(i, j int) bool { - count := func(x int) int { - return strings.Count(r.modsByModPath[x].Path, "/") - } - return count(j) < count(i) // descending order - }) - sort.Slice(r.modsByDir, func(i, j int) bool { - count := func(x int) int { - return strings.Count(r.modsByDir[x].Dir, "/") - } - return count(j) < count(i) // descending order - }) +func (r *ModuleResolver) ClearForNewScan() { + r.otherCache = &dirInfoCache{ + dirs: map[string]*directoryPackageInfo{}, + } +} - r.initialized = true - return nil +func (r *ModuleResolver) ClearForNewMod() { + env := r.env + *r = ModuleResolver{ + env: env, + } + r.init() } // findPackage returns the module and directory that contains the package at // the given import path, or returns nil, "" if no module is in scope. -func (r *moduleResolver) findPackage(importPath string) (*moduleJSON, string) { - for _, m := range r.modsByModPath { +func (r *ModuleResolver) findPackage(importPath string) (*ModuleJSON, string) { + // This can't find packages in the stdlib, but that's harmless for all + // the existing code paths. + for _, m := range r.ModsByModPath { if !strings.HasPrefix(importPath, m.Path) { continue } pathInModule := importPath[len(m.Path):] pkgDir := filepath.Join(m.Dir, pathInModule) - if dirIsNestedModule(pkgDir, m) { + if r.dirIsNestedModule(pkgDir, m) { continue } + if info, ok := r.cacheLoad(pkgDir); ok { + if loaded, err := info.reachedStatus(nameLoaded); loaded { + if err != nil { + continue // No package in this dir. + } + return m, pkgDir + } + if scanned, err := info.reachedStatus(directoryScanned); scanned && err != nil { + continue // Dir is unreadable, etc. + } + // This is slightly wrong: a directory doesn't have to have an + // importable package to count as a package for package-to-module + // resolution. package main or _test files should count but + // don't. + // TODO(heschi): fix this. + if _, err := r.cachePackageName(pkgDir); err == nil { + return m, pkgDir + } + } + + // Not cached. Read the filesystem. pkgFiles, err := ioutil.ReadDir(pkgDir) if err != nil { continue } - // A module only contains a package if it has buildable go // files in that directory. If not, it could be provided by an // outer module. See #29736. @@ -121,9 +191,45 @@ func (r *moduleResolver) findPackage(importPath string) (*moduleJSON, string) { return nil, "" } +func (r *ModuleResolver) cacheLoad(dir string) (directoryPackageInfo, bool) { + if info, ok := r.moduleCacheCache.Load(dir); ok { + return info, ok + } + return r.otherCache.Load(dir) +} + +func (r *ModuleResolver) cacheStore(info directoryPackageInfo) { + if info.rootType == gopathwalk.RootModuleCache { + r.moduleCacheCache.Store(info.dir, info) + } else { + r.otherCache.Store(info.dir, info) + } +} + +func (r *ModuleResolver) cacheKeys() []string { + return append(r.moduleCacheCache.Keys(), r.otherCache.Keys()...) +} + +// cachePackageName caches the package name for a dir already in the cache. +func (r *ModuleResolver) cachePackageName(dir string) (directoryPackageInfo, error) { + info, ok := r.cacheLoad(dir) + if !ok { + panic("cachePackageName on uncached dir " + dir) + } + + loaded, err := info.reachedStatus(nameLoaded) + if loaded { + return info, err + } + info.packageName, info.err = packageDirToName(info.dir) + info.status = nameLoaded + r.cacheStore(info) + return info, info.err +} + // findModuleByDir returns the module that contains dir, or nil if no such // module is in scope. -func (r *moduleResolver) findModuleByDir(dir string) *moduleJSON { +func (r *ModuleResolver) findModuleByDir(dir string) *ModuleJSON { // This is quite tricky and may not be correct. dir could be: // - a package in the main module. // - a replace target underneath the main module's directory. @@ -134,12 +240,12 @@ func (r *moduleResolver) findModuleByDir(dir string) *moduleJSON { // - in /vendor/ in -mod=vendor mode. // - nested module? Dunno. // Rumor has it that replace targets cannot contain other replace targets. - for _, m := range r.modsByDir { + for _, m := range r.ModsByDir { if !strings.HasPrefix(dir, m.Dir) { continue } - if dirIsNestedModule(dir, m) { + if r.dirIsNestedModule(dir, m) { continue } @@ -150,33 +256,67 @@ func (r *moduleResolver) findModuleByDir(dir string) *moduleJSON { // dirIsNestedModule reports if dir is contained in a nested module underneath // mod, not actually in mod. -func dirIsNestedModule(dir string, mod *moduleJSON) bool { +func (r *ModuleResolver) dirIsNestedModule(dir string, mod *ModuleJSON) bool { if !strings.HasPrefix(dir, mod.Dir) { return false } - mf := findModFile(dir) - if mf == "" { + if r.dirInModuleCache(dir) { + // Nested modules in the module cache are pruned, + // so it cannot be a nested module. + return false + } + if mod != nil && mod == r.dummyVendorMod { + // The /vendor pseudomodule is flattened and doesn't actually count. return false } - return filepath.Dir(mf) != mod.Dir + modDir, _ := r.modInfo(dir) + if modDir == "" { + return false + } + return modDir != mod.Dir } -func findModFile(dir string) string { +func (r *ModuleResolver) modInfo(dir string) (modDir string, modName string) { + readModName := func(modFile string) string { + modBytes, err := ioutil.ReadFile(modFile) + if err != nil { + return "" + } + return modulePath(modBytes) + } + + if r.dirInModuleCache(dir) { + matches := modCacheRegexp.FindStringSubmatch(dir) + index := strings.Index(dir, matches[1]+"@"+matches[2]) + modDir := filepath.Join(dir[:index], matches[1]+"@"+matches[2]) + return modDir, readModName(filepath.Join(modDir, "go.mod")) + } for { + if info, ok := r.cacheLoad(dir); ok { + return info.moduleDir, info.moduleName + } f := filepath.Join(dir, "go.mod") info, err := os.Stat(f) if err == nil && !info.IsDir() { - return f + return dir, readModName(f) } + d := filepath.Dir(dir) if len(d) >= len(dir) { - return "" // reached top of file system, no go.mod + return "", "" // reached top of file system, no go.mod } dir = d } } -func (r *moduleResolver) loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) { +func (r *ModuleResolver) dirInModuleCache(dir string) bool { + if r.moduleCacheDir == "" { + return false + } + return strings.HasPrefix(dir, r.moduleCacheDir) +} + +func (r *ModuleResolver) loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) { if err := r.init(); err != nil { return nil, err } @@ -195,7 +335,7 @@ func (r *moduleResolver) loadPackageNames(importPaths []string, srcDir string) ( return names, nil } -func (r *moduleResolver) scan(_ references) ([]*pkg, error) { +func (r *ModuleResolver) scan(_ references, loadNames bool, exclude []gopathwalk.RootType) ([]*pkg, error) { if err := r.init(); err != nil { return nil, err } @@ -204,106 +344,190 @@ func (r *moduleResolver) scan(_ references) ([]*pkg, error) { roots := []gopathwalk.Root{ {filepath.Join(r.env.GOROOT, "/src"), gopathwalk.RootGOROOT}, } - if r.main != nil { - roots = append(roots, gopathwalk.Root{r.main.Dir, gopathwalk.RootCurrentModule}) - } - for _, p := range filepath.SplitList(r.env.GOPATH) { - roots = append(roots, gopathwalk.Root{filepath.Join(p, "/pkg/mod"), gopathwalk.RootModuleCache}) + if r.Main != nil { + roots = append(roots, gopathwalk.Root{r.Main.Dir, gopathwalk.RootCurrentModule}) } - - // Walk replace targets, just in case they're not in any of the above. - for _, mod := range r.modsByModPath { - if mod.Replace != nil { - roots = append(roots, gopathwalk.Root{mod.Dir, gopathwalk.RootOther}) + if r.dummyVendorMod != nil { + roots = append(roots, gopathwalk.Root{r.dummyVendorMod.Dir, gopathwalk.RootOther}) + } else { + roots = append(roots, gopathwalk.Root{r.moduleCacheDir, gopathwalk.RootModuleCache}) + // Walk replace targets, just in case they're not in any of the above. + for _, mod := range r.ModsByModPath { + if mod.Replace != nil { + roots = append(roots, gopathwalk.Root{mod.Dir, gopathwalk.RootOther}) + } } } + roots = filterRoots(roots, exclude) + var result []*pkg - dupCheck := make(map[string]bool) var mu sync.Mutex - gopathwalk.Walk(roots, func(root gopathwalk.Root, dir string) { + // We assume cached directories have not changed. We can skip them and their + // children. + skip := func(root gopathwalk.Root, dir string) bool { mu.Lock() defer mu.Unlock() - if _, dup := dupCheck[dir]; dup { - return + info, ok := r.cacheLoad(dir) + if !ok { + return false } + // This directory can be skipped as long as we have already scanned it. + // Packages with errors will continue to have errors, so there is no need + // to rescan them. + packageScanned, _ := info.reachedStatus(directoryScanned) + return packageScanned + } - dupCheck[dir] = true + // Add anything new to the cache. We'll process everything in it below. + add := func(root gopathwalk.Root, dir string) { + mu.Lock() + defer mu.Unlock() + + r.cacheStore(r.scanDirForPackage(root, dir)) + } - subdir := "" - if dir != root.Path { - subdir = dir[len(root.Path)+len("/"):] + gopathwalk.WalkSkip(roots, add, skip, gopathwalk.Options{Debug: r.env.Debug, ModulesEnabled: true}) + + // Everything we already had, and everything new, is now in the cache. + for _, dir := range r.cacheKeys() { + info, ok := r.cacheLoad(dir) + if !ok { + continue } - importPath := filepath.ToSlash(subdir) - if strings.HasPrefix(importPath, "vendor/") { - // Ignore vendor dirs. If -mod=vendor is on, then things - // should mostly just work, but when it's not vendor/ - // is a mess. There's no easy way to tell if it's on. - // We can still find things in the mod cache and - // map them into /vendor when -mod=vendor is on. - return + + // Skip this directory if we were not able to get the package information successfully. + if scanned, err := info.reachedStatus(directoryScanned); !scanned || err != nil { + continue } - switch root.Type { - case gopathwalk.RootCurrentModule: - importPath = path.Join(r.main.Path, filepath.ToSlash(subdir)) - case gopathwalk.RootModuleCache: - matches := modCacheRegexp.FindStringSubmatch(subdir) - modPath, err := module.DecodePath(filepath.ToSlash(matches[1])) - if err != nil { - if r.env.Debug { - r.env.Logf("decoding module cache path %q: %v", subdir, err) - } - return - } - importPath = path.Join(modPath, filepath.ToSlash(matches[3])) - case gopathwalk.RootGOROOT: - importPath = subdir - } - - // Check if the directory is underneath a module that's in scope. - if mod := r.findModuleByDir(dir); mod != nil { - // It is. If dir is the target of a replace directive, - // our guessed import path is wrong. Use the real one. - if mod.Dir == dir { - importPath = mod.Path - } else { - dirInMod := dir[len(mod.Dir)+len("/"):] - importPath = path.Join(mod.Path, filepath.ToSlash(dirInMod)) + + // If we want package names, make sure the cache has them. + if loadNames { + var err error + if info, err = r.cachePackageName(info.dir); err != nil { + continue } + } + + res, err := r.canonicalize(info) + if err != nil { + continue + } + result = append(result, res) + } + + return result, nil +} + +// canonicalize gets the result of canonicalizing the packages using the results +// of initializing the resolver from 'go list -m'. +func (r *ModuleResolver) canonicalize(info directoryPackageInfo) (*pkg, error) { + // Packages in GOROOT are already canonical, regardless of the std/cmd modules. + if info.rootType == gopathwalk.RootGOROOT { + return &pkg{ + importPathShort: info.nonCanonicalImportPath, + dir: info.dir, + packageName: path.Base(info.nonCanonicalImportPath), + }, nil + } + + importPath := info.nonCanonicalImportPath + // Check if the directory is underneath a module that's in scope. + if mod := r.findModuleByDir(info.dir); mod != nil { + // It is. If dir is the target of a replace directive, + // our guessed import path is wrong. Use the real one. + if mod.Dir == info.dir { + importPath = mod.Path } else { - // The package is in an unknown module. Check that it's - // not obviously impossible to import. - var modFile string - switch root.Type { - case gopathwalk.RootModuleCache: - matches := modCacheRegexp.FindStringSubmatch(subdir) - modFile = filepath.Join(matches[1], "@", matches[2], "go.mod") - default: - modFile = findModFile(dir) - } + dirInMod := info.dir[len(mod.Dir)+len("/"):] + importPath = path.Join(mod.Path, filepath.ToSlash(dirInMod)) + } + } else if info.needsReplace { + return nil, fmt.Errorf("package in %q is not valid without a replace statement", info.dir) + } + + res := &pkg{ + importPathShort: importPath, + dir: info.dir, + packageName: info.packageName, // may not be populated if the caller didn't ask for it + } + // We may have discovered a package that has a different version + // in scope already. Canonicalize to that one if possible. + if _, canonicalDir := r.findPackage(importPath); canonicalDir != "" { + res.dir = canonicalDir + } + return res, nil +} + +func (r *ModuleResolver) loadExports(ctx context.Context, expectPackage string, pkg *pkg) (map[string]bool, error) { + if err := r.init(); err != nil { + return nil, err + } + return loadExportsFromFiles(ctx, r.env, expectPackage, pkg.dir) +} - modBytes, err := ioutil.ReadFile(modFile) - if err == nil && !strings.HasPrefix(importPath, modulePath(modBytes)) { - // The module's declared path does not match - // its expected path. It probably needs a - // replace directive we don't have. - return +func (r *ModuleResolver) scanDirForPackage(root gopathwalk.Root, dir string) directoryPackageInfo { + subdir := "" + if dir != root.Path { + subdir = dir[len(root.Path)+len("/"):] + } + importPath := filepath.ToSlash(subdir) + if strings.HasPrefix(importPath, "vendor/") { + // Only enter vendor directories if they're explicitly requested as a root. + return directoryPackageInfo{ + status: directoryScanned, + err: fmt.Errorf("unwanted vendor directory"), + } + } + switch root.Type { + case gopathwalk.RootCurrentModule: + importPath = path.Join(r.Main.Path, filepath.ToSlash(subdir)) + case gopathwalk.RootModuleCache: + matches := modCacheRegexp.FindStringSubmatch(subdir) + if len(matches) == 0 { + return directoryPackageInfo{ + status: directoryScanned, + err: fmt.Errorf("invalid module cache path: %v", subdir), } } - // We may have discovered a package that has a different version - // in scope already. Canonicalize to that one if possible. - if _, canonicalDir := r.findPackage(importPath); canonicalDir != "" { - dir = canonicalDir + modPath, err := module.DecodePath(filepath.ToSlash(matches[1])) + if err != nil { + if r.env.Debug { + r.env.Logf("decoding module cache path %q: %v", subdir, err) + } + return directoryPackageInfo{ + status: directoryScanned, + err: fmt.Errorf("decoding module cache path %q: %v", subdir, err), + } } + importPath = path.Join(modPath, filepath.ToSlash(matches[3])) + } - result = append(result, &pkg{ - importPathShort: VendorlessPath(importPath), - dir: dir, - }) - }, gopathwalk.Options{Debug: r.env.Debug, ModulesEnabled: true}) - return result, nil + modDir, modName := r.modInfo(dir) + result := directoryPackageInfo{ + status: directoryScanned, + dir: dir, + rootType: root.Type, + nonCanonicalImportPath: importPath, + needsReplace: false, + moduleDir: modDir, + moduleName: modName, + } + if root.Type == gopathwalk.RootGOROOT { + // stdlib packages are always in scope, despite the confusing go.mod + return result + } + // Check that this package is not obviously impossible to import. + if !strings.HasPrefix(importPath, modName) { + // The module's declared path does not match + // its expected path. It probably needs a + // replace directive we don't have. + result.needsReplace = true + } + + return result } // modCacheRegexp splits a path in a module cache into module, module version, and package. @@ -352,3 +576,63 @@ func modulePath(mod []byte) string { } return "" // missing module path } + +var modFlagRegexp = regexp.MustCompile(`-mod[ =](\w+)`) + +// vendorEnabled indicates if vendoring is enabled. +// Inspired by setDefaultBuildMod in modload/init.go +func vendorEnabled(env *ProcessEnv) (*ModuleJSON, bool, error) { + mainMod, go114, err := getMainModuleAnd114(env) + if err != nil { + return nil, false, err + } + matches := modFlagRegexp.FindStringSubmatch(env.GOFLAGS) + var modFlag string + if len(matches) != 0 { + modFlag = matches[1] + } + if modFlag != "" { + // Don't override an explicit '-mod=' argument. + return mainMod, modFlag == "vendor", nil + } + if mainMod == nil || !go114 { + return mainMod, false, nil + } + // Check 1.14's automatic vendor mode. + if fi, err := os.Stat(filepath.Join(mainMod.Dir, "vendor")); err == nil && fi.IsDir() { + if mainMod.GoVersion != "" && semver.Compare("v"+mainMod.GoVersion, "v1.14") >= 0 { + // The Go version is at least 1.14, and a vendor directory exists. + // Set -mod=vendor by default. + return mainMod, true, nil + } + } + return mainMod, false, nil +} + +// getMainModuleAnd114 gets the main module's information and whether the +// go command in use is 1.14+. This is the information needed to figure out +// if vendoring should be enabled. +func getMainModuleAnd114(env *ProcessEnv) (*ModuleJSON, bool, error) { + const format = `{{.Path}} +{{.Dir}} +{{.GoMod}} +{{.GoVersion}} +{{range context.ReleaseTags}}{{if eq . "go1.14"}}{{.}}{{end}}{{end}} +` + stdout, err := env.invokeGo("list", "-m", "-f", format) + if err != nil { + return nil, false, nil + } + lines := strings.Split(stdout.String(), "\n") + if len(lines) < 5 { + return nil, false, fmt.Errorf("unexpected stdout: %q", stdout) + } + mod := &ModuleJSON{ + Path: lines[0], + Dir: lines[1], + GoMod: lines[2], + GoVersion: lines[3], + Main: true, + } + return mod, lines[4] == "go1.14", nil +} diff --git a/vendor/golang.org/x/tools/internal/imports/mod_cache.go b/vendor/golang.org/x/tools/internal/imports/mod_cache.go new file mode 100644 index 0000000000..5c542e65f0 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/imports/mod_cache.go @@ -0,0 +1,123 @@ +package imports + +import ( + "sync" + + "golang.org/x/tools/internal/gopathwalk" +) + +// To find packages to import, the resolver needs to know about all of the +// the packages that could be imported. This includes packages that are +// already in modules that are in (1) the current module, (2) replace targets, +// and (3) packages in the module cache. Packages in (1) and (2) may change over +// time, as the client may edit the current module and locally replaced modules. +// The module cache (which includes all of the packages in (3)) can only +// ever be added to. +// +// The resolver can thus save state about packages in the module cache +// and guarantee that this will not change over time. To obtain information +// about new modules added to the module cache, the module cache should be +// rescanned. +// +// It is OK to serve information about modules that have been deleted, +// as they do still exist. +// TODO(suzmue): can we share information with the caller about +// what module needs to be downloaded to import this package? + +type directoryPackageStatus int + +const ( + _ directoryPackageStatus = iota + directoryScanned + nameLoaded +) + +type directoryPackageInfo struct { + // status indicates the extent to which this struct has been filled in. + status directoryPackageStatus + // err is non-nil when there was an error trying to reach status. + err error + + // Set when status >= directoryScanned. + + // dir is the absolute directory of this package. + dir string + rootType gopathwalk.RootType + // nonCanonicalImportPath is the package's expected import path. It may + // not actually be importable at that path. + nonCanonicalImportPath string + // needsReplace is true if the nonCanonicalImportPath does not match the + // module's declared path, making it impossible to import without a + // replace directive. + needsReplace bool + + // Module-related information. + moduleDir string // The directory that is the module root of this dir. + moduleName string // The module name that contains this dir. + + // Set when status >= nameLoaded. + + packageName string // the package name, as declared in the source. +} + +// reachedStatus returns true when info has a status at least target and any error associated with +// an attempt to reach target. +func (info *directoryPackageInfo) reachedStatus(target directoryPackageStatus) (bool, error) { + if info.err == nil { + return info.status >= target, nil + } + if info.status == target { + return true, info.err + } + return true, nil +} + +// dirInfoCache is a concurrency safe map for storing information about +// directories that may contain packages. +// +// The information in this cache is built incrementally. Entries are initialized in scan. +// No new keys should be added in any other functions, as all directories containing +// packages are identified in scan. +// +// Other functions, including loadExports and findPackage, may update entries in this cache +// as they discover new things about the directory. +// +// The information in the cache is not expected to change for the cache's +// lifetime, so there is no protection against competing writes. Users should +// take care not to hold the cache across changes to the underlying files. +// +// TODO(suzmue): consider other concurrency strategies and data structures (RWLocks, sync.Map, etc) +type dirInfoCache struct { + mu sync.Mutex + // dirs stores information about packages in directories, keyed by absolute path. + dirs map[string]*directoryPackageInfo +} + +// Store stores the package info for dir. +func (d *dirInfoCache) Store(dir string, info directoryPackageInfo) { + d.mu.Lock() + defer d.mu.Unlock() + stored := info // defensive copy + d.dirs[dir] = &stored +} + +// Load returns a copy of the directoryPackageInfo for absolute directory dir. +func (d *dirInfoCache) Load(dir string) (directoryPackageInfo, bool) { + d.mu.Lock() + defer d.mu.Unlock() + info, ok := d.dirs[dir] + if !ok { + return directoryPackageInfo{}, false + } + return *info, true +} + +// Keys returns the keys currently present in d. +func (d *dirInfoCache) Keys() (keys []string) { + d.mu.Lock() + defer d.mu.Unlock() + for key := range d.dirs { + keys = append(keys, key) + } + return keys +} diff --git a/vendor/golang.org/x/tools/internal/imports/sortimports.go b/vendor/golang.org/x/tools/internal/imports/sortimports.go index 0a156fe2ef..226279471d 100644 --- a/vendor/golang.org/x/tools/internal/imports/sortimports.go +++ b/vendor/golang.org/x/tools/internal/imports/sortimports.go @@ -58,6 +58,53 @@ func sortImports(env *ProcessEnv, fset *token.FileSet, f *ast.File) { } } +// mergeImports merges all the import declarations into the first one. +// Taken from golang.org/x/tools/ast/astutil. +func mergeImports(env *ProcessEnv, fset *token.FileSet, f *ast.File) { + if len(f.Decls) <= 1 { + return + } + + // Merge all the import declarations into the first one. + var first *ast.GenDecl + for i := 0; i < len(f.Decls); i++ { + decl := f.Decls[i] + gen, ok := decl.(*ast.GenDecl) + if !ok || gen.Tok != token.IMPORT || declImports(gen, "C") { + continue + } + if first == nil { + first = gen + continue // Don't touch the first one. + } + // We now know there is more than one package in this import + // declaration. Ensure that it ends up parenthesized. + first.Lparen = first.Pos() + // Move the imports of the other import declaration to the first one. + for _, spec := range gen.Specs { + spec.(*ast.ImportSpec).Path.ValuePos = first.Pos() + first.Specs = append(first.Specs, spec) + } + f.Decls = append(f.Decls[:i], f.Decls[i+1:]...) + i-- + } +} + +// declImports reports whether gen contains an import of path. +// Taken from golang.org/x/tools/ast/astutil. +func declImports(gen *ast.GenDecl, path string) bool { + if gen.Tok != token.IMPORT { + return false + } + for _, spec := range gen.Specs { + impspec := spec.(*ast.ImportSpec) + if importPath(impspec) == path { + return true + } + } + return false +} + func importPath(s ast.Spec) string { t, err := strconv.Unquote(s.(*ast.ImportSpec).Path.Value) if err == nil { diff --git a/vendor/golang.org/x/tools/internal/imports/zstdlib.go b/vendor/golang.org/x/tools/internal/imports/zstdlib.go index d81b8c5307..544339e53b 100644 --- a/vendor/golang.org/x/tools/internal/imports/zstdlib.go +++ b/vendor/golang.org/x/tools/internal/imports/zstdlib.go @@ -125,6 +125,7 @@ var stdlib = map[string]map[string]bool{ "ToTitleSpecial": true, "ToUpper": true, "ToUpperSpecial": true, + "ToValidUTF8": true, "Trim": true, "TrimFunc": true, "TrimLeft": true, @@ -304,6 +305,18 @@ var stdlib = map[string]map[string]bool{ "Sign": true, "Verify": true, }, + "crypto/ed25519": map[string]bool{ + "GenerateKey": true, + "NewKeyFromSeed": true, + "PrivateKey": true, + "PrivateKeySize": true, + "PublicKey": true, + "PublicKeySize": true, + "SeedSize": true, + "Sign": true, + "SignatureSize": true, + "Verify": true, + }, "crypto/elliptic": map[string]bool{ "Curve": true, "CurveParams": true, @@ -420,6 +433,7 @@ var stdlib = map[string]map[string]bool{ "ECDSAWithP384AndSHA384": true, "ECDSAWithP521AndSHA512": true, "ECDSAWithSHA1": true, + "Ed25519": true, "Listen": true, "LoadX509KeyPair": true, "NewLRUClientSessionCache": true, @@ -478,35 +492,36 @@ var stdlib = map[string]map[string]bool{ "X509KeyPair": true, }, "crypto/x509": map[string]bool{ - "CANotAuthorizedForExtKeyUsage": true, - "CANotAuthorizedForThisName": true, - "CertPool": true, - "Certificate": true, - "CertificateInvalidError": true, - "CertificateRequest": true, - "ConstraintViolationError": true, - "CreateCertificate": true, - "CreateCertificateRequest": true, - "DSA": true, - "DSAWithSHA1": true, - "DSAWithSHA256": true, - "DecryptPEMBlock": true, - "ECDSA": true, - "ECDSAWithSHA1": true, - "ECDSAWithSHA256": true, - "ECDSAWithSHA384": true, - "ECDSAWithSHA512": true, - "EncryptPEMBlock": true, - "ErrUnsupportedAlgorithm": true, - "Expired": true, - "ExtKeyUsage": true, - "ExtKeyUsageAny": true, - "ExtKeyUsageClientAuth": true, - "ExtKeyUsageCodeSigning": true, - "ExtKeyUsageEmailProtection": true, - "ExtKeyUsageIPSECEndSystem": true, - "ExtKeyUsageIPSECTunnel": true, - "ExtKeyUsageIPSECUser": true, + "CANotAuthorizedForExtKeyUsage": true, + "CANotAuthorizedForThisName": true, + "CertPool": true, + "Certificate": true, + "CertificateInvalidError": true, + "CertificateRequest": true, + "ConstraintViolationError": true, + "CreateCertificate": true, + "CreateCertificateRequest": true, + "DSA": true, + "DSAWithSHA1": true, + "DSAWithSHA256": true, + "DecryptPEMBlock": true, + "ECDSA": true, + "ECDSAWithSHA1": true, + "ECDSAWithSHA256": true, + "ECDSAWithSHA384": true, + "ECDSAWithSHA512": true, + "Ed25519": true, + "EncryptPEMBlock": true, + "ErrUnsupportedAlgorithm": true, + "Expired": true, + "ExtKeyUsage": true, + "ExtKeyUsageAny": true, + "ExtKeyUsageClientAuth": true, + "ExtKeyUsageCodeSigning": true, + "ExtKeyUsageEmailProtection": true, + "ExtKeyUsageIPSECEndSystem": true, + "ExtKeyUsageIPSECTunnel": true, + "ExtKeyUsageIPSECUser": true, "ExtKeyUsageMicrosoftCommercialCodeSigning": true, "ExtKeyUsageMicrosoftKernelCodeSigning": true, "ExtKeyUsageMicrosoftServerGatedCrypto": true, @@ -558,6 +573,7 @@ var stdlib = map[string]map[string]bool{ "ParsePKCS8PrivateKey": true, "ParsePKIXPublicKey": true, "PublicKeyAlgorithm": true, + "PureEd25519": true, "RSA": true, "SHA1WithRSA": true, "SHA256WithRSA": true, @@ -612,8 +628,10 @@ var stdlib = map[string]map[string]bool{ "NamedArg": true, "NullBool": true, "NullFloat64": true, + "NullInt32": true, "NullInt64": true, "NullString": true, + "NullTime": true, "Open": true, "OpenDB": true, "Out": true, @@ -860,6 +878,7 @@ var stdlib = map[string]map[string]bool{ "UcharType": true, "UintType": true, "UnspecifiedType": true, + "UnsupportedType": true, "VoidType": true, }, "debug/elf": map[string]bool{ @@ -2505,7 +2524,10 @@ var stdlib = map[string]map[string]bool{ "UnsupportedTypeError": true, }, "errors": map[string]bool{ - "New": true, + "As": true, + "Is": true, + "New": true, + "Unwrap": true, }, "expvar": map[string]bool{ "Do": true, @@ -2615,10 +2637,12 @@ var stdlib = map[string]map[string]bool{ "CommentMap": true, "CompositeLit": true, "Con": true, + "Decl": true, "DeclStmt": true, "DeferStmt": true, "Ellipsis": true, "EmptyStmt": true, + "Expr": true, "ExprStmt": true, "Field": true, "FieldFilter": true, @@ -2679,7 +2703,9 @@ var stdlib = map[string]map[string]bool{ "SendStmt": true, "SliceExpr": true, "SortImports": true, + "Spec": true, "StarExpr": true, + "Stmt": true, "StructType": true, "SwitchStmt": true, "Typ": true, @@ -2725,6 +2751,7 @@ var stdlib = map[string]map[string]bool{ "Int": true, "Int64Val": true, "Kind": true, + "Make": true, "MakeBool": true, "MakeFloat64": true, "MakeFromBytes": true, @@ -2746,6 +2773,8 @@ var stdlib = map[string]map[string]bool{ "Uint64Val": true, "UnaryOp": true, "Unknown": true, + "Val": true, + "Value": true, }, "go/doc": map[string]bool{ "AllDecls": true, @@ -2855,6 +2884,9 @@ var stdlib = map[string]map[string]bool{ "INC": true, "INT": true, "INTERFACE": true, + "IsExported": true, + "IsIdentifier": true, + "IsKeyword": true, "LAND": true, "LBRACE": true, "LBRACK": true, @@ -2916,6 +2948,7 @@ var stdlib = map[string]map[string]bool{ "Byte": true, "Chan": true, "ChanDir": true, + "CheckExpr": true, "Checker": true, "Comparable": true, "Complex128": true, @@ -2991,6 +3024,7 @@ var stdlib = map[string]map[string]bool{ "NewTypeName": true, "NewVar": true, "Nil": true, + "Object": true, "ObjectString": true, "Package": true, "PkgName": true, @@ -3349,6 +3383,7 @@ var stdlib = map[string]map[string]bool{ "SetFlags": true, "SetOutput": true, "SetPrefix": true, + "Writer": true, }, "log/syslog": map[string]bool{ "Dial": true, @@ -3801,6 +3836,7 @@ var stdlib = map[string]map[string]bool{ "MethodTrace": true, "NewFileTransport": true, "NewRequest": true, + "NewRequestWithContext": true, "NewServeMux": true, "NoBody": true, "NotFound": true, @@ -3825,6 +3861,7 @@ var stdlib = map[string]map[string]bool{ "SameSite": true, "SameSiteDefaultMode": true, "SameSiteLaxMode": true, + "SameSiteNoneMode": true, "SameSiteStrictMode": true, "Serve": true, "ServeContent": true, @@ -3846,6 +3883,7 @@ var stdlib = map[string]map[string]bool{ "StatusConflict": true, "StatusContinue": true, "StatusCreated": true, + "StatusEarlyHints": true, "StatusExpectationFailed": true, "StatusFailedDependency": true, "StatusForbidden": true, @@ -4163,6 +4201,7 @@ var stdlib = map[string]map[string]bool{ "Truncate": true, "Unsetenv": true, "UserCacheDir": true, + "UserConfigDir": true, "UserHomeDir": true, }, "os/exec": map[string]bool{ @@ -4293,6 +4332,7 @@ var stdlib = map[string]map[string]bool{ "StructOf": true, "StructTag": true, "Swapper": true, + "Type": true, "TypeOf": true, "Uint": true, "Uint16": true, @@ -4588,6 +4628,7 @@ var stdlib = map[string]map[string]bool{ "ToTitleSpecial": true, "ToUpper": true, "ToUpperSpecial": true, + "ToValidUTF8": true, "Trim": true, "TrimFunc": true, "TrimLeft": true, @@ -7989,6 +8030,7 @@ var stdlib = map[string]map[string]bool{ "Rmdir": true, "RouteMessage": true, "RouteRIB": true, + "RoutingMessage": true, "RtAttr": true, "RtGenmsg": true, "RtMetrics": true, @@ -9357,6 +9399,7 @@ var stdlib = map[string]map[string]bool{ "SlicePtrFromStrings": true, "SockFilter": true, "SockFprog": true, + "Sockaddr": true, "SockaddrDatalink": true, "SockaddrGen": true, "SockaddrInet4": true, @@ -9784,6 +9827,8 @@ var stdlib = map[string]map[string]bool{ "XP1_UNI_SEND": true, }, "syscall/js": map[string]bool{ + "CopyBytesToGo": true, + "CopyBytesToJS": true, "Error": true, "Func": true, "FuncOf": true, @@ -9798,8 +9843,6 @@ var stdlib = map[string]map[string]bool{ "TypeString": true, "TypeSymbol": true, "TypeUndefined": true, - "TypedArray": true, - "TypedArrayOf": true, "Undefined": true, "Value": true, "ValueError": true, @@ -9815,6 +9858,7 @@ var stdlib = map[string]map[string]bool{ "CoverBlock": true, "CoverMode": true, "Coverage": true, + "Init": true, "InternalBenchmark": true, "InternalExample": true, "InternalTest": true, @@ -9828,6 +9872,7 @@ var stdlib = map[string]map[string]bool{ "RunTests": true, "Short": true, "T": true, + "TB": true, "Verbose": true, }, "testing/iotest": map[string]bool{ @@ -10063,6 +10108,7 @@ var stdlib = map[string]map[string]bool{ "Devanagari": true, "Diacritic": true, "Digit": true, + "Dogra": true, "Duployan": true, "Egyptian_Hieroglyphs": true, "Elbasan": true, @@ -10077,9 +10123,11 @@ var stdlib = map[string]map[string]bool{ "GraphicRanges": true, "Greek": true, "Gujarati": true, + "Gunjala_Gondi": true, "Gurmukhi": true, "Han": true, "Hangul": true, + "Hanifi_Rohingya": true, "Hanunoo": true, "Hatran": true, "Hebrew": true, @@ -10140,6 +10188,7 @@ var stdlib = map[string]map[string]bool{ "Lydian": true, "M": true, "Mahajani": true, + "Makasar": true, "Malayalam": true, "Mandaic": true, "Manichaean": true, @@ -10152,6 +10201,7 @@ var stdlib = map[string]map[string]bool{ "MaxRune": true, "Mc": true, "Me": true, + "Medefaidrin": true, "Meetei_Mayek": true, "Mende_Kikakui": true, "Meroitic_Cursive": true, @@ -10181,6 +10231,7 @@ var stdlib = map[string]map[string]bool{ "Old_North_Arabian": true, "Old_Permic": true, "Old_Persian": true, + "Old_Sogdian": true, "Old_South_Arabian": true, "Old_Turkic": true, "Oriya": true, @@ -10241,6 +10292,7 @@ var stdlib = map[string]map[string]bool{ "Sm": true, "So": true, "Soft_Dotted": true, + "Sogdian": true, "Sora_Sompeng": true, "Soyombo": true, "Space": true, diff --git a/vendor/golang.org/x/tools/internal/span/parse.go b/vendor/golang.org/x/tools/internal/span/parse.go new file mode 100644 index 0000000000..b3f268a38a --- /dev/null +++ b/vendor/golang.org/x/tools/internal/span/parse.go @@ -0,0 +1,100 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package span + +import ( + "strconv" + "strings" + "unicode/utf8" +) + +// Parse returns the location represented by the input. +// All inputs are valid locations, as they can always be a pure filename. +// The returned span will be normalized, and thus if printed may produce a +// different string. +func Parse(input string) Span { + // :0:0#0-0:0#0 + valid := input + var hold, offset int + hadCol := false + suf := rstripSuffix(input) + if suf.sep == "#" { + offset = suf.num + suf = rstripSuffix(suf.remains) + } + if suf.sep == ":" { + valid = suf.remains + hold = suf.num + hadCol = true + suf = rstripSuffix(suf.remains) + } + switch { + case suf.sep == ":": + return New(NewURI(suf.remains), NewPoint(suf.num, hold, offset), Point{}) + case suf.sep == "-": + // we have a span, fall out of the case to continue + default: + // separator not valid, rewind to either the : or the start + return New(NewURI(valid), NewPoint(hold, 0, offset), Point{}) + } + // only the span form can get here + // at this point we still don't know what the numbers we have mean + // if have not yet seen a : then we might have either a line or a column depending + // on whether start has a column or not + // we build an end point and will fix it later if needed + end := NewPoint(suf.num, hold, offset) + hold, offset = 0, 0 + suf = rstripSuffix(suf.remains) + if suf.sep == "#" { + offset = suf.num + suf = rstripSuffix(suf.remains) + } + if suf.sep != ":" { + // turns out we don't have a span after all, rewind + return New(NewURI(valid), end, Point{}) + } + valid = suf.remains + hold = suf.num + suf = rstripSuffix(suf.remains) + if suf.sep != ":" { + // line#offset only + return New(NewURI(valid), NewPoint(hold, 0, offset), end) + } + // we have a column, so if end only had one number, it is also the column + if !hadCol { + end = NewPoint(suf.num, end.v.Line, end.v.Offset) + } + return New(NewURI(suf.remains), NewPoint(suf.num, hold, offset), end) +} + +type suffix struct { + remains string + sep string + num int +} + +func rstripSuffix(input string) suffix { + if len(input) == 0 { + return suffix{"", "", -1} + } + remains := input + num := -1 + // first see if we have a number at the end + last := strings.LastIndexFunc(remains, func(r rune) bool { return r < '0' || r > '9' }) + if last >= 0 && last < len(remains)-1 { + number, err := strconv.ParseInt(remains[last+1:], 10, 64) + if err == nil { + num = int(number) + remains = remains[:last+1] + } + } + // now see if we have a trailing separator + r, w := utf8.DecodeLastRuneInString(remains) + if r != ':' && r != '#' && r == '#' { + return suffix{input, "", -1} + } + remains = remains[:len(remains)-w] + return suffix{remains, string(r), num} +} diff --git a/vendor/golang.org/x/tools/internal/span/span.go b/vendor/golang.org/x/tools/internal/span/span.go new file mode 100644 index 0000000000..4d2ad09866 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/span/span.go @@ -0,0 +1,285 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package span contains support for representing with positions and ranges in +// text files. +package span + +import ( + "encoding/json" + "fmt" + "path" +) + +// Span represents a source code range in standardized form. +type Span struct { + v span +} + +// Point represents a single point within a file. +// In general this should only be used as part of a Span, as on its own it +// does not carry enough information. +type Point struct { + v point +} + +type span struct { + URI URI `json:"uri"` + Start point `json:"start"` + End point `json:"end"` +} + +type point struct { + Line int `json:"line"` + Column int `json:"column"` + Offset int `json:"offset"` +} + +// Invalid is a span that reports false from IsValid +var Invalid = Span{v: span{Start: invalidPoint.v, End: invalidPoint.v}} + +var invalidPoint = Point{v: point{Line: 0, Column: 0, Offset: -1}} + +// Converter is the interface to an object that can convert between line:column +// and offset forms for a single file. +type Converter interface { + //ToPosition converts from an offset to a line:column pair. + ToPosition(offset int) (int, int, error) + //ToOffset converts from a line:column pair to an offset. + ToOffset(line, col int) (int, error) +} + +func New(uri URI, start Point, end Point) Span { + s := Span{v: span{URI: uri, Start: start.v, End: end.v}} + s.v.clean() + return s +} + +func NewPoint(line, col, offset int) Point { + p := Point{v: point{Line: line, Column: col, Offset: offset}} + p.v.clean() + return p +} + +func Compare(a, b Span) int { + if r := CompareURI(a.URI(), b.URI()); r != 0 { + return r + } + if r := comparePoint(a.v.Start, b.v.Start); r != 0 { + return r + } + return comparePoint(a.v.End, b.v.End) +} + +func ComparePoint(a, b Point) int { + return comparePoint(a.v, b.v) +} + +func comparePoint(a, b point) int { + if !a.hasPosition() { + if a.Offset < b.Offset { + return -1 + } + if a.Offset > b.Offset { + return 1 + } + return 0 + } + if a.Line < b.Line { + return -1 + } + if a.Line > b.Line { + return 1 + } + if a.Column < b.Column { + return -1 + } + if a.Column > b.Column { + return 1 + } + return 0 +} + +func (s Span) HasPosition() bool { return s.v.Start.hasPosition() } +func (s Span) HasOffset() bool { return s.v.Start.hasOffset() } +func (s Span) IsValid() bool { return s.v.Start.isValid() } +func (s Span) IsPoint() bool { return s.v.Start == s.v.End } +func (s Span) URI() URI { return s.v.URI } +func (s Span) Start() Point { return Point{s.v.Start} } +func (s Span) End() Point { return Point{s.v.End} } +func (s *Span) MarshalJSON() ([]byte, error) { return json.Marshal(&s.v) } +func (s *Span) UnmarshalJSON(b []byte) error { return json.Unmarshal(b, &s.v) } + +func (p Point) HasPosition() bool { return p.v.hasPosition() } +func (p Point) HasOffset() bool { return p.v.hasOffset() } +func (p Point) IsValid() bool { return p.v.isValid() } +func (p *Point) MarshalJSON() ([]byte, error) { return json.Marshal(&p.v) } +func (p *Point) UnmarshalJSON(b []byte) error { return json.Unmarshal(b, &p.v) } +func (p Point) Line() int { + if !p.v.hasPosition() { + panic(fmt.Errorf("position not set in %v", p.v)) + } + return p.v.Line +} +func (p Point) Column() int { + if !p.v.hasPosition() { + panic(fmt.Errorf("position not set in %v", p.v)) + } + return p.v.Column +} +func (p Point) Offset() int { + if !p.v.hasOffset() { + panic(fmt.Errorf("offset not set in %v", p.v)) + } + return p.v.Offset +} + +func (p point) hasPosition() bool { return p.Line > 0 } +func (p point) hasOffset() bool { return p.Offset >= 0 } +func (p point) isValid() bool { return p.hasPosition() || p.hasOffset() } +func (p point) isZero() bool { + return (p.Line == 1 && p.Column == 1) || (!p.hasPosition() && p.Offset == 0) +} + +func (s *span) clean() { + //this presumes the points are already clean + if !s.End.isValid() || (s.End == point{}) { + s.End = s.Start + } +} + +func (p *point) clean() { + if p.Line < 0 { + p.Line = 0 + } + if p.Column <= 0 { + if p.Line > 0 { + p.Column = 1 + } else { + p.Column = 0 + } + } + if p.Offset == 0 && (p.Line > 1 || p.Column > 1) { + p.Offset = -1 + } +} + +// Format implements fmt.Formatter to print the Location in a standard form. +// The format produced is one that can be read back in using Parse. +func (s Span) Format(f fmt.State, c rune) { + fullForm := f.Flag('+') + preferOffset := f.Flag('#') + // we should always have a uri, simplify if it is file format + //TODO: make sure the end of the uri is unambiguous + uri := string(s.v.URI) + if c == 'f' { + uri = path.Base(uri) + } else if !fullForm { + uri = s.v.URI.Filename() + } + fmt.Fprint(f, uri) + if !s.IsValid() || (!fullForm && s.v.Start.isZero() && s.v.End.isZero()) { + return + } + // see which bits of start to write + printOffset := s.HasOffset() && (fullForm || preferOffset || !s.HasPosition()) + printLine := s.HasPosition() && (fullForm || !printOffset) + printColumn := printLine && (fullForm || (s.v.Start.Column > 1 || s.v.End.Column > 1)) + fmt.Fprint(f, ":") + if printLine { + fmt.Fprintf(f, "%d", s.v.Start.Line) + } + if printColumn { + fmt.Fprintf(f, ":%d", s.v.Start.Column) + } + if printOffset { + fmt.Fprintf(f, "#%d", s.v.Start.Offset) + } + // start is written, do we need end? + if s.IsPoint() { + return + } + // we don't print the line if it did not change + printLine = fullForm || (printLine && s.v.End.Line > s.v.Start.Line) + fmt.Fprint(f, "-") + if printLine { + fmt.Fprintf(f, "%d", s.v.End.Line) + } + if printColumn { + if printLine { + fmt.Fprint(f, ":") + } + fmt.Fprintf(f, "%d", s.v.End.Column) + } + if printOffset { + fmt.Fprintf(f, "#%d", s.v.End.Offset) + } +} + +func (s Span) WithPosition(c Converter) (Span, error) { + if err := s.update(c, true, false); err != nil { + return Span{}, err + } + return s, nil +} + +func (s Span) WithOffset(c Converter) (Span, error) { + if err := s.update(c, false, true); err != nil { + return Span{}, err + } + return s, nil +} + +func (s Span) WithAll(c Converter) (Span, error) { + if err := s.update(c, true, true); err != nil { + return Span{}, err + } + return s, nil +} + +func (s *Span) update(c Converter, withPos, withOffset bool) error { + if !s.IsValid() { + return fmt.Errorf("cannot add information to an invalid span") + } + if withPos && !s.HasPosition() { + if err := s.v.Start.updatePosition(c); err != nil { + return err + } + if s.v.End.Offset == s.v.Start.Offset { + s.v.End = s.v.Start + } else if err := s.v.End.updatePosition(c); err != nil { + return err + } + } + if withOffset && (!s.HasOffset() || (s.v.End.hasPosition() && !s.v.End.hasOffset())) { + if err := s.v.Start.updateOffset(c); err != nil { + return err + } + if s.v.End.Line == s.v.Start.Line && s.v.End.Column == s.v.Start.Column { + s.v.End.Offset = s.v.Start.Offset + } else if err := s.v.End.updateOffset(c); err != nil { + return err + } + } + return nil +} + +func (p *point) updatePosition(c Converter) error { + line, col, err := c.ToPosition(p.Offset) + if err != nil { + return err + } + p.Line = line + p.Column = col + return nil +} + +func (p *point) updateOffset(c Converter) error { + offset, err := c.ToOffset(p.Line, p.Column) + if err != nil { + return err + } + p.Offset = offset + return nil +} diff --git a/vendor/golang.org/x/tools/internal/span/token.go b/vendor/golang.org/x/tools/internal/span/token.go new file mode 100644 index 0000000000..ce44541b2f --- /dev/null +++ b/vendor/golang.org/x/tools/internal/span/token.go @@ -0,0 +1,151 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package span + +import ( + "fmt" + "go/token" +) + +// Range represents a source code range in token.Pos form. +// It also carries the FileSet that produced the positions, so that it is +// self contained. +type Range struct { + FileSet *token.FileSet + Start token.Pos + End token.Pos +} + +// TokenConverter is a Converter backed by a token file set and file. +// It uses the file set methods to work out the conversions, which +// makes it fast and does not require the file contents. +type TokenConverter struct { + fset *token.FileSet + file *token.File +} + +// NewRange creates a new Range from a FileSet and two positions. +// To represent a point pass a 0 as the end pos. +func NewRange(fset *token.FileSet, start, end token.Pos) Range { + return Range{ + FileSet: fset, + Start: start, + End: end, + } +} + +// NewTokenConverter returns an implementation of Converter backed by a +// token.File. +func NewTokenConverter(fset *token.FileSet, f *token.File) *TokenConverter { + return &TokenConverter{fset: fset, file: f} +} + +// NewContentConverter returns an implementation of Converter for the +// given file content. +func NewContentConverter(filename string, content []byte) *TokenConverter { + fset := token.NewFileSet() + f := fset.AddFile(filename, -1, len(content)) + f.SetLinesForContent(content) + return &TokenConverter{fset: fset, file: f} +} + +// IsPoint returns true if the range represents a single point. +func (r Range) IsPoint() bool { + return r.Start == r.End +} + +// Span converts a Range to a Span that represents the Range. +// It will fill in all the members of the Span, calculating the line and column +// information. +func (r Range) Span() (Span, error) { + f := r.FileSet.File(r.Start) + if f == nil { + return Span{}, fmt.Errorf("file not found in FileSet") + } + s := Span{v: span{URI: FileURI(f.Name())}} + var err error + s.v.Start.Offset, err = offset(f, r.Start) + if err != nil { + return Span{}, err + } + if r.End.IsValid() { + s.v.End.Offset, err = offset(f, r.End) + if err != nil { + return Span{}, err + } + } + s.v.Start.clean() + s.v.End.clean() + s.v.clean() + converter := NewTokenConverter(r.FileSet, f) + return s.WithPosition(converter) +} + +// offset is a copy of the Offset function in go/token, but with the adjustment +// that it does not panic on invalid positions. +func offset(f *token.File, pos token.Pos) (int, error) { + if int(pos) < f.Base() || int(pos) > f.Base()+f.Size() { + return 0, fmt.Errorf("invalid pos") + } + return int(pos) - f.Base(), nil +} + +// Range converts a Span to a Range that represents the Span for the supplied +// File. +func (s Span) Range(converter *TokenConverter) (Range, error) { + s, err := s.WithOffset(converter) + if err != nil { + return Range{}, err + } + // go/token will panic if the offset is larger than the file's size, + // so check here to avoid panicking. + if s.Start().Offset() > converter.file.Size() { + return Range{}, fmt.Errorf("start offset %v is past the end of the file %v", s.Start(), converter.file.Size()) + } + if s.End().Offset() > converter.file.Size() { + return Range{}, fmt.Errorf("end offset %v is past the end of the file %v", s.End(), converter.file.Size()) + } + return Range{ + FileSet: converter.fset, + Start: converter.file.Pos(s.Start().Offset()), + End: converter.file.Pos(s.End().Offset()), + }, nil +} + +func (l *TokenConverter) ToPosition(offset int) (int, int, error) { + if offset > l.file.Size() { + return 0, 0, fmt.Errorf("offset %v is past the end of the file %v", offset, l.file.Size()) + } + pos := l.file.Pos(offset) + p := l.fset.Position(pos) + if offset == l.file.Size() { + return p.Line + 1, 1, nil + } + return p.Line, p.Column, nil +} + +func (l *TokenConverter) ToOffset(line, col int) (int, error) { + if line < 0 { + return -1, fmt.Errorf("line is not valid") + } + lineMax := l.file.LineCount() + 1 + if line > lineMax { + return -1, fmt.Errorf("line is beyond end of file %v", lineMax) + } else if line == lineMax { + if col > 1 { + return -1, fmt.Errorf("column is beyond end of file") + } + // at the end of the file, allowing for a trailing eol + return l.file.Size(), nil + } + pos := lineStart(l.file, line) + if !pos.IsValid() { + return -1, fmt.Errorf("line is not in file") + } + // we assume that column is in bytes here, and that the first byte of a + // line is at column 1 + pos += token.Pos(col - 1) + return offset(l.file, pos) +} diff --git a/vendor/golang.org/x/tools/internal/span/token111.go b/vendor/golang.org/x/tools/internal/span/token111.go new file mode 100644 index 0000000000..bf7a5406b6 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/span/token111.go @@ -0,0 +1,39 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.12 + +package span + +import ( + "go/token" +) + +// lineStart is the pre-Go 1.12 version of (*token.File).LineStart. For Go +// versions <= 1.11, we borrow logic from the analysisutil package. +// TODO(rstambler): Delete this file when we no longer support Go 1.11. +func lineStart(f *token.File, line int) token.Pos { + // Use binary search to find the start offset of this line. + + min := 0 // inclusive + max := f.Size() // exclusive + for { + offset := (min + max) / 2 + pos := f.Pos(offset) + posn := f.Position(pos) + if posn.Line == line { + return pos - (token.Pos(posn.Column) - 1) + } + + if min+1 >= max { + return token.NoPos + } + + if posn.Line < line { + min = offset + } else { + max = offset + } + } +} diff --git a/vendor/golang.org/x/tools/internal/span/token112.go b/vendor/golang.org/x/tools/internal/span/token112.go new file mode 100644 index 0000000000..017aec9c13 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/span/token112.go @@ -0,0 +1,16 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.12 + +package span + +import ( + "go/token" +) + +// TODO(rstambler): Delete this file when we no longer support Go 1.11. +func lineStart(f *token.File, line int) token.Pos { + return f.LineStart(line) +} diff --git a/vendor/golang.org/x/tools/internal/span/uri.go b/vendor/golang.org/x/tools/internal/span/uri.go new file mode 100644 index 0000000000..e05a9e6ef5 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/span/uri.go @@ -0,0 +1,152 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package span + +import ( + "fmt" + "net/url" + "os" + "path" + "path/filepath" + "runtime" + "strings" + "unicode" +) + +const fileScheme = "file" + +// URI represents the full URI for a file. +type URI string + +// Filename returns the file path for the given URI. +// It is an error to call this on a URI that is not a valid filename. +func (uri URI) Filename() string { + filename, err := filename(uri) + if err != nil { + panic(err) + } + return filepath.FromSlash(filename) +} + +func filename(uri URI) (string, error) { + if uri == "" { + return "", nil + } + u, err := url.ParseRequestURI(string(uri)) + if err != nil { + return "", err + } + if u.Scheme != fileScheme { + return "", fmt.Errorf("only file URIs are supported, got %q from %q", u.Scheme, uri) + } + if isWindowsDriveURI(u.Path) { + u.Path = u.Path[1:] + } + return u.Path, nil +} + +// NewURI returns a span URI for the string. +// It will attempt to detect if the string is a file path or uri. +func NewURI(s string) URI { + if u, err := url.PathUnescape(s); err == nil { + s = u + } + if strings.HasPrefix(s, fileScheme+"://") { + return URI(s) + } + return FileURI(s) +} + +func CompareURI(a, b URI) int { + if equalURI(a, b) { + return 0 + } + if a < b { + return -1 + } + return 1 +} + +func equalURI(a, b URI) bool { + if a == b { + return true + } + // If we have the same URI basename, we may still have the same file URIs. + if !strings.EqualFold(path.Base(string(a)), path.Base(string(b))) { + return false + } + fa, err := filename(a) + if err != nil { + return false + } + fb, err := filename(b) + if err != nil { + return false + } + // Stat the files to check if they are equal. + infoa, err := os.Stat(filepath.FromSlash(fa)) + if err != nil { + return false + } + infob, err := os.Stat(filepath.FromSlash(fb)) + if err != nil { + return false + } + return os.SameFile(infoa, infob) +} + +// FileURI returns a span URI for the supplied file path. +// It will always have the file scheme. +func FileURI(path string) URI { + if path == "" { + return "" + } + // Handle standard library paths that contain the literal "$GOROOT". + // TODO(rstambler): The go/packages API should allow one to determine a user's $GOROOT. + const prefix = "$GOROOT" + if len(path) >= len(prefix) && strings.EqualFold(prefix, path[:len(prefix)]) { + suffix := path[len(prefix):] + path = runtime.GOROOT() + suffix + } + if !isWindowsDrivePath(path) { + if abs, err := filepath.Abs(path); err == nil { + path = abs + } + } + // Check the file path again, in case it became absolute. + if isWindowsDrivePath(path) { + path = "/" + path + } + path = filepath.ToSlash(path) + u := url.URL{ + Scheme: fileScheme, + Path: path, + } + uri := u.String() + if unescaped, err := url.PathUnescape(uri); err == nil { + uri = unescaped + } + return URI(uri) +} + +// isWindowsDrivePath returns true if the file path is of the form used by +// Windows. We check if the path begins with a drive letter, followed by a ":". +func isWindowsDrivePath(path string) bool { + if len(path) < 4 { + return false + } + return unicode.IsLetter(rune(path[0])) && path[1] == ':' +} + +// isWindowsDriveURI returns true if the file URI is of the format used by +// Windows URIs. The url.Parse package does not specially handle Windows paths +// (see https://golang.org/issue/6027). We check if the URI path has +// a drive prefix (e.g. "/C:"). If so, we trim the leading "/". +func isWindowsDriveURI(uri string) bool { + if len(uri) < 4 { + return false + } + return uri[0] == '/' && unicode.IsLetter(rune(uri[1])) && uri[2] == ':' +} diff --git a/vendor/golang.org/x/tools/internal/span/utf16.go b/vendor/golang.org/x/tools/internal/span/utf16.go new file mode 100644 index 0000000000..561b3fa50a --- /dev/null +++ b/vendor/golang.org/x/tools/internal/span/utf16.go @@ -0,0 +1,94 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package span + +import ( + "fmt" + "unicode/utf16" + "unicode/utf8" +) + +// ToUTF16Column calculates the utf16 column expressed by the point given the +// supplied file contents. +// This is used to convert from the native (always in bytes) column +// representation and the utf16 counts used by some editors. +func ToUTF16Column(p Point, content []byte) (int, error) { + if content == nil { + return -1, fmt.Errorf("ToUTF16Column: missing content") + } + if !p.HasPosition() { + return -1, fmt.Errorf("ToUTF16Column: point is missing position") + } + if !p.HasOffset() { + return -1, fmt.Errorf("ToUTF16Column: point is missing offset") + } + offset := p.Offset() // 0-based + colZero := p.Column() - 1 // 0-based + if colZero == 0 { + // 0-based column 0, so it must be chr 1 + return 1, nil + } else if colZero < 0 { + return -1, fmt.Errorf("ToUTF16Column: column is invalid (%v)", colZero) + } + // work out the offset at the start of the line using the column + lineOffset := offset - colZero + if lineOffset < 0 || offset > len(content) { + return -1, fmt.Errorf("ToUTF16Column: offsets %v-%v outside file contents (%v)", lineOffset, offset, len(content)) + } + // Use the offset to pick out the line start. + // This cannot panic: offset > len(content) and lineOffset < offset. + start := content[lineOffset:] + + // Now, truncate down to the supplied column. + start = start[:colZero] + + // and count the number of utf16 characters + // in theory we could do this by hand more efficiently... + return len(utf16.Encode([]rune(string(start)))) + 1, nil +} + +// FromUTF16Column advances the point by the utf16 character offset given the +// supplied line contents. +// This is used to convert from the utf16 counts used by some editors to the +// native (always in bytes) column representation. +func FromUTF16Column(p Point, chr int, content []byte) (Point, error) { + if !p.HasOffset() { + return Point{}, fmt.Errorf("FromUTF16Column: point is missing offset") + } + // if chr is 1 then no adjustment needed + if chr <= 1 { + return p, nil + } + if p.Offset() >= len(content) { + return p, fmt.Errorf("FromUTF16Column: offset (%v) greater than length of content (%v)", p.Offset(), len(content)) + } + remains := content[p.Offset():] + // scan forward the specified number of characters + for count := 1; count < chr; count++ { + if len(remains) <= 0 { + return Point{}, fmt.Errorf("FromUTF16Column: chr goes beyond the content") + } + r, w := utf8.DecodeRune(remains) + if r == '\n' { + // Per the LSP spec: + // + // > If the character value is greater than the line length it + // > defaults back to the line length. + break + } + remains = remains[w:] + if r >= 0x10000 { + // a two point rune + count++ + // if we finished in a two point rune, do not advance past the first + if count >= chr { + break + } + } + p.v.Column += w + p.v.Offset += w + } + return p, nil +} diff --git a/vendor/gopkg.in/imdario/mergo.v0/.gitignore b/vendor/gopkg.in/imdario/mergo.v0/.gitignore new file mode 100644 index 0000000000..529c3412ba --- /dev/null +++ b/vendor/gopkg.in/imdario/mergo.v0/.gitignore @@ -0,0 +1,33 @@ +#### joe made this: http://goel.io/joe + +#### go #### +# Binaries for programs and plugins +*.exe +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 +.glide/ + +#### vim #### +# Swap +[._]*.s[a-v][a-z] +[._]*.sw[a-p] +[._]s[a-v][a-z] +[._]sw[a-p] + +# Session +Session.vim + +# Temporary +.netrwhist +*~ +# Auto-generated tag files +tags diff --git a/vendor/gopkg.in/imdario/mergo.v0/.travis.yml b/vendor/gopkg.in/imdario/mergo.v0/.travis.yml new file mode 100644 index 0000000000..b13a50ed1f --- /dev/null +++ b/vendor/gopkg.in/imdario/mergo.v0/.travis.yml @@ -0,0 +1,7 @@ +language: go +install: + - go get -t + - go get golang.org/x/tools/cmd/cover + - go get github.com/mattn/goveralls +script: + - $HOME/gopath/bin/goveralls -service=travis-ci -repotoken $COVERALLS_TOKEN diff --git a/vendor/gopkg.in/imdario/mergo.v0/CODE_OF_CONDUCT.md b/vendor/gopkg.in/imdario/mergo.v0/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000..469b44907a --- /dev/null +++ b/vendor/gopkg.in/imdario/mergo.v0/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at i@dario.im. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/vendor/gopkg.in/imdario/mergo.v0/LICENSE b/vendor/gopkg.in/imdario/mergo.v0/LICENSE new file mode 100644 index 0000000000..686680298d --- /dev/null +++ b/vendor/gopkg.in/imdario/mergo.v0/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2013 Dario Castañé. All rights reserved. +Copyright (c) 2012 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/gopkg.in/imdario/mergo.v0/README.md b/vendor/gopkg.in/imdario/mergo.v0/README.md new file mode 100644 index 0000000000..02fc81e062 --- /dev/null +++ b/vendor/gopkg.in/imdario/mergo.v0/README.md @@ -0,0 +1,238 @@ +# Mergo + +A helper to merge structs and maps in Golang. Useful for configuration default values, avoiding messy if-statements. + +Also a lovely [comune](http://en.wikipedia.org/wiki/Mergo) (municipality) in the Province of Ancona in the Italian region of Marche. + +## Status + +It is ready for production use. [It is used in several projects by Docker, Google, The Linux Foundation, VMWare, Shopify, etc](https://github.com/imdario/mergo#mergo-in-the-wild). + +[![GoDoc][3]][4] +[![GoCard][5]][6] +[![Build Status][1]][2] +[![Coverage Status][7]][8] +[![Sourcegraph][9]][10] +[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fimdario%2Fmergo.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fimdario%2Fmergo?ref=badge_shield) + +[1]: https://travis-ci.org/imdario/mergo.png +[2]: https://travis-ci.org/imdario/mergo +[3]: https://godoc.org/github.com/imdario/mergo?status.svg +[4]: https://godoc.org/github.com/imdario/mergo +[5]: https://goreportcard.com/badge/imdario/mergo +[6]: https://goreportcard.com/report/github.com/imdario/mergo +[7]: https://coveralls.io/repos/github/imdario/mergo/badge.svg?branch=master +[8]: https://coveralls.io/github/imdario/mergo?branch=master +[9]: https://sourcegraph.com/github.com/imdario/mergo/-/badge.svg +[10]: https://sourcegraph.com/github.com/imdario/mergo?badge + +### Latest release + +[Release v0.3.7](https://github.com/imdario/mergo/releases/tag/v0.3.7). + +### Important note + +Please keep in mind that in [0.3.2](//github.com/imdario/mergo/releases/tag/0.3.2) Mergo changed `Merge()`and `Map()` signatures to support [transformers](#transformers). An optional/variadic argument has been added, so it won't break existing code. + +If you were using Mergo **before** April 6th 2015, please check your project works as intended after updating your local copy with ```go get -u github.com/imdario/mergo```. I apologize for any issue caused by its previous behavior and any future bug that Mergo could cause (I hope it won't!) in existing projects after the change (release 0.2.0). + +### Donations + +If Mergo is useful to you, consider buying me a coffee, a beer or making a monthly donation so I can keep building great free software. :heart_eyes: + +Buy Me a Coffee at ko-fi.com +[![Beerpay](https://beerpay.io/imdario/mergo/badge.svg)](https://beerpay.io/imdario/mergo) +[![Beerpay](https://beerpay.io/imdario/mergo/make-wish.svg)](https://beerpay.io/imdario/mergo) +Donate using Liberapay + +### Mergo in the wild + +- [moby/moby](https://github.com/moby/moby) +- [kubernetes/kubernetes](https://github.com/kubernetes/kubernetes) +- [vmware/dispatch](https://github.com/vmware/dispatch) +- [Shopify/themekit](https://github.com/Shopify/themekit) +- [imdario/zas](https://github.com/imdario/zas) +- [matcornic/hermes](https://github.com/matcornic/hermes) +- [OpenBazaar/openbazaar-go](https://github.com/OpenBazaar/openbazaar-go) +- [kataras/iris](https://github.com/kataras/iris) +- [michaelsauter/crane](https://github.com/michaelsauter/crane) +- [go-task/task](https://github.com/go-task/task) +- [sensu/uchiwa](https://github.com/sensu/uchiwa) +- [ory/hydra](https://github.com/ory/hydra) +- [sisatech/vcli](https://github.com/sisatech/vcli) +- [dairycart/dairycart](https://github.com/dairycart/dairycart) +- [projectcalico/felix](https://github.com/projectcalico/felix) +- [resin-os/balena](https://github.com/resin-os/balena) +- [go-kivik/kivik](https://github.com/go-kivik/kivik) +- [Telefonica/govice](https://github.com/Telefonica/govice) +- [supergiant/supergiant](supergiant/supergiant) +- [SergeyTsalkov/brooce](https://github.com/SergeyTsalkov/brooce) +- [soniah/dnsmadeeasy](https://github.com/soniah/dnsmadeeasy) +- [ohsu-comp-bio/funnel](https://github.com/ohsu-comp-bio/funnel) +- [EagerIO/Stout](https://github.com/EagerIO/Stout) +- [lynndylanhurley/defsynth-api](https://github.com/lynndylanhurley/defsynth-api) +- [russross/canvasassignments](https://github.com/russross/canvasassignments) +- [rdegges/cryptly-api](https://github.com/rdegges/cryptly-api) +- [casualjim/exeggutor](https://github.com/casualjim/exeggutor) +- [divshot/gitling](https://github.com/divshot/gitling) +- [RWJMurphy/gorl](https://github.com/RWJMurphy/gorl) +- [andrerocker/deploy42](https://github.com/andrerocker/deploy42) +- [elwinar/rambler](https://github.com/elwinar/rambler) +- [tmaiaroto/gopartman](https://github.com/tmaiaroto/gopartman) +- [jfbus/impressionist](https://github.com/jfbus/impressionist) +- [Jmeyering/zealot](https://github.com/Jmeyering/zealot) +- [godep-migrator/rigger-host](https://github.com/godep-migrator/rigger-host) +- [Dronevery/MultiwaySwitch-Go](https://github.com/Dronevery/MultiwaySwitch-Go) +- [thoas/picfit](https://github.com/thoas/picfit) +- [mantasmatelis/whooplist-server](https://github.com/mantasmatelis/whooplist-server) +- [jnuthong/item_search](https://github.com/jnuthong/item_search) +- [bukalapak/snowboard](https://github.com/bukalapak/snowboard) + +## Installation + + go get github.com/imdario/mergo + + // use in your .go code + import ( + "github.com/imdario/mergo" + ) + +## Usage + +You can only merge same-type structs with exported fields initialized as zero value of their type and same-types maps. Mergo won't merge unexported (private) fields but will do recursively any exported one. It won't merge empty structs value as [they are not considered zero values](https://golang.org/ref/spec#The_zero_value) either. Also maps will be merged recursively except for structs inside maps (because they are not addressable using Go reflection). + +```go +if err := mergo.Merge(&dst, src); err != nil { + // ... +} +``` + +Also, you can merge overwriting values using the transformer `WithOverride`. + +```go +if err := mergo.Merge(&dst, src, mergo.WithOverride); err != nil { + // ... +} +``` + +Additionally, you can map a `map[string]interface{}` to a struct (and otherwise, from struct to map), following the same restrictions as in `Merge()`. Keys are capitalized to find each corresponding exported field. + +```go +if err := mergo.Map(&dst, srcMap); err != nil { + // ... +} +``` + +Warning: if you map a struct to map, it won't do it recursively. Don't expect Mergo to map struct members of your struct as `map[string]interface{}`. They will be just assigned as values. + +More information and examples in [godoc documentation](http://godoc.org/github.com/imdario/mergo). + +### Nice example + +```go +package main + +import ( + "fmt" + "github.com/imdario/mergo" +) + +type Foo struct { + A string + B int64 +} + +func main() { + src := Foo{ + A: "one", + B: 2, + } + dest := Foo{ + A: "two", + } + mergo.Merge(&dest, src) + fmt.Println(dest) + // Will print + // {two 2} +} +``` + +Note: if test are failing due missing package, please execute: + + go get gopkg.in/yaml.v2 + +### Transformers + +Transformers allow to merge specific types differently than in the default behavior. In other words, now you can customize how some types are merged. For example, `time.Time` is a struct; it doesn't have zero value but IsZero can return true because it has fields with zero value. How can we merge a non-zero `time.Time`? + +```go +package main + +import ( + "fmt" + "github.com/imdario/mergo" + "reflect" + "time" +) + +type timeTransfomer struct { +} + +func (t timeTransfomer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error { + if typ == reflect.TypeOf(time.Time{}) { + return func(dst, src reflect.Value) error { + if dst.CanSet() { + isZero := dst.MethodByName("IsZero") + result := isZero.Call([]reflect.Value{}) + if result[0].Bool() { + dst.Set(src) + } + } + return nil + } + } + return nil +} + +type Snapshot struct { + Time time.Time + // ... +} + +func main() { + src := Snapshot{time.Now()} + dest := Snapshot{} + mergo.Merge(&dest, src, mergo.WithTransformers(timeTransfomer{})) + fmt.Println(dest) + // Will print + // { 2018-01-12 01:15:00 +0000 UTC m=+0.000000001 } +} +``` + + +## Contact me + +If I can help you, you have an idea or you are using Mergo in your projects, don't hesitate to drop me a line (or a pull request): [@im_dario](https://twitter.com/im_dario) + +## About + +Written by [Dario Castañé](http://dario.im). + +## Top Contributors + +[![0](https://sourcerer.io/fame/imdario/imdario/mergo/images/0)](https://sourcerer.io/fame/imdario/imdario/mergo/links/0) +[![1](https://sourcerer.io/fame/imdario/imdario/mergo/images/1)](https://sourcerer.io/fame/imdario/imdario/mergo/links/1) +[![2](https://sourcerer.io/fame/imdario/imdario/mergo/images/2)](https://sourcerer.io/fame/imdario/imdario/mergo/links/2) +[![3](https://sourcerer.io/fame/imdario/imdario/mergo/images/3)](https://sourcerer.io/fame/imdario/imdario/mergo/links/3) +[![4](https://sourcerer.io/fame/imdario/imdario/mergo/images/4)](https://sourcerer.io/fame/imdario/imdario/mergo/links/4) +[![5](https://sourcerer.io/fame/imdario/imdario/mergo/images/5)](https://sourcerer.io/fame/imdario/imdario/mergo/links/5) +[![6](https://sourcerer.io/fame/imdario/imdario/mergo/images/6)](https://sourcerer.io/fame/imdario/imdario/mergo/links/6) +[![7](https://sourcerer.io/fame/imdario/imdario/mergo/images/7)](https://sourcerer.io/fame/imdario/imdario/mergo/links/7) + + +## License + +[BSD 3-Clause](http://opensource.org/licenses/BSD-3-Clause) license, as [Go language](http://golang.org/LICENSE). + + +[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fimdario%2Fmergo.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fimdario%2Fmergo?ref=badge_large) diff --git a/vendor/gopkg.in/imdario/mergo.v0/doc.go b/vendor/gopkg.in/imdario/mergo.v0/doc.go new file mode 100644 index 0000000000..6e9aa7baf3 --- /dev/null +++ b/vendor/gopkg.in/imdario/mergo.v0/doc.go @@ -0,0 +1,44 @@ +// Copyright 2013 Dario Castañé. All rights reserved. +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package mergo merges same-type structs and maps by setting default values in zero-value fields. + +Mergo won't merge unexported (private) fields but will do recursively any exported one. It also won't merge structs inside maps (because they are not addressable using Go reflection). + +Usage + +From my own work-in-progress project: + + type networkConfig struct { + Protocol string + Address string + ServerType string `json: "server_type"` + Port uint16 + } + + type FssnConfig struct { + Network networkConfig + } + + var fssnDefault = FssnConfig { + networkConfig { + "tcp", + "127.0.0.1", + "http", + 31560, + }, + } + + // Inside a function [...] + + if err := mergo.Merge(&config, fssnDefault); err != nil { + log.Fatal(err) + } + + // More code [...] + +*/ +package mergo diff --git a/vendor/gopkg.in/imdario/mergo.v0/map.go b/vendor/gopkg.in/imdario/mergo.v0/map.go new file mode 100644 index 0000000000..3f5afa83a1 --- /dev/null +++ b/vendor/gopkg.in/imdario/mergo.v0/map.go @@ -0,0 +1,175 @@ +// Copyright 2014 Dario Castañé. All rights reserved. +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Based on src/pkg/reflect/deepequal.go from official +// golang's stdlib. + +package mergo + +import ( + "fmt" + "reflect" + "unicode" + "unicode/utf8" +) + +func changeInitialCase(s string, mapper func(rune) rune) string { + if s == "" { + return s + } + r, n := utf8.DecodeRuneInString(s) + return string(mapper(r)) + s[n:] +} + +func isExported(field reflect.StructField) bool { + r, _ := utf8.DecodeRuneInString(field.Name) + return r >= 'A' && r <= 'Z' +} + +// Traverses recursively both values, assigning src's fields values to dst. +// The map argument tracks comparisons that have already been seen, which allows +// short circuiting on recursive types. +func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) { + overwrite := config.Overwrite + if dst.CanAddr() { + addr := dst.UnsafeAddr() + h := 17 * addr + seen := visited[h] + typ := dst.Type() + for p := seen; p != nil; p = p.next { + if p.ptr == addr && p.typ == typ { + return nil + } + } + // Remember, remember... + visited[h] = &visit{addr, typ, seen} + } + zeroValue := reflect.Value{} + switch dst.Kind() { + case reflect.Map: + dstMap := dst.Interface().(map[string]interface{}) + for i, n := 0, src.NumField(); i < n; i++ { + srcType := src.Type() + field := srcType.Field(i) + if !isExported(field) { + continue + } + fieldName := field.Name + fieldName = changeInitialCase(fieldName, unicode.ToLower) + if v, ok := dstMap[fieldName]; !ok || (isEmptyValue(reflect.ValueOf(v)) || overwrite) { + dstMap[fieldName] = src.Field(i).Interface() + } + } + case reflect.Ptr: + if dst.IsNil() { + v := reflect.New(dst.Type().Elem()) + dst.Set(v) + } + dst = dst.Elem() + fallthrough + case reflect.Struct: + srcMap := src.Interface().(map[string]interface{}) + for key := range srcMap { + config.overwriteWithEmptyValue = true + srcValue := srcMap[key] + fieldName := changeInitialCase(key, unicode.ToUpper) + dstElement := dst.FieldByName(fieldName) + if dstElement == zeroValue { + // We discard it because the field doesn't exist. + continue + } + srcElement := reflect.ValueOf(srcValue) + dstKind := dstElement.Kind() + srcKind := srcElement.Kind() + if srcKind == reflect.Ptr && dstKind != reflect.Ptr { + srcElement = srcElement.Elem() + srcKind = reflect.TypeOf(srcElement.Interface()).Kind() + } else if dstKind == reflect.Ptr { + // Can this work? I guess it can't. + if srcKind != reflect.Ptr && srcElement.CanAddr() { + srcPtr := srcElement.Addr() + srcElement = reflect.ValueOf(srcPtr) + srcKind = reflect.Ptr + } + } + + if !srcElement.IsValid() { + continue + } + if srcKind == dstKind { + if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil { + return + } + } else if dstKind == reflect.Interface && dstElement.Kind() == reflect.Interface { + if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil { + return + } + } else if srcKind == reflect.Map { + if err = deepMap(dstElement, srcElement, visited, depth+1, config); err != nil { + return + } + } else { + return fmt.Errorf("type mismatch on %s field: found %v, expected %v", fieldName, srcKind, dstKind) + } + } + } + return +} + +// Map sets fields' values in dst from src. +// src can be a map with string keys or a struct. dst must be the opposite: +// if src is a map, dst must be a valid pointer to struct. If src is a struct, +// dst must be map[string]interface{}. +// It won't merge unexported (private) fields and will do recursively +// any exported field. +// If dst is a map, keys will be src fields' names in lower camel case. +// Missing key in src that doesn't match a field in dst will be skipped. This +// doesn't apply if dst is a map. +// This is separated method from Merge because it is cleaner and it keeps sane +// semantics: merging equal types, mapping different (restricted) types. +func Map(dst, src interface{}, opts ...func(*Config)) error { + return _map(dst, src, opts...) +} + +// MapWithOverwrite will do the same as Map except that non-empty dst attributes will be overridden by +// non-empty src attribute values. +// Deprecated: Use Map(…) with WithOverride +func MapWithOverwrite(dst, src interface{}, opts ...func(*Config)) error { + return _map(dst, src, append(opts, WithOverride)...) +} + +func _map(dst, src interface{}, opts ...func(*Config)) error { + var ( + vDst, vSrc reflect.Value + err error + ) + config := &Config{} + + for _, opt := range opts { + opt(config) + } + + if vDst, vSrc, err = resolveValues(dst, src); err != nil { + return err + } + // To be friction-less, we redirect equal-type arguments + // to deepMerge. Only because arguments can be anything. + if vSrc.Kind() == vDst.Kind() { + return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config) + } + switch vSrc.Kind() { + case reflect.Struct: + if vDst.Kind() != reflect.Map { + return ErrExpectedMapAsDestination + } + case reflect.Map: + if vDst.Kind() != reflect.Struct { + return ErrExpectedStructAsDestination + } + default: + return ErrNotSupported + } + return deepMap(vDst, vSrc, make(map[uintptr]*visit), 0, config) +} diff --git a/vendor/gopkg.in/imdario/mergo.v0/merge.go b/vendor/gopkg.in/imdario/mergo.v0/merge.go new file mode 100644 index 0000000000..f8de6c5430 --- /dev/null +++ b/vendor/gopkg.in/imdario/mergo.v0/merge.go @@ -0,0 +1,255 @@ +// Copyright 2013 Dario Castañé. All rights reserved. +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Based on src/pkg/reflect/deepequal.go from official +// golang's stdlib. + +package mergo + +import ( + "fmt" + "reflect" +) + +func hasExportedField(dst reflect.Value) (exported bool) { + for i, n := 0, dst.NumField(); i < n; i++ { + field := dst.Type().Field(i) + if field.Anonymous && dst.Field(i).Kind() == reflect.Struct { + exported = exported || hasExportedField(dst.Field(i)) + } else { + exported = exported || len(field.PkgPath) == 0 + } + } + return +} + +type Config struct { + Overwrite bool + AppendSlice bool + Transformers Transformers + overwriteWithEmptyValue bool +} + +type Transformers interface { + Transformer(reflect.Type) func(dst, src reflect.Value) error +} + +// Traverses recursively both values, assigning src's fields values to dst. +// The map argument tracks comparisons that have already been seen, which allows +// short circuiting on recursive types. +func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) { + overwrite := config.Overwrite + overwriteWithEmptySrc := config.overwriteWithEmptyValue + config.overwriteWithEmptyValue = false + + if !src.IsValid() { + return + } + if dst.CanAddr() { + addr := dst.UnsafeAddr() + h := 17 * addr + seen := visited[h] + typ := dst.Type() + for p := seen; p != nil; p = p.next { + if p.ptr == addr && p.typ == typ { + return nil + } + } + // Remember, remember... + visited[h] = &visit{addr, typ, seen} + } + + if config.Transformers != nil && !isEmptyValue(dst) { + if fn := config.Transformers.Transformer(dst.Type()); fn != nil { + err = fn(dst, src) + return + } + } + + switch dst.Kind() { + case reflect.Struct: + if hasExportedField(dst) { + for i, n := 0, dst.NumField(); i < n; i++ { + if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, config); err != nil { + return + } + } + } else { + if dst.CanSet() && (!isEmptyValue(src) || overwriteWithEmptySrc) && (overwrite || isEmptyValue(dst)) { + dst.Set(src) + } + } + case reflect.Map: + if dst.IsNil() && !src.IsNil() { + dst.Set(reflect.MakeMap(dst.Type())) + } + for _, key := range src.MapKeys() { + srcElement := src.MapIndex(key) + if !srcElement.IsValid() { + continue + } + dstElement := dst.MapIndex(key) + switch srcElement.Kind() { + case reflect.Chan, reflect.Func, reflect.Map, reflect.Interface, reflect.Slice: + if srcElement.IsNil() { + continue + } + fallthrough + default: + if !srcElement.CanInterface() { + continue + } + switch reflect.TypeOf(srcElement.Interface()).Kind() { + case reflect.Struct: + fallthrough + case reflect.Ptr: + fallthrough + case reflect.Map: + srcMapElm := srcElement + dstMapElm := dstElement + if srcMapElm.CanInterface() { + srcMapElm = reflect.ValueOf(srcMapElm.Interface()) + if dstMapElm.IsValid() { + dstMapElm = reflect.ValueOf(dstMapElm.Interface()) + } + } + if err = deepMerge(dstMapElm, srcMapElm, visited, depth+1, config); err != nil { + return + } + case reflect.Slice: + srcSlice := reflect.ValueOf(srcElement.Interface()) + + var dstSlice reflect.Value + if !dstElement.IsValid() || dstElement.IsNil() { + dstSlice = reflect.MakeSlice(srcSlice.Type(), 0, srcSlice.Len()) + } else { + dstSlice = reflect.ValueOf(dstElement.Interface()) + } + + if (!isEmptyValue(src) || overwriteWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice { + dstSlice = srcSlice + } else if config.AppendSlice { + if srcSlice.Type() != dstSlice.Type() { + return fmt.Errorf("cannot append two slice with different type (%s, %s)", srcSlice.Type(), dstSlice.Type()) + } + dstSlice = reflect.AppendSlice(dstSlice, srcSlice) + } + dst.SetMapIndex(key, dstSlice) + } + } + if dstElement.IsValid() && !isEmptyValue(dstElement) && (reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Map || reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Slice) { + continue + } + + if srcElement.IsValid() && (overwrite || (!dstElement.IsValid() || isEmptyValue(dstElement))) { + if dst.IsNil() { + dst.Set(reflect.MakeMap(dst.Type())) + } + dst.SetMapIndex(key, srcElement) + } + } + case reflect.Slice: + if !dst.CanSet() { + break + } + if (!isEmptyValue(src) || overwriteWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice { + dst.Set(src) + } else if config.AppendSlice { + if src.Type() != dst.Type() { + return fmt.Errorf("cannot append two slice with different type (%s, %s)", src.Type(), dst.Type()) + } + dst.Set(reflect.AppendSlice(dst, src)) + } + case reflect.Ptr: + fallthrough + case reflect.Interface: + if src.IsNil() { + break + } + if src.Kind() != reflect.Interface { + if dst.IsNil() || overwrite { + if dst.CanSet() && (overwrite || isEmptyValue(dst)) { + dst.Set(src) + } + } else if src.Kind() == reflect.Ptr { + if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil { + return + } + } else if dst.Elem().Type() == src.Type() { + if err = deepMerge(dst.Elem(), src, visited, depth+1, config); err != nil { + return + } + } else { + return ErrDifferentArgumentsTypes + } + break + } + if dst.IsNil() || overwrite { + if dst.CanSet() && (overwrite || isEmptyValue(dst)) { + dst.Set(src) + } + } else if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil { + return + } + default: + if dst.CanSet() && (!isEmptyValue(src) || overwriteWithEmptySrc) && (overwrite || isEmptyValue(dst)) { + dst.Set(src) + } + } + return +} + +// Merge will fill any empty for value type attributes on the dst struct using corresponding +// src attributes if they themselves are not empty. dst and src must be valid same-type structs +// and dst must be a pointer to struct. +// It won't merge unexported (private) fields and will do recursively any exported field. +func Merge(dst, src interface{}, opts ...func(*Config)) error { + return merge(dst, src, opts...) +} + +// MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overriden by +// non-empty src attribute values. +// Deprecated: use Merge(…) with WithOverride +func MergeWithOverwrite(dst, src interface{}, opts ...func(*Config)) error { + return merge(dst, src, append(opts, WithOverride)...) +} + +// WithTransformers adds transformers to merge, allowing to customize the merging of some types. +func WithTransformers(transformers Transformers) func(*Config) { + return func(config *Config) { + config.Transformers = transformers + } +} + +// WithOverride will make merge override non-empty dst attributes with non-empty src attributes values. +func WithOverride(config *Config) { + config.Overwrite = true +} + +// WithAppendSlice will make merge append slices instead of overwriting it +func WithAppendSlice(config *Config) { + config.AppendSlice = true +} + +func merge(dst, src interface{}, opts ...func(*Config)) error { + var ( + vDst, vSrc reflect.Value + err error + ) + + config := &Config{} + + for _, opt := range opts { + opt(config) + } + + if vDst, vSrc, err = resolveValues(dst, src); err != nil { + return err + } + if vDst.Type() != vSrc.Type() { + return ErrDifferentArgumentsTypes + } + return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config) +} diff --git a/vendor/gopkg.in/imdario/mergo.v0/mergo.go b/vendor/gopkg.in/imdario/mergo.v0/mergo.go new file mode 100644 index 0000000000..a82fea2fdc --- /dev/null +++ b/vendor/gopkg.in/imdario/mergo.v0/mergo.go @@ -0,0 +1,97 @@ +// Copyright 2013 Dario Castañé. All rights reserved. +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Based on src/pkg/reflect/deepequal.go from official +// golang's stdlib. + +package mergo + +import ( + "errors" + "reflect" +) + +// Errors reported by Mergo when it finds invalid arguments. +var ( + ErrNilArguments = errors.New("src and dst must not be nil") + ErrDifferentArgumentsTypes = errors.New("src and dst must be of same type") + ErrNotSupported = errors.New("only structs and maps are supported") + ErrExpectedMapAsDestination = errors.New("dst was expected to be a map") + ErrExpectedStructAsDestination = errors.New("dst was expected to be a struct") +) + +// During deepMerge, must keep track of checks that are +// in progress. The comparison algorithm assumes that all +// checks in progress are true when it reencounters them. +// Visited are stored in a map indexed by 17 * a1 + a2; +type visit struct { + ptr uintptr + typ reflect.Type + next *visit +} + +// From src/pkg/encoding/json/encode.go. +func isEmptyValue(v reflect.Value) bool { + switch v.Kind() { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + return v.Len() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Interface, reflect.Ptr: + if v.IsNil() { + return true + } + return isEmptyValue(v.Elem()) + case reflect.Func: + return v.IsNil() + case reflect.Invalid: + return true + } + return false +} + +func resolveValues(dst, src interface{}) (vDst, vSrc reflect.Value, err error) { + if dst == nil || src == nil { + err = ErrNilArguments + return + } + vDst = reflect.ValueOf(dst).Elem() + if vDst.Kind() != reflect.Struct && vDst.Kind() != reflect.Map { + err = ErrNotSupported + return + } + vSrc = reflect.ValueOf(src) + // We check if vSrc is a pointer to dereference it. + if vSrc.Kind() == reflect.Ptr { + vSrc = vSrc.Elem() + } + return +} + +// Traverses recursively both values, assigning src's fields values to dst. +// The map argument tracks comparisons that have already been seen, which allows +// short circuiting on recursive types. +func deeper(dst, src reflect.Value, visited map[uintptr]*visit, depth int) (err error) { + if dst.CanAddr() { + addr := dst.UnsafeAddr() + h := 17 * addr + seen := visited[h] + typ := dst.Type() + for p := seen; p != nil; p = p.next { + if p.ptr == addr && p.typ == typ { + return nil + } + } + // Remember, remember... + visited[h] = &visit{addr, typ, seen} + } + return // TODO refactor +} diff --git a/vendor/gopkg.in/op/go-logging.v1/.travis.yml b/vendor/gopkg.in/op/go-logging.v1/.travis.yml new file mode 100644 index 0000000000..70e012b81e --- /dev/null +++ b/vendor/gopkg.in/op/go-logging.v1/.travis.yml @@ -0,0 +1,6 @@ +language: go + +go: + - 1.0 + - 1.1 + - tip diff --git a/vendor/gopkg.in/op/go-logging.v1/CONTRIBUTORS b/vendor/gopkg.in/op/go-logging.v1/CONTRIBUTORS new file mode 100644 index 0000000000..958416ef1d --- /dev/null +++ b/vendor/gopkg.in/op/go-logging.v1/CONTRIBUTORS @@ -0,0 +1,5 @@ +Alec Thomas +Guilhem Lettron +Ivan Daniluk +Nimi Wariboko Jr +Róbert Selvek diff --git a/vendor/golang.org/x/sync/LICENSE b/vendor/gopkg.in/op/go-logging.v1/LICENSE similarity index 96% rename from vendor/golang.org/x/sync/LICENSE rename to vendor/gopkg.in/op/go-logging.v1/LICENSE index 6a66aea5ea..f1f6cfcef9 100644 --- a/vendor/golang.org/x/sync/LICENSE +++ b/vendor/gopkg.in/op/go-logging.v1/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. +Copyright (c) 2013 Örjan Persson. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are diff --git a/vendor/gopkg.in/op/go-logging.v1/README.md b/vendor/gopkg.in/op/go-logging.v1/README.md new file mode 100644 index 0000000000..9999d90f5a --- /dev/null +++ b/vendor/gopkg.in/op/go-logging.v1/README.md @@ -0,0 +1,89 @@ +## Golang logging library + +[![godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/op/go-logging) [![build](https://img.shields.io/travis/op/go-logging.svg?style=flat)](https://travis-ci.org/op/go-logging) + +Package logging implements a logging infrastructure for Go. Its output format +is customizable and supports different logging backends like syslog, file and +memory. Multiple backends can be utilized with different log levels per backend +and logger. + +## Example + +Let's have a look at an [example](examples/example.go) which demonstrates most +of the features found in this library. + +[![Example Output](examples/example.png)](examples/example.go) + +```go +package main + +import ( + "os" + + "github.com/op/go-logging" +) + +var log = logging.MustGetLogger("example") + +// Example format string. Everything except the message has a custom color +// which is dependent on the log level. Many fields have a custom output +// formatting too, eg. the time returns the hour down to the milli second. +var format = logging.MustStringFormatter( + `%{color}%{time:15:04:05.000} %{shortfunc} ▶ %{level:.4s} %{id:03x}%{color:reset} %{message}`, +) + +// Password is just an example type implementing the Redactor interface. Any +// time this is logged, the Redacted() function will be called. +type Password string + +func (p Password) Redacted() interface{} { + return logging.Redact(string(p)) +} + +func main() { + // For demo purposes, create two backend for os.Stderr. + backend1 := logging.NewLogBackend(os.Stderr, "", 0) + backend2 := logging.NewLogBackend(os.Stderr, "", 0) + + // For messages written to backend2 we want to add some additional + // information to the output, including the used log level and the name of + // the function. + backend2Formatter := logging.NewBackendFormatter(backend2, format) + + // Only errors and more severe messages should be sent to backend1 + backend1Leveled := logging.AddModuleLevel(backend1) + backend1Leveled.SetLevel(logging.ERROR, "") + + // Set the backends to be used. + logging.SetBackend(backend1Leveled, backend2Formatter) + + log.Debugf("debug %s", Password("secret")) + log.Info("info") + log.Notice("notice") + log.Warning("warning") + log.Error("err") + log.Critical("crit") +} +``` + +## Installing + +### Using *go get* + + $ go get github.com/op/go-logging + +After this command *go-logging* is ready to use. Its source will be in: + + $GOPATH/src/pkg/github.com/op/go-logging + +You can use `go get -u` to update the package. + +## Documentation + +For docs, see http://godoc.org/github.com/op/go-logging or run: + + $ godoc github.com/op/go-logging + +## Additional resources + +* [wslog](https://godoc.org/github.com/cryptix/go/logging/wslog) -- exposes log messages through a WebSocket. diff --git a/vendor/gopkg.in/op/go-logging.v1/backend.go b/vendor/gopkg.in/op/go-logging.v1/backend.go new file mode 100644 index 0000000000..74d9201083 --- /dev/null +++ b/vendor/gopkg.in/op/go-logging.v1/backend.go @@ -0,0 +1,39 @@ +// Copyright 2013, Örjan Persson. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package logging + +// defaultBackend is the backend used for all logging calls. +var defaultBackend LeveledBackend + +// Backend is the interface which a log backend need to implement to be able to +// be used as a logging backend. +type Backend interface { + Log(Level, int, *Record) error +} + +// SetBackend replaces the backend currently set with the given new logging +// backend. +func SetBackend(backends ...Backend) LeveledBackend { + var backend Backend + if len(backends) == 1 { + backend = backends[0] + } else { + backend = MultiLogger(backends...) + } + + defaultBackend = AddModuleLevel(backend) + return defaultBackend +} + +// SetLevel sets the logging level for the specified module. The module +// corresponds to the string specified in GetLogger. +func SetLevel(level Level, module string) { + defaultBackend.SetLevel(level, module) +} + +// GetLevel returns the logging level for the specified module. +func GetLevel(module string) Level { + return defaultBackend.GetLevel(module) +} diff --git a/vendor/gopkg.in/op/go-logging.v1/format.go b/vendor/gopkg.in/op/go-logging.v1/format.go new file mode 100644 index 0000000000..2d6a4044f3 --- /dev/null +++ b/vendor/gopkg.in/op/go-logging.v1/format.go @@ -0,0 +1,400 @@ +// Copyright 2013, Örjan Persson. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package logging + +import ( + "bytes" + "errors" + "fmt" + "io" + "os" + "path" + "path/filepath" + "regexp" + "runtime" + "strings" + "sync" + "time" +) + +// TODO see Formatter interface in fmt/print.go +// TODO try text/template, maybe it have enough performance +// TODO other template systems? +// TODO make it possible to specify formats per backend? +type fmtVerb int + +const ( + fmtVerbTime fmtVerb = iota + fmtVerbLevel + fmtVerbID + fmtVerbPid + fmtVerbProgram + fmtVerbModule + fmtVerbMessage + fmtVerbLongfile + fmtVerbShortfile + fmtVerbLongpkg + fmtVerbShortpkg + fmtVerbLongfunc + fmtVerbShortfunc + fmtVerbCallpath + fmtVerbLevelColor + + // Keep last, there are no match for these below. + fmtVerbUnknown + fmtVerbStatic +) + +var fmtVerbs = []string{ + "time", + "level", + "id", + "pid", + "program", + "module", + "message", + "longfile", + "shortfile", + "longpkg", + "shortpkg", + "longfunc", + "shortfunc", + "callpath", + "color", +} + +const rfc3339Milli = "2006-01-02T15:04:05.999Z07:00" + +var defaultVerbsLayout = []string{ + rfc3339Milli, + "s", + "d", + "d", + "s", + "s", + "s", + "s", + "s", + "s", + "s", + "s", + "s", + "s", + "", +} + +var ( + pid = os.Getpid() + program = filepath.Base(os.Args[0]) +) + +func getFmtVerbByName(name string) fmtVerb { + for i, verb := range fmtVerbs { + if name == verb { + return fmtVerb(i) + } + } + return fmtVerbUnknown +} + +// Formatter is the required interface for a custom log record formatter. +type Formatter interface { + Format(calldepth int, r *Record, w io.Writer) error +} + +// formatter is used by all backends unless otherwise overriden. +var formatter struct { + sync.RWMutex + def Formatter +} + +func getFormatter() Formatter { + formatter.RLock() + defer formatter.RUnlock() + return formatter.def +} + +var ( + // DefaultFormatter is the default formatter used and is only the message. + DefaultFormatter = MustStringFormatter("%{message}") + + // GlogFormatter mimics the glog format + GlogFormatter = MustStringFormatter("%{level:.1s}%{time:0102 15:04:05.999999} %{pid} %{shortfile}] %{message}") +) + +// SetFormatter sets the default formatter for all new backends. A backend will +// fetch this value once it is needed to format a record. Note that backends +// will cache the formatter after the first point. For now, make sure to set +// the formatter before logging. +func SetFormatter(f Formatter) { + formatter.Lock() + defer formatter.Unlock() + formatter.def = f +} + +var formatRe = regexp.MustCompile(`%{([a-z]+)(?::(.*?[^\\]))?}`) + +type part struct { + verb fmtVerb + layout string +} + +// stringFormatter contains a list of parts which explains how to build the +// formatted string passed on to the logging backend. +type stringFormatter struct { + parts []part +} + +// NewStringFormatter returns a new Formatter which outputs the log record as a +// string based on the 'verbs' specified in the format string. +// +// The verbs: +// +// General: +// %{id} Sequence number for log message (uint64). +// %{pid} Process id (int) +// %{time} Time when log occurred (time.Time) +// %{level} Log level (Level) +// %{module} Module (string) +// %{program} Basename of os.Args[0] (string) +// %{message} Message (string) +// %{longfile} Full file name and line number: /a/b/c/d.go:23 +// %{shortfile} Final file name element and line number: d.go:23 +// %{callpath} Callpath like main.a.b.c...c "..." meaning recursive call +// %{color} ANSI color based on log level +// +// For normal types, the output can be customized by using the 'verbs' defined +// in the fmt package, eg. '%{id:04d}' to make the id output be '%04d' as the +// format string. +// +// For time.Time, use the same layout as time.Format to change the time format +// when output, eg "2006-01-02T15:04:05.999Z-07:00". +// +// For the 'color' verb, the output can be adjusted to either use bold colors, +// i.e., '%{color:bold}' or to reset the ANSI attributes, i.e., +// '%{color:reset}' Note that if you use the color verb explicitly, be sure to +// reset it or else the color state will persist past your log message. e.g., +// "%{color:bold}%{time:15:04:05} %{level:-8s}%{color:reset} %{message}" will +// just colorize the time and level, leaving the message uncolored. +// +// Colors on Windows is unfortunately not supported right now and is currently +// a no-op. +// +// There's also a couple of experimental 'verbs'. These are exposed to get +// feedback and needs a bit of tinkering. Hence, they might change in the +// future. +// +// Experimental: +// %{longpkg} Full package path, eg. github.com/go-logging +// %{shortpkg} Base package path, eg. go-logging +// %{longfunc} Full function name, eg. littleEndian.PutUint32 +// %{shortfunc} Base function name, eg. PutUint32 +// %{callpath} Call function path, eg. main.a.b.c +func NewStringFormatter(format string) (Formatter, error) { + var fmter = &stringFormatter{} + + // Find the boundaries of all %{vars} + matches := formatRe.FindAllStringSubmatchIndex(format, -1) + if matches == nil { + return nil, errors.New("logger: invalid log format: " + format) + } + + // Collect all variables and static text for the format + prev := 0 + for _, m := range matches { + start, end := m[0], m[1] + if start > prev { + fmter.add(fmtVerbStatic, format[prev:start]) + } + + name := format[m[2]:m[3]] + verb := getFmtVerbByName(name) + if verb == fmtVerbUnknown { + return nil, errors.New("logger: unknown variable: " + name) + } + + // Handle layout customizations or use the default. If this is not for the + // time or color formatting, we need to prefix with %. + layout := defaultVerbsLayout[verb] + if m[4] != -1 { + layout = format[m[4]:m[5]] + } + if verb != fmtVerbTime && verb != fmtVerbLevelColor { + layout = "%" + layout + } + + fmter.add(verb, layout) + prev = end + } + end := format[prev:] + if end != "" { + fmter.add(fmtVerbStatic, end) + } + + // Make a test run to make sure we can format it correctly. + t, err := time.Parse(time.RFC3339, "2010-02-04T21:00:57-08:00") + if err != nil { + panic(err) + } + r := &Record{ + Id: 12345, + Time: t, + Module: "logger", + Args: []interface{}{"go"}, + fmt: "hello %s", + } + if err := fmter.Format(0, r, &bytes.Buffer{}); err != nil { + return nil, err + } + + return fmter, nil +} + +// MustStringFormatter is equivalent to NewStringFormatter with a call to panic +// on error. +func MustStringFormatter(format string) Formatter { + f, err := NewStringFormatter(format) + if err != nil { + panic("Failed to initialized string formatter: " + err.Error()) + } + return f +} + +func (f *stringFormatter) add(verb fmtVerb, layout string) { + f.parts = append(f.parts, part{verb, layout}) +} + +func (f *stringFormatter) Format(calldepth int, r *Record, output io.Writer) error { + for _, part := range f.parts { + if part.verb == fmtVerbStatic { + output.Write([]byte(part.layout)) + } else if part.verb == fmtVerbTime { + output.Write([]byte(r.Time.Format(part.layout))) + } else if part.verb == fmtVerbLevelColor { + doFmtVerbLevelColor(part.layout, r.Level, output) + } else { + var v interface{} + switch part.verb { + case fmtVerbLevel: + v = r.Level + break + case fmtVerbID: + v = r.Id + break + case fmtVerbPid: + v = pid + break + case fmtVerbProgram: + v = program + break + case fmtVerbModule: + v = r.Module + break + case fmtVerbMessage: + v = r.Message() + break + case fmtVerbLongfile, fmtVerbShortfile: + _, file, line, ok := runtime.Caller(calldepth + 1) + if !ok { + file = "???" + line = 0 + } else if part.verb == fmtVerbShortfile { + file = filepath.Base(file) + } + v = fmt.Sprintf("%s:%d", file, line) + case fmtVerbLongfunc, fmtVerbShortfunc, + fmtVerbLongpkg, fmtVerbShortpkg: + // TODO cache pc + v = "???" + if pc, _, _, ok := runtime.Caller(calldepth + 1); ok { + if f := runtime.FuncForPC(pc); f != nil { + v = formatFuncName(part.verb, f.Name()) + } + } + case fmtVerbCallpath: + v = formatCallpath(calldepth + 1) + default: + panic("unhandled format part") + } + fmt.Fprintf(output, part.layout, v) + } + } + return nil +} + +// formatFuncName tries to extract certain part of the runtime formatted +// function name to some pre-defined variation. +// +// This function is known to not work properly if the package path or name +// contains a dot. +func formatFuncName(v fmtVerb, f string) string { + i := strings.LastIndex(f, "/") + j := strings.Index(f[i+1:], ".") + if j < 1 { + return "???" + } + pkg, fun := f[:i+j+1], f[i+j+2:] + switch v { + case fmtVerbLongpkg: + return pkg + case fmtVerbShortpkg: + return path.Base(pkg) + case fmtVerbLongfunc: + return fun + case fmtVerbShortfunc: + i = strings.LastIndex(fun, ".") + return fun[i+1:] + } + panic("unexpected func formatter") +} + +func formatCallpath(calldepth int) string { + v := "" + callers := make([]uintptr, 64) + n := runtime.Callers(calldepth+2, callers) + oldPc := callers[n-1] + + recursiveCall := false + for i := n - 3; i >= 0; i-- { + pc := callers[i] + if oldPc == pc { + recursiveCall = true + continue + } + oldPc = pc + if recursiveCall { + recursiveCall = false + v += ".." + } + if i < n-3 { + v += "." + } + if f := runtime.FuncForPC(pc); f != nil { + v += formatFuncName(fmtVerbShortfunc, f.Name()) + } + } + return v +} + +// backendFormatter combines a backend with a specific formatter making it +// possible to have different log formats for different backends. +type backendFormatter struct { + b Backend + f Formatter +} + +// NewBackendFormatter creates a new backend which makes all records that +// passes through it beeing formatted by the specific formatter. +func NewBackendFormatter(b Backend, f Formatter) Backend { + return &backendFormatter{b, f} +} + +// Log implements the Log function required by the Backend interface. +func (bf *backendFormatter) Log(level Level, calldepth int, r *Record) error { + // Make a shallow copy of the record and replace any formatter + r2 := *r + r2.formatter = bf.f + return bf.b.Log(level, calldepth+1, &r2) +} diff --git a/vendor/gopkg.in/op/go-logging.v1/level.go b/vendor/gopkg.in/op/go-logging.v1/level.go new file mode 100644 index 0000000000..98dd191873 --- /dev/null +++ b/vendor/gopkg.in/op/go-logging.v1/level.go @@ -0,0 +1,128 @@ +// Copyright 2013, Örjan Persson. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package logging + +import ( + "errors" + "strings" + "sync" +) + +// ErrInvalidLogLevel is used when an invalid log level has been used. +var ErrInvalidLogLevel = errors.New("logger: invalid log level") + +// Level defines all available log levels for log messages. +type Level int + +// Log levels. +const ( + CRITICAL Level = iota + ERROR + WARNING + NOTICE + INFO + DEBUG +) + +var levelNames = []string{ + "CRITICAL", + "ERROR", + "WARNING", + "NOTICE", + "INFO", + "DEBUG", +} + +// String returns the string representation of a logging level. +func (p Level) String() string { + return levelNames[p] +} + +// LogLevel returns the log level from a string representation. +func LogLevel(level string) (Level, error) { + for i, name := range levelNames { + if strings.EqualFold(name, level) { + return Level(i), nil + } + } + return ERROR, ErrInvalidLogLevel +} + +// Leveled interface is the interface required to be able to add leveled +// logging. +type Leveled interface { + GetLevel(string) Level + SetLevel(Level, string) + IsEnabledFor(Level, string) bool +} + +// LeveledBackend is a log backend with additional knobs for setting levels on +// individual modules to different levels. +type LeveledBackend interface { + Backend + Leveled +} + +type moduleLeveled struct { + levels map[string]Level + backend Backend + formatter Formatter + once sync.Once +} + +// AddModuleLevel wraps a log backend with knobs to have different log levels +// for different modules. +func AddModuleLevel(backend Backend) LeveledBackend { + var leveled LeveledBackend + var ok bool + if leveled, ok = backend.(LeveledBackend); !ok { + leveled = &moduleLeveled{ + levels: make(map[string]Level), + backend: backend, + } + } + return leveled +} + +// GetLevel returns the log level for the given module. +func (l *moduleLeveled) GetLevel(module string) Level { + level, exists := l.levels[module] + if exists == false { + level, exists = l.levels[""] + // no configuration exists, default to debug + if exists == false { + level = DEBUG + } + } + return level +} + +// SetLevel sets the log level for the given module. +func (l *moduleLeveled) SetLevel(level Level, module string) { + l.levels[module] = level +} + +// IsEnabledFor will return true if logging is enabled for the given module. +func (l *moduleLeveled) IsEnabledFor(level Level, module string) bool { + return level <= l.GetLevel(module) +} + +func (l *moduleLeveled) Log(level Level, calldepth int, rec *Record) (err error) { + if l.IsEnabledFor(level, rec.Module) { + // TODO get rid of traces of formatter here. BackendFormatter should be used. + rec.formatter = l.getFormatterAndCacheCurrent() + err = l.backend.Log(level, calldepth+1, rec) + } + return +} + +func (l *moduleLeveled) getFormatterAndCacheCurrent() Formatter { + l.once.Do(func() { + if l.formatter == nil { + l.formatter = getFormatter() + } + }) + return l.formatter +} diff --git a/vendor/gopkg.in/op/go-logging.v1/log_nix.go b/vendor/gopkg.in/op/go-logging.v1/log_nix.go new file mode 100644 index 0000000000..4ff2ab1ab8 --- /dev/null +++ b/vendor/gopkg.in/op/go-logging.v1/log_nix.go @@ -0,0 +1,109 @@ +// +build !windows + +// Copyright 2013, Örjan Persson. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package logging + +import ( + "bytes" + "fmt" + "io" + "log" +) + +type color int + +const ( + ColorBlack = iota + 30 + ColorRed + ColorGreen + ColorYellow + ColorBlue + ColorMagenta + ColorCyan + ColorWhite +) + +var ( + colors = []string{ + CRITICAL: ColorSeq(ColorMagenta), + ERROR: ColorSeq(ColorRed), + WARNING: ColorSeq(ColorYellow), + NOTICE: ColorSeq(ColorGreen), + DEBUG: ColorSeq(ColorCyan), + } + boldcolors = []string{ + CRITICAL: ColorSeqBold(ColorMagenta), + ERROR: ColorSeqBold(ColorRed), + WARNING: ColorSeqBold(ColorYellow), + NOTICE: ColorSeqBold(ColorGreen), + DEBUG: ColorSeqBold(ColorCyan), + } +) + +// LogBackend utilizes the standard log module. +type LogBackend struct { + Logger *log.Logger + Color bool + ColorConfig []string +} + +// NewLogBackend creates a new LogBackend. +func NewLogBackend(out io.Writer, prefix string, flag int) *LogBackend { + return &LogBackend{Logger: log.New(out, prefix, flag)} +} + +// Log implements the Backend interface. +func (b *LogBackend) Log(level Level, calldepth int, rec *Record) error { + if b.Color { + col := colors[level] + if len(b.ColorConfig) > int(level) && b.ColorConfig[level] != "" { + col = b.ColorConfig[level] + } + + buf := &bytes.Buffer{} + buf.Write([]byte(col)) + buf.Write([]byte(rec.Formatted(calldepth + 1))) + buf.Write([]byte("\033[0m")) + // For some reason, the Go logger arbitrarily decided "2" was the correct + // call depth... + return b.Logger.Output(calldepth+2, buf.String()) + } + + return b.Logger.Output(calldepth+2, rec.Formatted(calldepth+1)) +} + +// ConvertColors takes a list of ints representing colors for log levels and +// converts them into strings for ANSI color formatting +func ConvertColors(colors []int, bold bool) []string { + converted := []string{} + for _, i := range colors { + if bold { + converted = append(converted, ColorSeqBold(color(i))) + } else { + converted = append(converted, ColorSeq(color(i))) + } + } + + return converted +} + +func ColorSeq(color color) string { + return fmt.Sprintf("\033[%dm", int(color)) +} + +func ColorSeqBold(color color) string { + return fmt.Sprintf("\033[%d;1m", int(color)) +} + +func doFmtVerbLevelColor(layout string, level Level, output io.Writer) { + if layout == "bold" { + output.Write([]byte(boldcolors[level])) + } else if layout == "reset" { + output.Write([]byte("\033[0m")) + } else { + output.Write([]byte(colors[level])) + } +} diff --git a/vendor/gopkg.in/op/go-logging.v1/log_windows.go b/vendor/gopkg.in/op/go-logging.v1/log_windows.go new file mode 100644 index 0000000000..b8dc92c67a --- /dev/null +++ b/vendor/gopkg.in/op/go-logging.v1/log_windows.go @@ -0,0 +1,107 @@ +// +build windows +// Copyright 2013, Örjan Persson. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package logging + +import ( + "bytes" + "io" + "log" + "syscall" +) + +var ( + kernel32DLL = syscall.NewLazyDLL("kernel32.dll") + setConsoleTextAttributeProc = kernel32DLL.NewProc("SetConsoleTextAttribute") +) + +// Character attributes +// Note: +// -- The attributes are combined to produce various colors (e.g., Blue + Green will create Cyan). +// Clearing all foreground or background colors results in black; setting all creates white. +// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms682088(v=vs.85).aspx#_win32_character_attributes. +const ( + fgBlack = 0x0000 + fgBlue = 0x0001 + fgGreen = 0x0002 + fgCyan = 0x0003 + fgRed = 0x0004 + fgMagenta = 0x0005 + fgYellow = 0x0006 + fgWhite = 0x0007 + fgIntensity = 0x0008 + fgMask = 0x000F +) + +var ( + colors = []uint16{ + INFO: fgWhite, + CRITICAL: fgMagenta, + ERROR: fgRed, + WARNING: fgYellow, + NOTICE: fgGreen, + DEBUG: fgCyan, + } + boldcolors = []uint16{ + INFO: fgWhite | fgIntensity, + CRITICAL: fgMagenta | fgIntensity, + ERROR: fgRed | fgIntensity, + WARNING: fgYellow | fgIntensity, + NOTICE: fgGreen | fgIntensity, + DEBUG: fgCyan | fgIntensity, + } +) + +type file interface { + Fd() uintptr +} + +// LogBackend utilizes the standard log module. +type LogBackend struct { + Logger *log.Logger + Color bool + + // f is set to a non-nil value if the underlying writer which logs writes to + // implements the file interface. This makes us able to colorise the output. + f file +} + +// NewLogBackend creates a new LogBackend. +func NewLogBackend(out io.Writer, prefix string, flag int) *LogBackend { + b := &LogBackend{Logger: log.New(out, prefix, flag)} + + // Unfortunately, the API used only takes an io.Writer where the Windows API + // need the actual fd to change colors. + if f, ok := out.(file); ok { + b.f = f + } + + return b +} + +func (b *LogBackend) Log(level Level, calldepth int, rec *Record) error { + if b.Color && b.f != nil { + buf := &bytes.Buffer{} + setConsoleTextAttribute(b.f, colors[level]) + buf.Write([]byte(rec.Formatted(calldepth + 1))) + err := b.Logger.Output(calldepth+2, buf.String()) + setConsoleTextAttribute(b.f, fgWhite) + return err + } + return b.Logger.Output(calldepth+2, rec.Formatted(calldepth+1)) +} + +// setConsoleTextAttribute sets the attributes of characters written to the +// console screen buffer by the WriteFile or WriteConsole function. +// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms686047(v=vs.85).aspx. +func setConsoleTextAttribute(f file, attribute uint16) bool { + ok, _, _ := setConsoleTextAttributeProc.Call(f.Fd(), uintptr(attribute), 0) + return ok != 0 +} + +func doFmtVerbLevelColor(layout string, level Level, output io.Writer) { + // TODO not supported on Windows since the io.Writer here is actually a + // bytes.Buffer. +} diff --git a/vendor/gopkg.in/op/go-logging.v1/logger.go b/vendor/gopkg.in/op/go-logging.v1/logger.go new file mode 100644 index 0000000000..8e4ebab962 --- /dev/null +++ b/vendor/gopkg.in/op/go-logging.v1/logger.go @@ -0,0 +1,249 @@ +// Copyright 2013, Örjan Persson. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package logging implements a logging infrastructure for Go. It supports +// different logging backends like syslog, file and memory. Multiple backends +// can be utilized with different log levels per backend and logger. +package logging + +import ( + "bytes" + "fmt" + "log" + "os" + "strings" + "sync/atomic" + "time" +) + +// Redactor is an interface for types that may contain sensitive information +// (like passwords), which shouldn't be printed to the log. The idea was found +// in relog as part of the vitness project. +type Redactor interface { + Redacted() interface{} +} + +// Redact returns a string of * having the same length as s. +func Redact(s string) string { + return strings.Repeat("*", len(s)) +} + +var ( + // Sequence number is incremented and utilized for all log records created. + sequenceNo uint64 + + // timeNow is a customizable for testing purposes. + timeNow = time.Now +) + +// Record represents a log record and contains the timestamp when the record +// was created, an increasing id, filename and line and finally the actual +// formatted log line. +type Record struct { + Id uint64 + Time time.Time + Module string + Level Level + Args []interface{} + + // message is kept as a pointer to have shallow copies update this once + // needed. + message *string + fmt string + formatter Formatter + formatted string +} + +// Formatted returns the formatted log record string. +func (r *Record) Formatted(calldepth int) string { + if r.formatted == "" { + var buf bytes.Buffer + r.formatter.Format(calldepth+1, r, &buf) + r.formatted = buf.String() + } + return r.formatted +} + +// Message returns the log record message. +func (r *Record) Message() string { + if r.message == nil { + // Redact the arguments that implements the Redactor interface + for i, arg := range r.Args { + if redactor, ok := arg.(Redactor); ok == true { + r.Args[i] = redactor.Redacted() + } + } + msg := fmt.Sprintf(r.fmt, r.Args...) + r.message = &msg + } + return *r.message +} + +// Logger is the actual logger which creates log records based on the functions +// called and passes them to the underlying logging backend. +type Logger struct { + Module string + backend LeveledBackend + haveBackend bool + + // ExtraCallDepth can be used to add additional call depth when getting the + // calling function. This is normally used when wrapping a logger. + ExtraCalldepth int +} + +// SetBackend overrides any previously defined backend for this logger. +func (l *Logger) SetBackend(backend LeveledBackend) { + l.backend = backend + l.haveBackend = true +} + +// TODO call NewLogger and remove MustGetLogger? + +// GetLogger creates and returns a Logger object based on the module name. +func GetLogger(module string) (*Logger, error) { + return &Logger{Module: module}, nil +} + +// MustGetLogger is like GetLogger but panics if the logger can't be created. +// It simplifies safe initialization of a global logger for eg. a package. +func MustGetLogger(module string) *Logger { + logger, err := GetLogger(module) + if err != nil { + panic("logger: " + module + ": " + err.Error()) + } + return logger +} + +// Reset restores the internal state of the logging library. +func Reset() { + // TODO make a global Init() method to be less magic? or make it such that + // if there's no backends at all configured, we could use some tricks to + // automatically setup backends based if we have a TTY or not. + sequenceNo = 0 + b := SetBackend(NewLogBackend(os.Stderr, "", log.LstdFlags)) + b.SetLevel(DEBUG, "") + SetFormatter(DefaultFormatter) + timeNow = time.Now +} + +// IsEnabledFor returns true if the logger is enabled for the given level. +func (l *Logger) IsEnabledFor(level Level) bool { + return defaultBackend.IsEnabledFor(level, l.Module) +} + +func (l *Logger) log(lvl Level, format string, args ...interface{}) { + if !l.IsEnabledFor(lvl) { + return + } + + // Create the logging record and pass it in to the backend + record := &Record{ + Id: atomic.AddUint64(&sequenceNo, 1), + Time: timeNow(), + Module: l.Module, + Level: lvl, + fmt: format, + Args: args, + } + + // TODO use channels to fan out the records to all backends? + // TODO in case of errors, do something (tricky) + + // calldepth=2 brings the stack up to the caller of the level + // methods, Info(), Fatal(), etc. + // ExtraCallDepth allows this to be extended further up the stack in case we + // are wrapping these methods, eg. to expose them package level + if l.haveBackend { + l.backend.Log(lvl, 2+l.ExtraCalldepth, record) + return + } + + defaultBackend.Log(lvl, 2+l.ExtraCalldepth, record) +} + +// Fatal is equivalent to l.Critical(fmt.Sprint()) followed by a call to os.Exit(1). +func (l *Logger) Fatal(args ...interface{}) { + s := fmt.Sprint(args...) + l.log(CRITICAL, "%s", s) + os.Exit(1) +} + +// Fatalf is equivalent to l.Critical followed by a call to os.Exit(1). +func (l *Logger) Fatalf(format string, args ...interface{}) { + l.log(CRITICAL, format, args...) + os.Exit(1) +} + +// Panic is equivalent to l.Critical(fmt.Sprint()) followed by a call to panic(). +func (l *Logger) Panic(args ...interface{}) { + s := fmt.Sprint(args...) + l.log(CRITICAL, "%s", s) + panic(s) +} + +// Panicf is equivalent to l.Critical followed by a call to panic(). +func (l *Logger) Panicf(format string, args ...interface{}) { + s := fmt.Sprintf(format, args...) + l.log(CRITICAL, "%s", s) + panic(s) +} + +// Critical logs a message using CRITICAL as log level. +func (l *Logger) Critical(format string, args ...interface{}) { + l.log(CRITICAL, format, args...) +} + +// Error logs a message using ERROR as log level. +func (l *Logger) Error(format string, args ...interface{}) { + l.log(ERROR, format, args...) +} + +// Errorf logs a message using ERROR as log level. +func (l *Logger) Errorf(format string, args ...interface{}) { + l.log(ERROR, format, args...) +} + +// Warning logs a message using WARNING as log level. +func (l *Logger) Warning(format string, args ...interface{}) { + l.log(WARNING, format, args...) +} + +// Warningf logs a message using WARNING as log level. +func (l *Logger) Warningf(format string, args ...interface{}) { + l.log(WARNING, format, args...) +} + +// Notice logs a message using NOTICE as log level. +func (l *Logger) Notice(format string, args ...interface{}) { + l.log(NOTICE, format, args...) +} + +// Noticef logs a message using NOTICE as log level. +func (l *Logger) Noticef(format string, args ...interface{}) { + l.log(NOTICE, format, args...) +} + +// Info logs a message using INFO as log level. +func (l *Logger) Info(format string, args ...interface{}) { + l.log(INFO, format, args...) +} + +// Infof logs a message using INFO as log level. +func (l *Logger) Infof(format string, args ...interface{}) { + l.log(INFO, format, args...) +} + +// Debug logs a message using DEBUG as log level. +func (l *Logger) Debug(format string, args ...interface{}) { + l.log(DEBUG, format, args...) +} + +// Debugf logs a message using DEBUG as log level. +func (l *Logger) Debugf(format string, args ...interface{}) { + l.log(DEBUG, format, args...) +} + +func init() { + Reset() +} diff --git a/vendor/gopkg.in/op/go-logging.v1/memory.go b/vendor/gopkg.in/op/go-logging.v1/memory.go new file mode 100644 index 0000000000..8d5152c0ba --- /dev/null +++ b/vendor/gopkg.in/op/go-logging.v1/memory.go @@ -0,0 +1,237 @@ +// Copyright 2013, Örjan Persson. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !appengine + +package logging + +import ( + "sync" + "sync/atomic" + "time" + "unsafe" +) + +// TODO pick one of the memory backends and stick with it or share interface. + +// InitForTesting is a convenient method when using logging in a test. Once +// called, the time will be frozen to January 1, 1970 UTC. +func InitForTesting(level Level) *MemoryBackend { + Reset() + + memoryBackend := NewMemoryBackend(10240) + + leveledBackend := AddModuleLevel(memoryBackend) + leveledBackend.SetLevel(level, "") + SetBackend(leveledBackend) + + timeNow = func() time.Time { + return time.Unix(0, 0).UTC() + } + return memoryBackend +} + +// Node is a record node pointing to an optional next node. +type node struct { + next *node + Record *Record +} + +// Next returns the next record node. If there's no node available, it will +// return nil. +func (n *node) Next() *node { + return n.next +} + +// MemoryBackend is a simple memory based logging backend that will not produce +// any output but merly keep records, up to the given size, in memory. +type MemoryBackend struct { + size int32 + maxSize int32 + head, tail unsafe.Pointer +} + +// NewMemoryBackend creates a simple in-memory logging backend. +func NewMemoryBackend(size int) *MemoryBackend { + return &MemoryBackend{maxSize: int32(size)} +} + +// Log implements the Log method required by Backend. +func (b *MemoryBackend) Log(level Level, calldepth int, rec *Record) error { + var size int32 + + n := &node{Record: rec} + np := unsafe.Pointer(n) + + // Add the record to the tail. If there's no records available, tail and + // head will both be nil. When we successfully set the tail and the previous + // value was nil, it's safe to set the head to the current value too. + for { + tailp := b.tail + swapped := atomic.CompareAndSwapPointer( + &b.tail, + tailp, + np, + ) + if swapped == true { + if tailp == nil { + b.head = np + } else { + (*node)(tailp).next = n + } + size = atomic.AddInt32(&b.size, 1) + break + } + } + + // Since one record was added, we might have overflowed the list. Remove + // a record if that is the case. The size will fluctate a bit, but + // eventual consistent. + if b.maxSize > 0 && size > b.maxSize { + for { + headp := b.head + head := (*node)(b.head) + if head.next == nil { + break + } + swapped := atomic.CompareAndSwapPointer( + &b.head, + headp, + unsafe.Pointer(head.next), + ) + if swapped == true { + atomic.AddInt32(&b.size, -1) + break + } + } + } + return nil +} + +// Head returns the oldest record node kept in memory. It can be used to +// iterate over records, one by one, up to the last record. +// +// Note: new records can get added while iterating. Hence the number of records +// iterated over might be larger than the maximum size. +func (b *MemoryBackend) Head() *node { + return (*node)(b.head) +} + +type event int + +const ( + eventFlush event = iota + eventStop +) + +// ChannelMemoryBackend is very similar to the MemoryBackend, except that it +// internally utilizes a channel. +type ChannelMemoryBackend struct { + maxSize int + size int + incoming chan *Record + events chan event + mu sync.Mutex + running bool + flushWg sync.WaitGroup + stopWg sync.WaitGroup + head, tail *node +} + +// NewChannelMemoryBackend creates a simple in-memory logging backend which +// utilizes a go channel for communication. +// +// Start will automatically be called by this function. +func NewChannelMemoryBackend(size int) *ChannelMemoryBackend { + backend := &ChannelMemoryBackend{ + maxSize: size, + incoming: make(chan *Record, 1024), + events: make(chan event), + } + backend.Start() + return backend +} + +// Start launches the internal goroutine which starts processing data from the +// input channel. +func (b *ChannelMemoryBackend) Start() { + b.mu.Lock() + defer b.mu.Unlock() + + // Launch the goroutine unless it's already running. + if b.running != true { + b.running = true + b.stopWg.Add(1) + go b.process() + } +} + +func (b *ChannelMemoryBackend) process() { + defer b.stopWg.Done() + for { + select { + case rec := <-b.incoming: + b.insertRecord(rec) + case e := <-b.events: + switch e { + case eventStop: + return + case eventFlush: + for len(b.incoming) > 0 { + b.insertRecord(<-b.incoming) + } + b.flushWg.Done() + } + } + } +} + +func (b *ChannelMemoryBackend) insertRecord(rec *Record) { + prev := b.tail + b.tail = &node{Record: rec} + if prev == nil { + b.head = b.tail + } else { + prev.next = b.tail + } + + if b.maxSize > 0 && b.size >= b.maxSize { + b.head = b.head.next + } else { + b.size++ + } +} + +// Flush waits until all records in the buffered channel have been processed. +func (b *ChannelMemoryBackend) Flush() { + b.flushWg.Add(1) + b.events <- eventFlush + b.flushWg.Wait() +} + +// Stop signals the internal goroutine to exit and waits until it have. +func (b *ChannelMemoryBackend) Stop() { + b.mu.Lock() + if b.running == true { + b.running = false + b.events <- eventStop + } + b.mu.Unlock() + b.stopWg.Wait() +} + +// Log implements the Log method required by Backend. +func (b *ChannelMemoryBackend) Log(level Level, calldepth int, rec *Record) error { + b.incoming <- rec + return nil +} + +// Head returns the oldest record node kept in memory. It can be used to +// iterate over records, one by one, up to the last record. +// +// Note: new records can get added while iterating. Hence the number of records +// iterated over might be larger than the maximum size. +func (b *ChannelMemoryBackend) Head() *node { + return b.head +} diff --git a/vendor/gopkg.in/op/go-logging.v1/multi.go b/vendor/gopkg.in/op/go-logging.v1/multi.go new file mode 100644 index 0000000000..3731653e68 --- /dev/null +++ b/vendor/gopkg.in/op/go-logging.v1/multi.go @@ -0,0 +1,65 @@ +// Copyright 2013, Örjan Persson. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package logging + +// TODO remove Level stuff from the multi logger. Do one thing. + +// multiLogger is a log multiplexer which can be used to utilize multiple log +// backends at once. +type multiLogger struct { + backends []LeveledBackend +} + +// MultiLogger creates a logger which contain multiple loggers. +func MultiLogger(backends ...Backend) LeveledBackend { + var leveledBackends []LeveledBackend + for _, backend := range backends { + leveledBackends = append(leveledBackends, AddModuleLevel(backend)) + } + return &multiLogger{leveledBackends} +} + +// Log passes the log record to all backends. +func (b *multiLogger) Log(level Level, calldepth int, rec *Record) (err error) { + for _, backend := range b.backends { + if backend.IsEnabledFor(level, rec.Module) { + // Shallow copy of the record for the formatted cache on Record and get the + // record formatter from the backend. + r2 := *rec + if e := backend.Log(level, calldepth+1, &r2); e != nil { + err = e + } + } + } + return +} + +// GetLevel returns the highest level enabled by all backends. +func (b *multiLogger) GetLevel(module string) Level { + var level Level + for _, backend := range b.backends { + if backendLevel := backend.GetLevel(module); backendLevel > level { + level = backendLevel + } + } + return level +} + +// SetLevel propagates the same level to all backends. +func (b *multiLogger) SetLevel(level Level, module string) { + for _, backend := range b.backends { + backend.SetLevel(level, module) + } +} + +// IsEnabledFor returns true if any of the backends are enabled for it. +func (b *multiLogger) IsEnabledFor(level Level, module string) bool { + for _, backend := range b.backends { + if backend.IsEnabledFor(level, module) { + return true + } + } + return false +} diff --git a/vendor/gopkg.in/op/go-logging.v1/syslog.go b/vendor/gopkg.in/op/go-logging.v1/syslog.go new file mode 100644 index 0000000000..4faa531709 --- /dev/null +++ b/vendor/gopkg.in/op/go-logging.v1/syslog.go @@ -0,0 +1,53 @@ +// Copyright 2013, Örjan Persson. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//+build !windows,!plan9 + +package logging + +import "log/syslog" + +// SyslogBackend is a simple logger to syslog backend. It automatically maps +// the internal log levels to appropriate syslog log levels. +type SyslogBackend struct { + Writer *syslog.Writer +} + +// NewSyslogBackend connects to the syslog daemon using UNIX sockets with the +// given prefix. If prefix is not given, the prefix will be derived from the +// launched command. +func NewSyslogBackend(prefix string) (b *SyslogBackend, err error) { + var w *syslog.Writer + w, err = syslog.New(syslog.LOG_CRIT, prefix) + return &SyslogBackend{w}, err +} + +// NewSyslogBackendPriority is the same as NewSyslogBackend, but with custom +// syslog priority, like syslog.LOG_LOCAL3|syslog.LOG_DEBUG etc. +func NewSyslogBackendPriority(prefix string, priority syslog.Priority) (b *SyslogBackend, err error) { + var w *syslog.Writer + w, err = syslog.New(priority, prefix) + return &SyslogBackend{w}, err +} + +// Log implements the Backend interface. +func (b *SyslogBackend) Log(level Level, calldepth int, rec *Record) error { + line := rec.Formatted(calldepth + 1) + switch level { + case CRITICAL: + return b.Writer.Crit(line) + case ERROR: + return b.Writer.Err(line) + case WARNING: + return b.Writer.Warning(line) + case NOTICE: + return b.Writer.Notice(line) + case INFO: + return b.Writer.Info(line) + case DEBUG: + return b.Writer.Debug(line) + default: + } + panic("unhandled log level") +} diff --git a/vendor/gopkg.in/op/go-logging.v1/syslog_fallback.go b/vendor/gopkg.in/op/go-logging.v1/syslog_fallback.go new file mode 100644 index 0000000000..91bc18de63 --- /dev/null +++ b/vendor/gopkg.in/op/go-logging.v1/syslog_fallback.go @@ -0,0 +1,28 @@ +// Copyright 2013, Örjan Persson. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//+build windows plan9 + +package logging + +import ( + "fmt" +) + +type Priority int + +type SyslogBackend struct { +} + +func NewSyslogBackend(prefix string) (b *SyslogBackend, err error) { + return nil, fmt.Errorf("Platform does not support syslog") +} + +func NewSyslogBackendPriority(prefix string, priority Priority) (b *SyslogBackend, err error) { + return nil, fmt.Errorf("Platform does not support syslog") +} + +func (b *SyslogBackend) Log(level Level, calldepth int, rec *Record) error { + return fmt.Errorf("Platform does not support syslog") +} diff --git a/vendor/gopkg.in/yaml.v3/.travis.yml b/vendor/gopkg.in/yaml.v3/.travis.yml new file mode 100644 index 0000000000..1bc5c1cd20 --- /dev/null +++ b/vendor/gopkg.in/yaml.v3/.travis.yml @@ -0,0 +1,15 @@ +language: go + +go: + - "1.4" + - "1.5" + - "1.6" + - "1.7" + - "1.8" + - "1.9" + - "1.10" + - "1.11" + - "1.12" + - tip + +go_import_path: gopkg.in/yaml.v3 diff --git a/vendor/gopkg.in/yaml.v3/LICENSE b/vendor/gopkg.in/yaml.v3/LICENSE new file mode 100644 index 0000000000..2683e4bb1f --- /dev/null +++ b/vendor/gopkg.in/yaml.v3/LICENSE @@ -0,0 +1,50 @@ + +This project is covered by two different licenses: MIT and Apache. + +#### MIT License #### + +The following files were ported to Go from C files of libyaml, and thus +are still covered by their original MIT license, with the additional +copyright staring in 2011 when the project was ported over: + + apic.go emitterc.go parserc.go readerc.go scannerc.go + writerc.go yamlh.go yamlprivateh.go + +Copyright (c) 2006-2010 Kirill Simonov +Copyright (c) 2006-2011 Kirill Simonov + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +### Apache License ### + +All the remaining project files are covered by the Apache license: + +Copyright (c) 2011-2019 Canonical Ltd + +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. diff --git a/vendor/gopkg.in/yaml.v3/NOTICE b/vendor/gopkg.in/yaml.v3/NOTICE new file mode 100644 index 0000000000..866d74a7ad --- /dev/null +++ b/vendor/gopkg.in/yaml.v3/NOTICE @@ -0,0 +1,13 @@ +Copyright 2011-2016 Canonical Ltd. + +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. diff --git a/vendor/gopkg.in/yaml.v3/README.md b/vendor/gopkg.in/yaml.v3/README.md new file mode 100644 index 0000000000..08eb1babdd --- /dev/null +++ b/vendor/gopkg.in/yaml.v3/README.md @@ -0,0 +1,150 @@ +# YAML support for the Go language + +Introduction +------------ + +The yaml package enables Go programs to comfortably encode and decode YAML +values. It was developed within [Canonical](https://www.canonical.com) as +part of the [juju](https://juju.ubuntu.com) project, and is based on a +pure Go port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML) +C library to parse and generate YAML data quickly and reliably. + +Compatibility +------------- + +The yaml package supports most of YAML 1.2, but preserves some behavior +from 1.1 for backwards compatibility. + +Specifically, as of v3 of the yaml package: + + - YAML 1.1 bools (_yes/no, on/off_) are supported as long as they are being + decoded into a typed bool value. Otherwise they behave as a string. Booleans + in YAML 1.2 are _true/false_ only. + - Octals encode and decode as _0777_ per YAML 1.1, rather than _0o777_ + as specified in YAML 1.2, because most parsers still use the old format. + Octals in the _0o777_ format are supported though, so new files work. + - Does not support base-60 floats. These are gone from YAML 1.2, and were + actually never supported by this package as it's clearly a poor choice. + +and offers backwards +compatibility with YAML 1.1 in some cases. +1.2, including support for +anchors, tags, map merging, etc. Multi-document unmarshalling is not yet +implemented, and base-60 floats from YAML 1.1 are purposefully not +supported since they're a poor design and are gone in YAML 1.2. + +Installation and usage +---------------------- + +The import path for the package is *gopkg.in/yaml.v3*. + +To install it, run: + + go get gopkg.in/yaml.v3 + +API documentation +----------------- + +If opened in a browser, the import path itself leads to the API documentation: + + - [https://gopkg.in/yaml.v3](https://gopkg.in/yaml.v3) + +API stability +------------- + +The package API for yaml v3 will remain stable as described in [gopkg.in](https://gopkg.in). + + +License +------- + +The yaml package is licensed under the MIT and Apache License 2.0 licenses. +Please see the LICENSE file for details. + + +Example +------- + +```Go +package main + +import ( + "fmt" + "log" + + "gopkg.in/yaml.v3" +) + +var data = ` +a: Easy! +b: + c: 2 + d: [3, 4] +` + +// Note: struct fields must be public in order for unmarshal to +// correctly populate the data. +type T struct { + A string + B struct { + RenamedC int `yaml:"c"` + D []int `yaml:",flow"` + } +} + +func main() { + t := T{} + + err := yaml.Unmarshal([]byte(data), &t) + if err != nil { + log.Fatalf("error: %v", err) + } + fmt.Printf("--- t:\n%v\n\n", t) + + d, err := yaml.Marshal(&t) + if err != nil { + log.Fatalf("error: %v", err) + } + fmt.Printf("--- t dump:\n%s\n\n", string(d)) + + m := make(map[interface{}]interface{}) + + err = yaml.Unmarshal([]byte(data), &m) + if err != nil { + log.Fatalf("error: %v", err) + } + fmt.Printf("--- m:\n%v\n\n", m) + + d, err = yaml.Marshal(&m) + if err != nil { + log.Fatalf("error: %v", err) + } + fmt.Printf("--- m dump:\n%s\n\n", string(d)) +} +``` + +This example will generate the following output: + +``` +--- t: +{Easy! {2 [3 4]}} + +--- t dump: +a: Easy! +b: + c: 2 + d: [3, 4] + + +--- m: +map[a:Easy! b:map[c:2 d:[3 4]]] + +--- m dump: +a: Easy! +b: + c: 2 + d: + - 3 + - 4 +``` + diff --git a/vendor/gopkg.in/yaml.v3/apic.go b/vendor/gopkg.in/yaml.v3/apic.go new file mode 100644 index 0000000000..65846e6749 --- /dev/null +++ b/vendor/gopkg.in/yaml.v3/apic.go @@ -0,0 +1,746 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// Copyright (c) 2006-2010 Kirill Simonov +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package yaml + +import ( + "io" +) + +func yaml_insert_token(parser *yaml_parser_t, pos int, token *yaml_token_t) { + //fmt.Println("yaml_insert_token", "pos:", pos, "typ:", token.typ, "head:", parser.tokens_head, "len:", len(parser.tokens)) + + // Check if we can move the queue at the beginning of the buffer. + if parser.tokens_head > 0 && len(parser.tokens) == cap(parser.tokens) { + if parser.tokens_head != len(parser.tokens) { + copy(parser.tokens, parser.tokens[parser.tokens_head:]) + } + parser.tokens = parser.tokens[:len(parser.tokens)-parser.tokens_head] + parser.tokens_head = 0 + } + parser.tokens = append(parser.tokens, *token) + if pos < 0 { + return + } + copy(parser.tokens[parser.tokens_head+pos+1:], parser.tokens[parser.tokens_head+pos:]) + parser.tokens[parser.tokens_head+pos] = *token +} + +// Create a new parser object. +func yaml_parser_initialize(parser *yaml_parser_t) bool { + *parser = yaml_parser_t{ + raw_buffer: make([]byte, 0, input_raw_buffer_size), + buffer: make([]byte, 0, input_buffer_size), + } + return true +} + +// Destroy a parser object. +func yaml_parser_delete(parser *yaml_parser_t) { + *parser = yaml_parser_t{} +} + +// String read handler. +func yaml_string_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) { + if parser.input_pos == len(parser.input) { + return 0, io.EOF + } + n = copy(buffer, parser.input[parser.input_pos:]) + parser.input_pos += n + return n, nil +} + +// Reader read handler. +func yaml_reader_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) { + return parser.input_reader.Read(buffer) +} + +// Set a string input. +func yaml_parser_set_input_string(parser *yaml_parser_t, input []byte) { + if parser.read_handler != nil { + panic("must set the input source only once") + } + parser.read_handler = yaml_string_read_handler + parser.input = input + parser.input_pos = 0 +} + +// Set a file input. +func yaml_parser_set_input_reader(parser *yaml_parser_t, r io.Reader) { + if parser.read_handler != nil { + panic("must set the input source only once") + } + parser.read_handler = yaml_reader_read_handler + parser.input_reader = r +} + +// Set the source encoding. +func yaml_parser_set_encoding(parser *yaml_parser_t, encoding yaml_encoding_t) { + if parser.encoding != yaml_ANY_ENCODING { + panic("must set the encoding only once") + } + parser.encoding = encoding +} + +// Create a new emitter object. +func yaml_emitter_initialize(emitter *yaml_emitter_t) { + *emitter = yaml_emitter_t{ + buffer: make([]byte, output_buffer_size), + raw_buffer: make([]byte, 0, output_raw_buffer_size), + states: make([]yaml_emitter_state_t, 0, initial_stack_size), + events: make([]yaml_event_t, 0, initial_queue_size), + } +} + +// Destroy an emitter object. +func yaml_emitter_delete(emitter *yaml_emitter_t) { + *emitter = yaml_emitter_t{} +} + +// String write handler. +func yaml_string_write_handler(emitter *yaml_emitter_t, buffer []byte) error { + *emitter.output_buffer = append(*emitter.output_buffer, buffer...) + return nil +} + +// yaml_writer_write_handler uses emitter.output_writer to write the +// emitted text. +func yaml_writer_write_handler(emitter *yaml_emitter_t, buffer []byte) error { + _, err := emitter.output_writer.Write(buffer) + return err +} + +// Set a string output. +func yaml_emitter_set_output_string(emitter *yaml_emitter_t, output_buffer *[]byte) { + if emitter.write_handler != nil { + panic("must set the output target only once") + } + emitter.write_handler = yaml_string_write_handler + emitter.output_buffer = output_buffer +} + +// Set a file output. +func yaml_emitter_set_output_writer(emitter *yaml_emitter_t, w io.Writer) { + if emitter.write_handler != nil { + panic("must set the output target only once") + } + emitter.write_handler = yaml_writer_write_handler + emitter.output_writer = w +} + +// Set the output encoding. +func yaml_emitter_set_encoding(emitter *yaml_emitter_t, encoding yaml_encoding_t) { + if emitter.encoding != yaml_ANY_ENCODING { + panic("must set the output encoding only once") + } + emitter.encoding = encoding +} + +// Set the canonical output style. +func yaml_emitter_set_canonical(emitter *yaml_emitter_t, canonical bool) { + emitter.canonical = canonical +} + +// Set the indentation increment. +func yaml_emitter_set_indent(emitter *yaml_emitter_t, indent int) { + if indent < 2 || indent > 9 { + indent = 2 + } + emitter.best_indent = indent +} + +// Set the preferred line width. +func yaml_emitter_set_width(emitter *yaml_emitter_t, width int) { + if width < 0 { + width = -1 + } + emitter.best_width = width +} + +// Set if unescaped non-ASCII characters are allowed. +func yaml_emitter_set_unicode(emitter *yaml_emitter_t, unicode bool) { + emitter.unicode = unicode +} + +// Set the preferred line break character. +func yaml_emitter_set_break(emitter *yaml_emitter_t, line_break yaml_break_t) { + emitter.line_break = line_break +} + +///* +// * Destroy a token object. +// */ +// +//YAML_DECLARE(void) +//yaml_token_delete(yaml_token_t *token) +//{ +// assert(token); // Non-NULL token object expected. +// +// switch (token.type) +// { +// case YAML_TAG_DIRECTIVE_TOKEN: +// yaml_free(token.data.tag_directive.handle); +// yaml_free(token.data.tag_directive.prefix); +// break; +// +// case YAML_ALIAS_TOKEN: +// yaml_free(token.data.alias.value); +// break; +// +// case YAML_ANCHOR_TOKEN: +// yaml_free(token.data.anchor.value); +// break; +// +// case YAML_TAG_TOKEN: +// yaml_free(token.data.tag.handle); +// yaml_free(token.data.tag.suffix); +// break; +// +// case YAML_SCALAR_TOKEN: +// yaml_free(token.data.scalar.value); +// break; +// +// default: +// break; +// } +// +// memset(token, 0, sizeof(yaml_token_t)); +//} +// +///* +// * Check if a string is a valid UTF-8 sequence. +// * +// * Check 'reader.c' for more details on UTF-8 encoding. +// */ +// +//static int +//yaml_check_utf8(yaml_char_t *start, size_t length) +//{ +// yaml_char_t *end = start+length; +// yaml_char_t *pointer = start; +// +// while (pointer < end) { +// unsigned char octet; +// unsigned int width; +// unsigned int value; +// size_t k; +// +// octet = pointer[0]; +// width = (octet & 0x80) == 0x00 ? 1 : +// (octet & 0xE0) == 0xC0 ? 2 : +// (octet & 0xF0) == 0xE0 ? 3 : +// (octet & 0xF8) == 0xF0 ? 4 : 0; +// value = (octet & 0x80) == 0x00 ? octet & 0x7F : +// (octet & 0xE0) == 0xC0 ? octet & 0x1F : +// (octet & 0xF0) == 0xE0 ? octet & 0x0F : +// (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0; +// if (!width) return 0; +// if (pointer+width > end) return 0; +// for (k = 1; k < width; k ++) { +// octet = pointer[k]; +// if ((octet & 0xC0) != 0x80) return 0; +// value = (value << 6) + (octet & 0x3F); +// } +// if (!((width == 1) || +// (width == 2 && value >= 0x80) || +// (width == 3 && value >= 0x800) || +// (width == 4 && value >= 0x10000))) return 0; +// +// pointer += width; +// } +// +// return 1; +//} +// + +// Create STREAM-START. +func yaml_stream_start_event_initialize(event *yaml_event_t, encoding yaml_encoding_t) { + *event = yaml_event_t{ + typ: yaml_STREAM_START_EVENT, + encoding: encoding, + } +} + +// Create STREAM-END. +func yaml_stream_end_event_initialize(event *yaml_event_t) { + *event = yaml_event_t{ + typ: yaml_STREAM_END_EVENT, + } +} + +// Create DOCUMENT-START. +func yaml_document_start_event_initialize( + event *yaml_event_t, + version_directive *yaml_version_directive_t, + tag_directives []yaml_tag_directive_t, + implicit bool, +) { + *event = yaml_event_t{ + typ: yaml_DOCUMENT_START_EVENT, + version_directive: version_directive, + tag_directives: tag_directives, + implicit: implicit, + } +} + +// Create DOCUMENT-END. +func yaml_document_end_event_initialize(event *yaml_event_t, implicit bool) { + *event = yaml_event_t{ + typ: yaml_DOCUMENT_END_EVENT, + implicit: implicit, + } +} + +// Create ALIAS. +func yaml_alias_event_initialize(event *yaml_event_t, anchor []byte) bool { + *event = yaml_event_t{ + typ: yaml_ALIAS_EVENT, + anchor: anchor, + } + return true +} + +// Create SCALAR. +func yaml_scalar_event_initialize(event *yaml_event_t, anchor, tag, value []byte, plain_implicit, quoted_implicit bool, style yaml_scalar_style_t) bool { + *event = yaml_event_t{ + typ: yaml_SCALAR_EVENT, + anchor: anchor, + tag: tag, + value: value, + implicit: plain_implicit, + quoted_implicit: quoted_implicit, + style: yaml_style_t(style), + } + return true +} + +// Create SEQUENCE-START. +func yaml_sequence_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_sequence_style_t) bool { + *event = yaml_event_t{ + typ: yaml_SEQUENCE_START_EVENT, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(style), + } + return true +} + +// Create SEQUENCE-END. +func yaml_sequence_end_event_initialize(event *yaml_event_t) bool { + *event = yaml_event_t{ + typ: yaml_SEQUENCE_END_EVENT, + } + return true +} + +// Create MAPPING-START. +func yaml_mapping_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_mapping_style_t) { + *event = yaml_event_t{ + typ: yaml_MAPPING_START_EVENT, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(style), + } +} + +// Create MAPPING-END. +func yaml_mapping_end_event_initialize(event *yaml_event_t) { + *event = yaml_event_t{ + typ: yaml_MAPPING_END_EVENT, + } +} + +// Destroy an event object. +func yaml_event_delete(event *yaml_event_t) { + *event = yaml_event_t{} +} + +///* +// * Create a document object. +// */ +// +//YAML_DECLARE(int) +//yaml_document_initialize(document *yaml_document_t, +// version_directive *yaml_version_directive_t, +// tag_directives_start *yaml_tag_directive_t, +// tag_directives_end *yaml_tag_directive_t, +// start_implicit int, end_implicit int) +//{ +// struct { +// error yaml_error_type_t +// } context +// struct { +// start *yaml_node_t +// end *yaml_node_t +// top *yaml_node_t +// } nodes = { NULL, NULL, NULL } +// version_directive_copy *yaml_version_directive_t = NULL +// struct { +// start *yaml_tag_directive_t +// end *yaml_tag_directive_t +// top *yaml_tag_directive_t +// } tag_directives_copy = { NULL, NULL, NULL } +// value yaml_tag_directive_t = { NULL, NULL } +// mark yaml_mark_t = { 0, 0, 0 } +// +// assert(document) // Non-NULL document object is expected. +// assert((tag_directives_start && tag_directives_end) || +// (tag_directives_start == tag_directives_end)) +// // Valid tag directives are expected. +// +// if (!STACK_INIT(&context, nodes, INITIAL_STACK_SIZE)) goto error +// +// if (version_directive) { +// version_directive_copy = yaml_malloc(sizeof(yaml_version_directive_t)) +// if (!version_directive_copy) goto error +// version_directive_copy.major = version_directive.major +// version_directive_copy.minor = version_directive.minor +// } +// +// if (tag_directives_start != tag_directives_end) { +// tag_directive *yaml_tag_directive_t +// if (!STACK_INIT(&context, tag_directives_copy, INITIAL_STACK_SIZE)) +// goto error +// for (tag_directive = tag_directives_start +// tag_directive != tag_directives_end; tag_directive ++) { +// assert(tag_directive.handle) +// assert(tag_directive.prefix) +// if (!yaml_check_utf8(tag_directive.handle, +// strlen((char *)tag_directive.handle))) +// goto error +// if (!yaml_check_utf8(tag_directive.prefix, +// strlen((char *)tag_directive.prefix))) +// goto error +// value.handle = yaml_strdup(tag_directive.handle) +// value.prefix = yaml_strdup(tag_directive.prefix) +// if (!value.handle || !value.prefix) goto error +// if (!PUSH(&context, tag_directives_copy, value)) +// goto error +// value.handle = NULL +// value.prefix = NULL +// } +// } +// +// DOCUMENT_INIT(*document, nodes.start, nodes.end, version_directive_copy, +// tag_directives_copy.start, tag_directives_copy.top, +// start_implicit, end_implicit, mark, mark) +// +// return 1 +// +//error: +// STACK_DEL(&context, nodes) +// yaml_free(version_directive_copy) +// while (!STACK_EMPTY(&context, tag_directives_copy)) { +// value yaml_tag_directive_t = POP(&context, tag_directives_copy) +// yaml_free(value.handle) +// yaml_free(value.prefix) +// } +// STACK_DEL(&context, tag_directives_copy) +// yaml_free(value.handle) +// yaml_free(value.prefix) +// +// return 0 +//} +// +///* +// * Destroy a document object. +// */ +// +//YAML_DECLARE(void) +//yaml_document_delete(document *yaml_document_t) +//{ +// struct { +// error yaml_error_type_t +// } context +// tag_directive *yaml_tag_directive_t +// +// context.error = YAML_NO_ERROR // Eliminate a compiler warning. +// +// assert(document) // Non-NULL document object is expected. +// +// while (!STACK_EMPTY(&context, document.nodes)) { +// node yaml_node_t = POP(&context, document.nodes) +// yaml_free(node.tag) +// switch (node.type) { +// case YAML_SCALAR_NODE: +// yaml_free(node.data.scalar.value) +// break +// case YAML_SEQUENCE_NODE: +// STACK_DEL(&context, node.data.sequence.items) +// break +// case YAML_MAPPING_NODE: +// STACK_DEL(&context, node.data.mapping.pairs) +// break +// default: +// assert(0) // Should not happen. +// } +// } +// STACK_DEL(&context, document.nodes) +// +// yaml_free(document.version_directive) +// for (tag_directive = document.tag_directives.start +// tag_directive != document.tag_directives.end +// tag_directive++) { +// yaml_free(tag_directive.handle) +// yaml_free(tag_directive.prefix) +// } +// yaml_free(document.tag_directives.start) +// +// memset(document, 0, sizeof(yaml_document_t)) +//} +// +///** +// * Get a document node. +// */ +// +//YAML_DECLARE(yaml_node_t *) +//yaml_document_get_node(document *yaml_document_t, index int) +//{ +// assert(document) // Non-NULL document object is expected. +// +// if (index > 0 && document.nodes.start + index <= document.nodes.top) { +// return document.nodes.start + index - 1 +// } +// return NULL +//} +// +///** +// * Get the root object. +// */ +// +//YAML_DECLARE(yaml_node_t *) +//yaml_document_get_root_node(document *yaml_document_t) +//{ +// assert(document) // Non-NULL document object is expected. +// +// if (document.nodes.top != document.nodes.start) { +// return document.nodes.start +// } +// return NULL +//} +// +///* +// * Add a scalar node to a document. +// */ +// +//YAML_DECLARE(int) +//yaml_document_add_scalar(document *yaml_document_t, +// tag *yaml_char_t, value *yaml_char_t, length int, +// style yaml_scalar_style_t) +//{ +// struct { +// error yaml_error_type_t +// } context +// mark yaml_mark_t = { 0, 0, 0 } +// tag_copy *yaml_char_t = NULL +// value_copy *yaml_char_t = NULL +// node yaml_node_t +// +// assert(document) // Non-NULL document object is expected. +// assert(value) // Non-NULL value is expected. +// +// if (!tag) { +// tag = (yaml_char_t *)YAML_DEFAULT_SCALAR_TAG +// } +// +// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error +// tag_copy = yaml_strdup(tag) +// if (!tag_copy) goto error +// +// if (length < 0) { +// length = strlen((char *)value) +// } +// +// if (!yaml_check_utf8(value, length)) goto error +// value_copy = yaml_malloc(length+1) +// if (!value_copy) goto error +// memcpy(value_copy, value, length) +// value_copy[length] = '\0' +// +// SCALAR_NODE_INIT(node, tag_copy, value_copy, length, style, mark, mark) +// if (!PUSH(&context, document.nodes, node)) goto error +// +// return document.nodes.top - document.nodes.start +// +//error: +// yaml_free(tag_copy) +// yaml_free(value_copy) +// +// return 0 +//} +// +///* +// * Add a sequence node to a document. +// */ +// +//YAML_DECLARE(int) +//yaml_document_add_sequence(document *yaml_document_t, +// tag *yaml_char_t, style yaml_sequence_style_t) +//{ +// struct { +// error yaml_error_type_t +// } context +// mark yaml_mark_t = { 0, 0, 0 } +// tag_copy *yaml_char_t = NULL +// struct { +// start *yaml_node_item_t +// end *yaml_node_item_t +// top *yaml_node_item_t +// } items = { NULL, NULL, NULL } +// node yaml_node_t +// +// assert(document) // Non-NULL document object is expected. +// +// if (!tag) { +// tag = (yaml_char_t *)YAML_DEFAULT_SEQUENCE_TAG +// } +// +// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error +// tag_copy = yaml_strdup(tag) +// if (!tag_copy) goto error +// +// if (!STACK_INIT(&context, items, INITIAL_STACK_SIZE)) goto error +// +// SEQUENCE_NODE_INIT(node, tag_copy, items.start, items.end, +// style, mark, mark) +// if (!PUSH(&context, document.nodes, node)) goto error +// +// return document.nodes.top - document.nodes.start +// +//error: +// STACK_DEL(&context, items) +// yaml_free(tag_copy) +// +// return 0 +//} +// +///* +// * Add a mapping node to a document. +// */ +// +//YAML_DECLARE(int) +//yaml_document_add_mapping(document *yaml_document_t, +// tag *yaml_char_t, style yaml_mapping_style_t) +//{ +// struct { +// error yaml_error_type_t +// } context +// mark yaml_mark_t = { 0, 0, 0 } +// tag_copy *yaml_char_t = NULL +// struct { +// start *yaml_node_pair_t +// end *yaml_node_pair_t +// top *yaml_node_pair_t +// } pairs = { NULL, NULL, NULL } +// node yaml_node_t +// +// assert(document) // Non-NULL document object is expected. +// +// if (!tag) { +// tag = (yaml_char_t *)YAML_DEFAULT_MAPPING_TAG +// } +// +// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error +// tag_copy = yaml_strdup(tag) +// if (!tag_copy) goto error +// +// if (!STACK_INIT(&context, pairs, INITIAL_STACK_SIZE)) goto error +// +// MAPPING_NODE_INIT(node, tag_copy, pairs.start, pairs.end, +// style, mark, mark) +// if (!PUSH(&context, document.nodes, node)) goto error +// +// return document.nodes.top - document.nodes.start +// +//error: +// STACK_DEL(&context, pairs) +// yaml_free(tag_copy) +// +// return 0 +//} +// +///* +// * Append an item to a sequence node. +// */ +// +//YAML_DECLARE(int) +//yaml_document_append_sequence_item(document *yaml_document_t, +// sequence int, item int) +//{ +// struct { +// error yaml_error_type_t +// } context +// +// assert(document) // Non-NULL document is required. +// assert(sequence > 0 +// && document.nodes.start + sequence <= document.nodes.top) +// // Valid sequence id is required. +// assert(document.nodes.start[sequence-1].type == YAML_SEQUENCE_NODE) +// // A sequence node is required. +// assert(item > 0 && document.nodes.start + item <= document.nodes.top) +// // Valid item id is required. +// +// if (!PUSH(&context, +// document.nodes.start[sequence-1].data.sequence.items, item)) +// return 0 +// +// return 1 +//} +// +///* +// * Append a pair of a key and a value to a mapping node. +// */ +// +//YAML_DECLARE(int) +//yaml_document_append_mapping_pair(document *yaml_document_t, +// mapping int, key int, value int) +//{ +// struct { +// error yaml_error_type_t +// } context +// +// pair yaml_node_pair_t +// +// assert(document) // Non-NULL document is required. +// assert(mapping > 0 +// && document.nodes.start + mapping <= document.nodes.top) +// // Valid mapping id is required. +// assert(document.nodes.start[mapping-1].type == YAML_MAPPING_NODE) +// // A mapping node is required. +// assert(key > 0 && document.nodes.start + key <= document.nodes.top) +// // Valid key id is required. +// assert(value > 0 && document.nodes.start + value <= document.nodes.top) +// // Valid value id is required. +// +// pair.key = key +// pair.value = value +// +// if (!PUSH(&context, +// document.nodes.start[mapping-1].data.mapping.pairs, pair)) +// return 0 +// +// return 1 +//} +// +// diff --git a/vendor/gopkg.in/yaml.v3/decode.go b/vendor/gopkg.in/yaml.v3/decode.go new file mode 100644 index 0000000000..b2e16a6da1 --- /dev/null +++ b/vendor/gopkg.in/yaml.v3/decode.go @@ -0,0 +1,902 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// +// 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 yaml + +import ( + "encoding" + "encoding/base64" + "fmt" + "io" + "math" + "reflect" + "strconv" + "time" +) + +// ---------------------------------------------------------------------------- +// Parser, produces a node tree out of a libyaml event stream. + +type parser struct { + parser yaml_parser_t + event yaml_event_t + doc *Node + anchors map[string]*Node + doneInit bool +} + +func newParser(b []byte) *parser { + p := parser{} + if !yaml_parser_initialize(&p.parser) { + panic("failed to initialize YAML emitter") + } + if len(b) == 0 { + b = []byte{'\n'} + } + yaml_parser_set_input_string(&p.parser, b) + return &p +} + +func newParserFromReader(r io.Reader) *parser { + p := parser{} + if !yaml_parser_initialize(&p.parser) { + panic("failed to initialize YAML emitter") + } + yaml_parser_set_input_reader(&p.parser, r) + return &p +} + +func (p *parser) init() { + if p.doneInit { + return + } + p.anchors = make(map[string]*Node) + p.expect(yaml_STREAM_START_EVENT) + p.doneInit = true +} + +func (p *parser) destroy() { + if p.event.typ != yaml_NO_EVENT { + yaml_event_delete(&p.event) + } + yaml_parser_delete(&p.parser) +} + +// expect consumes an event from the event stream and +// checks that it's of the expected type. +func (p *parser) expect(e yaml_event_type_t) { + if p.event.typ == yaml_NO_EVENT { + if !yaml_parser_parse(&p.parser, &p.event) { + p.fail() + } + } + if p.event.typ == yaml_STREAM_END_EVENT { + failf("attempted to go past the end of stream; corrupted value?") + } + if p.event.typ != e { + p.parser.problem = fmt.Sprintf("expected %s event but got %s", e, p.event.typ) + p.fail() + } + yaml_event_delete(&p.event) + p.event.typ = yaml_NO_EVENT +} + +// peek peeks at the next event in the event stream, +// puts the results into p.event and returns the event type. +func (p *parser) peek() yaml_event_type_t { + if p.event.typ != yaml_NO_EVENT { + return p.event.typ + } + if !yaml_parser_parse(&p.parser, &p.event) { + p.fail() + } + return p.event.typ +} + +func (p *parser) fail() { + var where string + var line int + if p.parser.problem_mark.line != 0 { + line = p.parser.problem_mark.line + // Scanner errors don't iterate line before returning error + if p.parser.error == yaml_SCANNER_ERROR { + line++ + } + } else if p.parser.context_mark.line != 0 { + line = p.parser.context_mark.line + } + if line != 0 { + where = "line " + strconv.Itoa(line) + ": " + } + var msg string + if len(p.parser.problem) > 0 { + msg = p.parser.problem + } else { + msg = "unknown problem parsing YAML content" + } + failf("%s%s", where, msg) +} + +func (p *parser) anchor(n *Node, anchor []byte) { + if anchor != nil { + n.Anchor = string(anchor) + p.anchors[n.Anchor] = n + } +} + +func (p *parser) parse() *Node { + p.init() + switch p.peek() { + case yaml_SCALAR_EVENT: + return p.scalar() + case yaml_ALIAS_EVENT: + return p.alias() + case yaml_MAPPING_START_EVENT: + return p.mapping() + case yaml_SEQUENCE_START_EVENT: + return p.sequence() + case yaml_DOCUMENT_START_EVENT: + return p.document() + case yaml_STREAM_END_EVENT: + // Happens when attempting to decode an empty buffer. + return nil + case yaml_TAIL_COMMENT_EVENT: + panic("internal error: unexpected tail comment event (please report)") + default: + panic("internal error: attempted to parse unknown event (please report): " + p.event.typ.String()) + } +} + +func (p *parser) node(kind Kind, defaultTag, tag, value string) *Node { + var style Style + if tag != "" && tag != "!" { + tag = shortTag(tag) + style = TaggedStyle + } else if defaultTag != "" { + tag = defaultTag + } else if kind == ScalarNode { + tag, _ = resolve("", value) + } + return &Node{ + Kind: kind, + Tag: tag, + Value: value, + Style: style, + Line: p.event.start_mark.line + 1, + Column: p.event.start_mark.column + 1, + HeadComment: string(p.event.head_comment), + LineComment: string(p.event.line_comment), + FootComment: string(p.event.foot_comment), + } +} + +func (p *parser) parseChild(parent *Node) *Node { + child := p.parse() + parent.Content = append(parent.Content, child) + return child +} + +func (p *parser) document() *Node { + n := p.node(DocumentNode, "", "", "") + p.doc = n + p.expect(yaml_DOCUMENT_START_EVENT) + p.parseChild(n) + if p.peek() == yaml_DOCUMENT_END_EVENT { + n.FootComment = string(p.event.foot_comment) + } + p.expect(yaml_DOCUMENT_END_EVENT) + return n +} + +func (p *parser) alias() *Node { + n := p.node(AliasNode, "", "", string(p.event.anchor)) + n.Alias = p.anchors[n.Value] + if n.Alias == nil { + failf("unknown anchor '%s' referenced", n.Value) + } + p.expect(yaml_ALIAS_EVENT) + return n +} + +func (p *parser) scalar() *Node { + var parsedStyle = p.event.scalar_style() + var nodeStyle Style + switch { + case parsedStyle&yaml_DOUBLE_QUOTED_SCALAR_STYLE != 0: + nodeStyle = DoubleQuotedStyle + case parsedStyle&yaml_SINGLE_QUOTED_SCALAR_STYLE != 0: + nodeStyle = SingleQuotedStyle + case parsedStyle&yaml_LITERAL_SCALAR_STYLE != 0: + nodeStyle = LiteralStyle + case parsedStyle&yaml_FOLDED_SCALAR_STYLE != 0: + nodeStyle = FoldedStyle + } + var nodeValue = string(p.event.value) + var nodeTag = string(p.event.tag) + var defaultTag string + if nodeStyle == 0 { + if nodeValue == "<<" { + defaultTag = mergeTag + } + } else { + defaultTag = strTag + } + n := p.node(ScalarNode, defaultTag, nodeTag, nodeValue) + n.Style |= nodeStyle + p.anchor(n, p.event.anchor) + p.expect(yaml_SCALAR_EVENT) + return n +} + +func (p *parser) sequence() *Node { + n := p.node(SequenceNode, seqTag, string(p.event.tag), "") + if p.event.sequence_style()&yaml_FLOW_SEQUENCE_STYLE != 0 { + n.Style |= FlowStyle + } + p.anchor(n, p.event.anchor) + p.expect(yaml_SEQUENCE_START_EVENT) + for p.peek() != yaml_SEQUENCE_END_EVENT { + p.parseChild(n) + } + n.LineComment = string(p.event.line_comment) + n.FootComment = string(p.event.foot_comment) + p.expect(yaml_SEQUENCE_END_EVENT) + return n +} + +func (p *parser) mapping() *Node { + n := p.node(MappingNode, mapTag, string(p.event.tag), "") + block := true + if p.event.mapping_style()&yaml_FLOW_MAPPING_STYLE != 0 { + block = false + n.Style |= FlowStyle + } + p.anchor(n, p.event.anchor) + p.expect(yaml_MAPPING_START_EVENT) + for p.peek() != yaml_MAPPING_END_EVENT { + k := p.parseChild(n) + if block && k.FootComment != "" { + // Must be a foot comment for the prior value when being dedented. + if len(n.Content) > 2 { + n.Content[len(n.Content)-3].FootComment = k.FootComment + k.FootComment = "" + } + } + v := p.parseChild(n) + if k.FootComment == "" && v.FootComment != "" { + k.FootComment = v.FootComment + v.FootComment = "" + } + if p.peek() == yaml_TAIL_COMMENT_EVENT { + if k.FootComment == "" { + k.FootComment = string(p.event.foot_comment) + } + p.expect(yaml_TAIL_COMMENT_EVENT) + } + } + n.LineComment = string(p.event.line_comment) + n.FootComment = string(p.event.foot_comment) + if n.Style&FlowStyle == 0 && n.FootComment != "" && len(n.Content) > 1 { + n.Content[len(n.Content)-2].FootComment = n.FootComment + n.FootComment = "" + } + p.expect(yaml_MAPPING_END_EVENT) + return n +} + +// ---------------------------------------------------------------------------- +// Decoder, unmarshals a node into a provided value. + +type decoder struct { + doc *Node + aliases map[*Node]bool + terrors []string + + stringMapType reflect.Type + generalMapType reflect.Type + + knownFields bool + uniqueKeys bool + decodeCount int + aliasCount int + aliasDepth int +} + +var ( + nodeType = reflect.TypeOf(Node{}) + durationType = reflect.TypeOf(time.Duration(0)) + stringMapType = reflect.TypeOf(map[string]interface{}{}) + generalMapType = reflect.TypeOf(map[interface{}]interface{}{}) + ifaceType = generalMapType.Elem() + timeType = reflect.TypeOf(time.Time{}) + ptrTimeType = reflect.TypeOf(&time.Time{}) +) + +func newDecoder() *decoder { + d := &decoder{ + stringMapType: stringMapType, + generalMapType: generalMapType, + uniqueKeys: true, + } + d.aliases = make(map[*Node]bool) + return d +} + +func (d *decoder) terror(n *Node, tag string, out reflect.Value) { + if n.Tag != "" { + tag = n.Tag + } + value := n.Value + if tag != seqTag && tag != mapTag { + if len(value) > 10 { + value = " `" + value[:7] + "...`" + } else { + value = " `" + value + "`" + } + } + d.terrors = append(d.terrors, fmt.Sprintf("line %d: cannot unmarshal %s%s into %s", n.Line, shortTag(tag), value, out.Type())) +} + +func (d *decoder) callUnmarshaler(n *Node, u Unmarshaler) (good bool) { + err := u.UnmarshalYAML(n) + if e, ok := err.(*TypeError); ok { + d.terrors = append(d.terrors, e.Errors...) + return false + } + if err != nil { + fail(err) + } + return true +} + +func (d *decoder) callObsoleteUnmarshaler(n *Node, u obsoleteUnmarshaler) (good bool) { + terrlen := len(d.terrors) + err := u.UnmarshalYAML(func(v interface{}) (err error) { + defer handleErr(&err) + d.unmarshal(n, reflect.ValueOf(v)) + if len(d.terrors) > terrlen { + issues := d.terrors[terrlen:] + d.terrors = d.terrors[:terrlen] + return &TypeError{issues} + } + return nil + }) + if e, ok := err.(*TypeError); ok { + d.terrors = append(d.terrors, e.Errors...) + return false + } + if err != nil { + fail(err) + } + return true +} + +// d.prepare initializes and dereferences pointers and calls UnmarshalYAML +// if a value is found to implement it. +// It returns the initialized and dereferenced out value, whether +// unmarshalling was already done by UnmarshalYAML, and if so whether +// its types unmarshalled appropriately. +// +// If n holds a null value, prepare returns before doing anything. +func (d *decoder) prepare(n *Node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) { + if n.ShortTag() == nullTag { + return out, false, false + } + again := true + for again { + again = false + if out.Kind() == reflect.Ptr { + if out.IsNil() { + out.Set(reflect.New(out.Type().Elem())) + } + out = out.Elem() + again = true + } + if out.CanAddr() { + outi := out.Addr().Interface() + if u, ok := outi.(Unmarshaler); ok { + good = d.callUnmarshaler(n, u) + return out, true, good + } + if u, ok := outi.(obsoleteUnmarshaler); ok { + good = d.callObsoleteUnmarshaler(n, u) + return out, true, good + } + } + } + return out, false, false +} + +func (d *decoder) fieldByIndex(n *Node, v reflect.Value, index []int) (field reflect.Value) { + if n.ShortTag() == nullTag { + return reflect.Value{} + } + for _, num := range index { + for { + if v.Kind() == reflect.Ptr { + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + v = v.Elem() + continue + } + break + } + v = v.Field(num) + } + return v +} + +func (d *decoder) unmarshal(n *Node, out reflect.Value) (good bool) { + d.decodeCount++ + if d.aliasDepth > 0 { + d.aliasCount++ + } + if d.aliasCount > 100 && d.decodeCount > 1000 && float64(d.aliasCount)/float64(d.decodeCount) > 0.99 { + failf("document contains excessive aliasing") + } + if out.Type() == nodeType { + out.Set(reflect.ValueOf(n).Elem()) + return true + } + switch n.Kind { + case DocumentNode: + return d.document(n, out) + case AliasNode: + return d.alias(n, out) + } + out, unmarshaled, good := d.prepare(n, out) + if unmarshaled { + return good + } + switch n.Kind { + case ScalarNode: + good = d.scalar(n, out) + case MappingNode: + good = d.mapping(n, out) + case SequenceNode: + good = d.sequence(n, out) + default: + panic("internal error: unknown node kind: " + strconv.Itoa(int(n.Kind))) + } + return good +} + +func (d *decoder) document(n *Node, out reflect.Value) (good bool) { + if len(n.Content) == 1 { + d.doc = n + d.unmarshal(n.Content[0], out) + return true + } + return false +} + +func (d *decoder) alias(n *Node, out reflect.Value) (good bool) { + if d.aliases[n] { + // TODO this could actually be allowed in some circumstances. + failf("anchor '%s' value contains itself", n.Value) + } + d.aliases[n] = true + d.aliasDepth++ + good = d.unmarshal(n.Alias, out) + d.aliasDepth-- + delete(d.aliases, n) + return good +} + +var zeroValue reflect.Value + +func resetMap(out reflect.Value) { + for _, k := range out.MapKeys() { + out.SetMapIndex(k, zeroValue) + } +} + +func (d *decoder) scalar(n *Node, out reflect.Value) bool { + var tag string + var resolved interface{} + if n.indicatedString() { + tag = strTag + resolved = n.Value + } else { + tag, resolved = resolve(n.Tag, n.Value) + if tag == binaryTag { + data, err := base64.StdEncoding.DecodeString(resolved.(string)) + if err != nil { + failf("!!binary value contains invalid base64 data") + } + resolved = string(data) + } + } + if resolved == nil { + if out.CanAddr() { + switch out.Kind() { + case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice: + out.Set(reflect.Zero(out.Type())) + return true + } + } + return false + } + if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() { + // We've resolved to exactly the type we want, so use that. + out.Set(resolvedv) + return true + } + // Perhaps we can use the value as a TextUnmarshaler to + // set its value. + if out.CanAddr() { + u, ok := out.Addr().Interface().(encoding.TextUnmarshaler) + if ok { + var text []byte + if tag == binaryTag { + text = []byte(resolved.(string)) + } else { + // We let any value be unmarshaled into TextUnmarshaler. + // That might be more lax than we'd like, but the + // TextUnmarshaler itself should bowl out any dubious values. + text = []byte(n.Value) + } + err := u.UnmarshalText(text) + if err != nil { + fail(err) + } + return true + } + } + switch out.Kind() { + case reflect.String: + if tag == binaryTag { + out.SetString(resolved.(string)) + return true + } + out.SetString(n.Value) + return true + case reflect.Interface: + out.Set(reflect.ValueOf(resolved)) + return true + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + // This used to work in v2, but it's very unfriendly. + isDuration := out.Type() == durationType + + switch resolved := resolved.(type) { + case int: + if !isDuration && !out.OverflowInt(int64(resolved)) { + out.SetInt(int64(resolved)) + return true + } + case int64: + if !isDuration && !out.OverflowInt(resolved) { + out.SetInt(resolved) + return true + } + case uint64: + if !isDuration && resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { + out.SetInt(int64(resolved)) + return true + } + case float64: + if !isDuration && resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { + out.SetInt(int64(resolved)) + return true + } + case string: + if out.Type() == durationType { + d, err := time.ParseDuration(resolved) + if err == nil { + out.SetInt(int64(d)) + return true + } + } + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + switch resolved := resolved.(type) { + case int: + if resolved >= 0 && !out.OverflowUint(uint64(resolved)) { + out.SetUint(uint64(resolved)) + return true + } + case int64: + if resolved >= 0 && !out.OverflowUint(uint64(resolved)) { + out.SetUint(uint64(resolved)) + return true + } + case uint64: + if !out.OverflowUint(uint64(resolved)) { + out.SetUint(uint64(resolved)) + return true + } + case float64: + if resolved <= math.MaxUint64 && !out.OverflowUint(uint64(resolved)) { + out.SetUint(uint64(resolved)) + return true + } + } + case reflect.Bool: + switch resolved := resolved.(type) { + case bool: + out.SetBool(resolved) + return true + case string: + // This offers some compatibility with the 1.1 spec (https://yaml.org/type/bool.html). + // It only works if explicitly attempting to unmarshal into a typed bool value. + switch resolved { + case "y", "Y", "yes", "Yes", "YES", "on", "On", "ON": + out.SetBool(true) + return true + case "n", "N", "no", "No", "NO", "off", "Off", "OFF": + out.SetBool(false) + return true + } + } + case reflect.Float32, reflect.Float64: + switch resolved := resolved.(type) { + case int: + out.SetFloat(float64(resolved)) + return true + case int64: + out.SetFloat(float64(resolved)) + return true + case uint64: + out.SetFloat(float64(resolved)) + return true + case float64: + out.SetFloat(resolved) + return true + } + case reflect.Struct: + if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() { + out.Set(resolvedv) + return true + } + case reflect.Ptr: + panic("yaml internal error: please report the issue") + } + d.terror(n, tag, out) + return false +} + +func settableValueOf(i interface{}) reflect.Value { + v := reflect.ValueOf(i) + sv := reflect.New(v.Type()).Elem() + sv.Set(v) + return sv +} + +func (d *decoder) sequence(n *Node, out reflect.Value) (good bool) { + l := len(n.Content) + + var iface reflect.Value + switch out.Kind() { + case reflect.Slice: + out.Set(reflect.MakeSlice(out.Type(), l, l)) + case reflect.Array: + if l != out.Len() { + failf("invalid array: want %d elements but got %d", out.Len(), l) + } + case reflect.Interface: + // No type hints. Will have to use a generic sequence. + iface = out + out = settableValueOf(make([]interface{}, l)) + default: + d.terror(n, seqTag, out) + return false + } + et := out.Type().Elem() + + j := 0 + for i := 0; i < l; i++ { + e := reflect.New(et).Elem() + if ok := d.unmarshal(n.Content[i], e); ok { + out.Index(j).Set(e) + j++ + } + } + if out.Kind() != reflect.Array { + out.Set(out.Slice(0, j)) + } + if iface.IsValid() { + iface.Set(out) + } + return true +} + +func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) { + l := len(n.Content) + if d.uniqueKeys { + nerrs := len(d.terrors) + for i := 0; i < l; i += 2 { + ni := n.Content[i] + for j := i + 2; j < l; j += 2 { + nj := n.Content[j] + if ni.Kind == nj.Kind && ni.Value == nj.Value { + d.terrors = append(d.terrors, fmt.Sprintf("line %d: mapping key %#v already defined at line %d", nj.Line, nj.Value, ni.Line)) + } + } + } + if len(d.terrors) > nerrs { + return false + } + } + switch out.Kind() { + case reflect.Struct: + return d.mappingStruct(n, out) + case reflect.Map: + // okay + case reflect.Interface: + iface := out + if isStringMap(n) { + out = reflect.MakeMap(d.stringMapType) + } else { + out = reflect.MakeMap(d.generalMapType) + } + iface.Set(out) + default: + d.terror(n, mapTag, out) + return false + } + + outt := out.Type() + kt := outt.Key() + et := outt.Elem() + + stringMapType := d.stringMapType + generalMapType := d.generalMapType + if outt.Elem() == ifaceType { + if outt.Key().Kind() == reflect.String { + d.stringMapType = outt + } else if outt.Key() == ifaceType { + d.generalMapType = outt + } + } + + if out.IsNil() { + out.Set(reflect.MakeMap(outt)) + } + for i := 0; i < l; i += 2 { + if isMerge(n.Content[i]) { + d.merge(n.Content[i+1], out) + continue + } + k := reflect.New(kt).Elem() + if d.unmarshal(n.Content[i], k) { + kkind := k.Kind() + if kkind == reflect.Interface { + kkind = k.Elem().Kind() + } + if kkind == reflect.Map || kkind == reflect.Slice { + failf("invalid map key: %#v", k.Interface()) + } + e := reflect.New(et).Elem() + if d.unmarshal(n.Content[i+1], e) { + out.SetMapIndex(k, e) + } + } + } + d.stringMapType = stringMapType + d.generalMapType = generalMapType + return true +} + +func isStringMap(n *Node) bool { + if n.Kind != MappingNode { + return false + } + l := len(n.Content) + for i := 0; i < l; i++ { + if n.Content[i].ShortTag() != strTag { + return false + } + } + return true +} + +func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) { + sinfo, err := getStructInfo(out.Type()) + if err != nil { + panic(err) + } + + var inlineMap reflect.Value + var elemType reflect.Type + if sinfo.InlineMap != -1 { + inlineMap = out.Field(sinfo.InlineMap) + inlineMap.Set(reflect.New(inlineMap.Type()).Elem()) + elemType = inlineMap.Type().Elem() + } + + for _, index := range sinfo.InlineUnmarshalers { + field := d.fieldByIndex(n, out, index) + d.prepare(n, field) + } + + var doneFields []bool + if d.uniqueKeys { + doneFields = make([]bool, len(sinfo.FieldsList)) + } + name := settableValueOf("") + l := len(n.Content) + for i := 0; i < l; i += 2 { + ni := n.Content[i] + if isMerge(ni) { + d.merge(n.Content[i+1], out) + continue + } + if !d.unmarshal(ni, name) { + continue + } + if info, ok := sinfo.FieldsMap[name.String()]; ok { + if d.uniqueKeys { + if doneFields[info.Id] { + d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.Line, name.String(), out.Type())) + continue + } + doneFields[info.Id] = true + } + var field reflect.Value + if info.Inline == nil { + field = out.Field(info.Num) + } else { + field = d.fieldByIndex(n, out, info.Inline) + } + d.unmarshal(n.Content[i+1], field) + } else if sinfo.InlineMap != -1 { + if inlineMap.IsNil() { + inlineMap.Set(reflect.MakeMap(inlineMap.Type())) + } + value := reflect.New(elemType).Elem() + d.unmarshal(n.Content[i+1], value) + inlineMap.SetMapIndex(name, value) + } else if d.knownFields { + d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.Line, name.String(), out.Type())) + } + } + return true +} + +func failWantMap() { + failf("map merge requires map or sequence of maps as the value") +} + +func (d *decoder) merge(n *Node, out reflect.Value) { + switch n.Kind { + case MappingNode: + d.unmarshal(n, out) + case AliasNode: + if n.Alias != nil && n.Alias.Kind != MappingNode { + failWantMap() + } + d.unmarshal(n, out) + case SequenceNode: + // Step backwards as earlier nodes take precedence. + for i := len(n.Content) - 1; i >= 0; i-- { + ni := n.Content[i] + if ni.Kind == AliasNode { + if ni.Alias != nil && ni.Alias.Kind != MappingNode { + failWantMap() + } + } else if ni.Kind != MappingNode { + failWantMap() + } + d.unmarshal(ni, out) + } + default: + failWantMap() + } +} + +func isMerge(n *Node) bool { + return n.Kind == ScalarNode && n.Value == "<<" && (n.Tag == "" || n.Tag == "!" || shortTag(n.Tag) == mergeTag) +} diff --git a/vendor/gopkg.in/yaml.v3/emitterc.go b/vendor/gopkg.in/yaml.v3/emitterc.go new file mode 100644 index 0000000000..a1383c0a04 --- /dev/null +++ b/vendor/gopkg.in/yaml.v3/emitterc.go @@ -0,0 +1,1992 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// Copyright (c) 2006-2010 Kirill Simonov +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package yaml + +import ( + "bytes" + "fmt" +) + +// Flush the buffer if needed. +func flush(emitter *yaml_emitter_t) bool { + if emitter.buffer_pos+5 >= len(emitter.buffer) { + return yaml_emitter_flush(emitter) + } + return true +} + +// Put a character to the output buffer. +func put(emitter *yaml_emitter_t, value byte) bool { + if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) { + return false + } + emitter.buffer[emitter.buffer_pos] = value + emitter.buffer_pos++ + emitter.column++ + return true +} + +// Put a line break to the output buffer. +func put_break(emitter *yaml_emitter_t) bool { + if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) { + return false + } + switch emitter.line_break { + case yaml_CR_BREAK: + emitter.buffer[emitter.buffer_pos] = '\r' + emitter.buffer_pos += 1 + case yaml_LN_BREAK: + emitter.buffer[emitter.buffer_pos] = '\n' + emitter.buffer_pos += 1 + case yaml_CRLN_BREAK: + emitter.buffer[emitter.buffer_pos+0] = '\r' + emitter.buffer[emitter.buffer_pos+1] = '\n' + emitter.buffer_pos += 2 + default: + panic("unknown line break setting") + } + if emitter.column == 0 { + emitter.space_above = true + } + emitter.column = 0 + emitter.line++ + // [Go] Do this here and below and drop from everywhere else (see commented lines). + emitter.indention = true + return true +} + +// Copy a character from a string into buffer. +func write(emitter *yaml_emitter_t, s []byte, i *int) bool { + if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) { + return false + } + p := emitter.buffer_pos + w := width(s[*i]) + switch w { + case 4: + emitter.buffer[p+3] = s[*i+3] + fallthrough + case 3: + emitter.buffer[p+2] = s[*i+2] + fallthrough + case 2: + emitter.buffer[p+1] = s[*i+1] + fallthrough + case 1: + emitter.buffer[p+0] = s[*i+0] + default: + panic("unknown character width") + } + emitter.column++ + emitter.buffer_pos += w + *i += w + return true +} + +// Write a whole string into buffer. +func write_all(emitter *yaml_emitter_t, s []byte) bool { + for i := 0; i < len(s); { + if !write(emitter, s, &i) { + return false + } + } + return true +} + +// Copy a line break character from a string into buffer. +func write_break(emitter *yaml_emitter_t, s []byte, i *int) bool { + if s[*i] == '\n' { + if !put_break(emitter) { + return false + } + *i++ + } else { + if !write(emitter, s, i) { + return false + } + if emitter.column == 0 { + emitter.space_above = true + } + emitter.column = 0 + emitter.line++ + // [Go] Do this here and above and drop from everywhere else (see commented lines). + emitter.indention = true + } + return true +} + +// Set an emitter error and return false. +func yaml_emitter_set_emitter_error(emitter *yaml_emitter_t, problem string) bool { + emitter.error = yaml_EMITTER_ERROR + emitter.problem = problem + return false +} + +// Emit an event. +func yaml_emitter_emit(emitter *yaml_emitter_t, event *yaml_event_t) bool { + emitter.events = append(emitter.events, *event) + for !yaml_emitter_need_more_events(emitter) { + event := &emitter.events[emitter.events_head] + if !yaml_emitter_analyze_event(emitter, event) { + return false + } + if !yaml_emitter_state_machine(emitter, event) { + return false + } + yaml_event_delete(event) + emitter.events_head++ + } + return true +} + +// Check if we need to accumulate more events before emitting. +// +// We accumulate extra +// - 1 event for DOCUMENT-START +// - 2 events for SEQUENCE-START +// - 3 events for MAPPING-START +// +func yaml_emitter_need_more_events(emitter *yaml_emitter_t) bool { + if emitter.events_head == len(emitter.events) { + return true + } + var accumulate int + switch emitter.events[emitter.events_head].typ { + case yaml_DOCUMENT_START_EVENT: + accumulate = 1 + break + case yaml_SEQUENCE_START_EVENT: + accumulate = 2 + break + case yaml_MAPPING_START_EVENT: + accumulate = 3 + break + default: + return false + } + if len(emitter.events)-emitter.events_head > accumulate { + return false + } + var level int + for i := emitter.events_head; i < len(emitter.events); i++ { + switch emitter.events[i].typ { + case yaml_STREAM_START_EVENT, yaml_DOCUMENT_START_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT: + level++ + case yaml_STREAM_END_EVENT, yaml_DOCUMENT_END_EVENT, yaml_SEQUENCE_END_EVENT, yaml_MAPPING_END_EVENT: + level-- + } + if level == 0 { + return false + } + } + return true +} + +// Append a directive to the directives stack. +func yaml_emitter_append_tag_directive(emitter *yaml_emitter_t, value *yaml_tag_directive_t, allow_duplicates bool) bool { + for i := 0; i < len(emitter.tag_directives); i++ { + if bytes.Equal(value.handle, emitter.tag_directives[i].handle) { + if allow_duplicates { + return true + } + return yaml_emitter_set_emitter_error(emitter, "duplicate %TAG directive") + } + } + + // [Go] Do we actually need to copy this given garbage collection + // and the lack of deallocating destructors? + tag_copy := yaml_tag_directive_t{ + handle: make([]byte, len(value.handle)), + prefix: make([]byte, len(value.prefix)), + } + copy(tag_copy.handle, value.handle) + copy(tag_copy.prefix, value.prefix) + emitter.tag_directives = append(emitter.tag_directives, tag_copy) + return true +} + +// Increase the indentation level. +func yaml_emitter_increase_indent(emitter *yaml_emitter_t, flow, indentless bool) bool { + emitter.indents = append(emitter.indents, emitter.indent) + if emitter.indent < 0 { + if flow { + emitter.indent = emitter.best_indent + } else { + emitter.indent = 0 + } + } else if !indentless { + emitter.indent += emitter.best_indent + // [Go] If inside a block sequence item, discount the space taken by the indicator. + if emitter.best_indent > 2 && emitter.states[len(emitter.states)-1] == yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE { + emitter.indent -= 2 + } + } + return true +} + +// State dispatcher. +func yaml_emitter_state_machine(emitter *yaml_emitter_t, event *yaml_event_t) bool { + switch emitter.state { + default: + case yaml_EMIT_STREAM_START_STATE: + return yaml_emitter_emit_stream_start(emitter, event) + + case yaml_EMIT_FIRST_DOCUMENT_START_STATE: + return yaml_emitter_emit_document_start(emitter, event, true) + + case yaml_EMIT_DOCUMENT_START_STATE: + return yaml_emitter_emit_document_start(emitter, event, false) + + case yaml_EMIT_DOCUMENT_CONTENT_STATE: + return yaml_emitter_emit_document_content(emitter, event) + + case yaml_EMIT_DOCUMENT_END_STATE: + return yaml_emitter_emit_document_end(emitter, event) + + case yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE: + return yaml_emitter_emit_flow_sequence_item(emitter, event, true, false) + + case yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE: + return yaml_emitter_emit_flow_sequence_item(emitter, event, false, true) + + case yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE: + return yaml_emitter_emit_flow_sequence_item(emitter, event, false, false) + + case yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE: + return yaml_emitter_emit_flow_mapping_key(emitter, event, true, false) + + case yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE: + return yaml_emitter_emit_flow_mapping_key(emitter, event, false, true) + + case yaml_EMIT_FLOW_MAPPING_KEY_STATE: + return yaml_emitter_emit_flow_mapping_key(emitter, event, false, false) + + case yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE: + return yaml_emitter_emit_flow_mapping_value(emitter, event, true) + + case yaml_EMIT_FLOW_MAPPING_VALUE_STATE: + return yaml_emitter_emit_flow_mapping_value(emitter, event, false) + + case yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE: + return yaml_emitter_emit_block_sequence_item(emitter, event, true) + + case yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE: + return yaml_emitter_emit_block_sequence_item(emitter, event, false) + + case yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE: + return yaml_emitter_emit_block_mapping_key(emitter, event, true) + + case yaml_EMIT_BLOCK_MAPPING_KEY_STATE: + return yaml_emitter_emit_block_mapping_key(emitter, event, false) + + case yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE: + return yaml_emitter_emit_block_mapping_value(emitter, event, true) + + case yaml_EMIT_BLOCK_MAPPING_VALUE_STATE: + return yaml_emitter_emit_block_mapping_value(emitter, event, false) + + case yaml_EMIT_END_STATE: + return yaml_emitter_set_emitter_error(emitter, "expected nothing after STREAM-END") + } + panic("invalid emitter state") +} + +// Expect STREAM-START. +func yaml_emitter_emit_stream_start(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if event.typ != yaml_STREAM_START_EVENT { + return yaml_emitter_set_emitter_error(emitter, "expected STREAM-START") + } + if emitter.encoding == yaml_ANY_ENCODING { + emitter.encoding = event.encoding + if emitter.encoding == yaml_ANY_ENCODING { + emitter.encoding = yaml_UTF8_ENCODING + } + } + if emitter.best_indent < 2 || emitter.best_indent > 9 { + emitter.best_indent = 2 + } + if emitter.best_width >= 0 && emitter.best_width <= emitter.best_indent*2 { + emitter.best_width = 80 + } + if emitter.best_width < 0 { + emitter.best_width = 1<<31 - 1 + } + if emitter.line_break == yaml_ANY_BREAK { + emitter.line_break = yaml_LN_BREAK + } + + emitter.indent = -1 + emitter.line = 0 + emitter.column = 0 + emitter.whitespace = true + emitter.indention = true + emitter.space_above = true + emitter.foot_indent = -1 + + if emitter.encoding != yaml_UTF8_ENCODING { + if !yaml_emitter_write_bom(emitter) { + return false + } + } + emitter.state = yaml_EMIT_FIRST_DOCUMENT_START_STATE + return true +} + +// Expect DOCUMENT-START or STREAM-END. +func yaml_emitter_emit_document_start(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { + + if event.typ == yaml_DOCUMENT_START_EVENT { + + if event.version_directive != nil { + if !yaml_emitter_analyze_version_directive(emitter, event.version_directive) { + return false + } + } + + for i := 0; i < len(event.tag_directives); i++ { + tag_directive := &event.tag_directives[i] + if !yaml_emitter_analyze_tag_directive(emitter, tag_directive) { + return false + } + if !yaml_emitter_append_tag_directive(emitter, tag_directive, false) { + return false + } + } + + for i := 0; i < len(default_tag_directives); i++ { + tag_directive := &default_tag_directives[i] + if !yaml_emitter_append_tag_directive(emitter, tag_directive, true) { + return false + } + } + + implicit := event.implicit + if !first || emitter.canonical { + implicit = false + } + + if emitter.open_ended && (event.version_directive != nil || len(event.tag_directives) > 0) { + if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + + if event.version_directive != nil { + implicit = false + if !yaml_emitter_write_indicator(emitter, []byte("%YAML"), true, false, false) { + return false + } + if !yaml_emitter_write_indicator(emitter, []byte("1.1"), true, false, false) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + + if len(event.tag_directives) > 0 { + implicit = false + for i := 0; i < len(event.tag_directives); i++ { + tag_directive := &event.tag_directives[i] + if !yaml_emitter_write_indicator(emitter, []byte("%TAG"), true, false, false) { + return false + } + if !yaml_emitter_write_tag_handle(emitter, tag_directive.handle) { + return false + } + if !yaml_emitter_write_tag_content(emitter, tag_directive.prefix, true) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + } + + if yaml_emitter_check_empty_document(emitter) { + implicit = false + } + if !implicit { + if !yaml_emitter_write_indent(emitter) { + return false + } + if !yaml_emitter_write_indicator(emitter, []byte("---"), true, false, false) { + return false + } + if emitter.canonical || true { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + } + + if len(emitter.head_comment) > 0 { + if !yaml_emitter_process_head_comment(emitter) { + return false + } + if !put_break(emitter) { + return false + } + } + + emitter.state = yaml_EMIT_DOCUMENT_CONTENT_STATE + return true + } + + if event.typ == yaml_STREAM_END_EVENT { + if emitter.open_ended { + if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !yaml_emitter_flush(emitter) { + return false + } + emitter.state = yaml_EMIT_END_STATE + return true + } + + return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-START or STREAM-END") +} + +// Expect the root node. +func yaml_emitter_emit_document_content(emitter *yaml_emitter_t, event *yaml_event_t) bool { + emitter.states = append(emitter.states, yaml_EMIT_DOCUMENT_END_STATE) + + if !yaml_emitter_process_head_comment(emitter) { + return false + } + if !yaml_emitter_emit_node(emitter, event, true, false, false, false) { + return false + } + if !yaml_emitter_process_line_comment(emitter) { + return false + } + if !yaml_emitter_process_foot_comment(emitter) { + return false + } + return true +} + +// Expect DOCUMENT-END. +func yaml_emitter_emit_document_end(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if event.typ != yaml_DOCUMENT_END_EVENT { + return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-END") + } + // [Go] Force document foot separation. + emitter.foot_indent = 0 + if !yaml_emitter_process_foot_comment(emitter) { + return false + } + emitter.foot_indent = -1 + if !yaml_emitter_write_indent(emitter) { + return false + } + if !event.implicit { + // [Go] Allocate the slice elsewhere. + if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !yaml_emitter_flush(emitter) { + return false + } + emitter.state = yaml_EMIT_DOCUMENT_START_STATE + emitter.tag_directives = emitter.tag_directives[:0] + return true +} + +// Expect a flow item node. +func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first, trail bool) bool { + if first { + if !yaml_emitter_write_indicator(emitter, []byte{'['}, true, true, false) { + return false + } + if !yaml_emitter_increase_indent(emitter, true, false) { + return false + } + emitter.flow_level++ + } + + if event.typ == yaml_SEQUENCE_END_EVENT { + if emitter.canonical && !first && !trail { + if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { + return false + } + } + emitter.flow_level-- + emitter.indent = emitter.indents[len(emitter.indents)-1] + emitter.indents = emitter.indents[:len(emitter.indents)-1] + if emitter.column == 0 || emitter.canonical && !first { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !yaml_emitter_write_indicator(emitter, []byte{']'}, false, false, false) { + return false + } + if !yaml_emitter_process_line_comment(emitter) { + return false + } + if !yaml_emitter_process_foot_comment(emitter) { + return false + } + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + + return true + } + + if !first && !trail { + if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { + return false + } + } + + if !yaml_emitter_process_head_comment(emitter) { + return false + } + if emitter.column == 0 { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + + if emitter.canonical || emitter.column > emitter.best_width { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 { + emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE) + } else { + emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE) + } + if !yaml_emitter_emit_node(emitter, event, false, true, false, false) { + return false + } + if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 { + if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { + return false + } + } + if !yaml_emitter_process_line_comment(emitter) { + return false + } + if !yaml_emitter_process_foot_comment(emitter) { + return false + } + return true +} + +// Expect a flow key node. +func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first, trail bool) bool { + if first { + if !yaml_emitter_write_indicator(emitter, []byte{'{'}, true, true, false) { + return false + } + if !yaml_emitter_increase_indent(emitter, true, false) { + return false + } + emitter.flow_level++ + } + + if event.typ == yaml_MAPPING_END_EVENT { + if (emitter.canonical || len(emitter.head_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0) && !first && !trail { + if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { + return false + } + } + if !yaml_emitter_process_head_comment(emitter) { + return false + } + emitter.flow_level-- + emitter.indent = emitter.indents[len(emitter.indents)-1] + emitter.indents = emitter.indents[:len(emitter.indents)-1] + if emitter.canonical && !first { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !yaml_emitter_write_indicator(emitter, []byte{'}'}, false, false, false) { + return false + } + if !yaml_emitter_process_line_comment(emitter) { + return false + } + if !yaml_emitter_process_foot_comment(emitter) { + return false + } + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + return true + } + + if !first && !trail { + if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { + return false + } + } + + if !yaml_emitter_process_head_comment(emitter) { + return false + } + + if emitter.column == 0 { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + + if emitter.canonical || emitter.column > emitter.best_width { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + + if !emitter.canonical && yaml_emitter_check_simple_key(emitter) { + emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE) + return yaml_emitter_emit_node(emitter, event, false, false, true, true) + } + if !yaml_emitter_write_indicator(emitter, []byte{'?'}, true, false, false) { + return false + } + emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_VALUE_STATE) + return yaml_emitter_emit_node(emitter, event, false, false, true, false) +} + +// Expect a flow value node. +func yaml_emitter_emit_flow_mapping_value(emitter *yaml_emitter_t, event *yaml_event_t, simple bool) bool { + if simple { + if !yaml_emitter_write_indicator(emitter, []byte{':'}, false, false, false) { + return false + } + } else { + if emitter.canonical || emitter.column > emitter.best_width { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !yaml_emitter_write_indicator(emitter, []byte{':'}, true, false, false) { + return false + } + } + if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 { + emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE) + } else { + emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_KEY_STATE) + } + if !yaml_emitter_emit_node(emitter, event, false, false, true, false) { + return false + } + if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 { + if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { + return false + } + } + if !yaml_emitter_process_line_comment(emitter) { + return false + } + if !yaml_emitter_process_foot_comment(emitter) { + return false + } + return true +} + +// Expect a block item node. +func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { + if first { + // [Go] The original logic here would not indent the sequence when inside a mapping. + // In Go we always indent it, but take the sequence indicator out of the indentation. + indentless := emitter.best_indent == 2 && emitter.mapping_context && !emitter.indention + original := emitter.indent + if !yaml_emitter_increase_indent(emitter, false, indentless) { + return false + } + if emitter.indent > original+2 { + emitter.indent -= 2 + } + } + if event.typ == yaml_SEQUENCE_END_EVENT { + emitter.indent = emitter.indents[len(emitter.indents)-1] + emitter.indents = emitter.indents[:len(emitter.indents)-1] + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + return true + } + if !yaml_emitter_process_head_comment(emitter) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + if !yaml_emitter_write_indicator(emitter, []byte{'-'}, true, false, true) { + return false + } + emitter.states = append(emitter.states, yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE) + if !yaml_emitter_emit_node(emitter, event, false, true, false, false) { + return false + } + if !yaml_emitter_process_line_comment(emitter) { + return false + } + if !yaml_emitter_process_foot_comment(emitter) { + return false + } + return true +} + +// Expect a block key node. +func yaml_emitter_emit_block_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { + if first { + if !yaml_emitter_increase_indent(emitter, false, false) { + return false + } + } + if !yaml_emitter_process_head_comment(emitter) { + return false + } + if event.typ == yaml_MAPPING_END_EVENT { + emitter.indent = emitter.indents[len(emitter.indents)-1] + emitter.indents = emitter.indents[:len(emitter.indents)-1] + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + return true + } + if !yaml_emitter_write_indent(emitter) { + return false + } + if yaml_emitter_check_simple_key(emitter) { + emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE) + return yaml_emitter_emit_node(emitter, event, false, false, true, true) + } + if !yaml_emitter_write_indicator(emitter, []byte{'?'}, true, false, true) { + return false + } + emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_VALUE_STATE) + return yaml_emitter_emit_node(emitter, event, false, false, true, false) +} + +// Expect a block value node. +func yaml_emitter_emit_block_mapping_value(emitter *yaml_emitter_t, event *yaml_event_t, simple bool) bool { + if simple { + if !yaml_emitter_write_indicator(emitter, []byte{':'}, false, false, false) { + return false + } + } else { + if !yaml_emitter_write_indent(emitter) { + return false + } + if !yaml_emitter_write_indicator(emitter, []byte{':'}, true, false, true) { + return false + } + } + emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_KEY_STATE) + if !yaml_emitter_emit_node(emitter, event, false, false, true, false) { + return false + } + if !yaml_emitter_process_line_comment(emitter) { + return false + } + if !yaml_emitter_process_foot_comment(emitter) { + return false + } + return true +} + +// Expect a node. +func yaml_emitter_emit_node(emitter *yaml_emitter_t, event *yaml_event_t, + root bool, sequence bool, mapping bool, simple_key bool) bool { + + emitter.root_context = root + emitter.sequence_context = sequence + emitter.mapping_context = mapping + emitter.simple_key_context = simple_key + + switch event.typ { + case yaml_ALIAS_EVENT: + return yaml_emitter_emit_alias(emitter, event) + case yaml_SCALAR_EVENT: + return yaml_emitter_emit_scalar(emitter, event) + case yaml_SEQUENCE_START_EVENT: + return yaml_emitter_emit_sequence_start(emitter, event) + case yaml_MAPPING_START_EVENT: + return yaml_emitter_emit_mapping_start(emitter, event) + default: + return yaml_emitter_set_emitter_error(emitter, + fmt.Sprintf("expected SCALAR, SEQUENCE-START, MAPPING-START, or ALIAS, but got %v", event.typ)) + } +} + +// Expect ALIAS. +func yaml_emitter_emit_alias(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if !yaml_emitter_process_anchor(emitter) { + return false + } + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + return true +} + +// Expect SCALAR. +func yaml_emitter_emit_scalar(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if !yaml_emitter_select_scalar_style(emitter, event) { + return false + } + if !yaml_emitter_process_anchor(emitter) { + return false + } + if !yaml_emitter_process_tag(emitter) { + return false + } + if !yaml_emitter_increase_indent(emitter, true, false) { + return false + } + if !yaml_emitter_process_scalar(emitter) { + return false + } + emitter.indent = emitter.indents[len(emitter.indents)-1] + emitter.indents = emitter.indents[:len(emitter.indents)-1] + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + return true +} + +// Expect SEQUENCE-START. +func yaml_emitter_emit_sequence_start(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if !yaml_emitter_process_anchor(emitter) { + return false + } + if !yaml_emitter_process_tag(emitter) { + return false + } + if emitter.flow_level > 0 || emitter.canonical || event.sequence_style() == yaml_FLOW_SEQUENCE_STYLE || + yaml_emitter_check_empty_sequence(emitter) { + emitter.state = yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE + } else { + emitter.state = yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE + } + return true +} + +// Expect MAPPING-START. +func yaml_emitter_emit_mapping_start(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if !yaml_emitter_process_anchor(emitter) { + return false + } + if !yaml_emitter_process_tag(emitter) { + return false + } + if emitter.flow_level > 0 || emitter.canonical || event.mapping_style() == yaml_FLOW_MAPPING_STYLE || + yaml_emitter_check_empty_mapping(emitter) { + emitter.state = yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE + } else { + emitter.state = yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE + } + return true +} + +// Check if the document content is an empty scalar. +func yaml_emitter_check_empty_document(emitter *yaml_emitter_t) bool { + return false // [Go] Huh? +} + +// Check if the next events represent an empty sequence. +func yaml_emitter_check_empty_sequence(emitter *yaml_emitter_t) bool { + if len(emitter.events)-emitter.events_head < 2 { + return false + } + return emitter.events[emitter.events_head].typ == yaml_SEQUENCE_START_EVENT && + emitter.events[emitter.events_head+1].typ == yaml_SEQUENCE_END_EVENT +} + +// Check if the next events represent an empty mapping. +func yaml_emitter_check_empty_mapping(emitter *yaml_emitter_t) bool { + if len(emitter.events)-emitter.events_head < 2 { + return false + } + return emitter.events[emitter.events_head].typ == yaml_MAPPING_START_EVENT && + emitter.events[emitter.events_head+1].typ == yaml_MAPPING_END_EVENT +} + +// Check if the next node can be expressed as a simple key. +func yaml_emitter_check_simple_key(emitter *yaml_emitter_t) bool { + length := 0 + switch emitter.events[emitter.events_head].typ { + case yaml_ALIAS_EVENT: + length += len(emitter.anchor_data.anchor) + case yaml_SCALAR_EVENT: + if emitter.scalar_data.multiline { + return false + } + length += len(emitter.anchor_data.anchor) + + len(emitter.tag_data.handle) + + len(emitter.tag_data.suffix) + + len(emitter.scalar_data.value) + case yaml_SEQUENCE_START_EVENT: + if !yaml_emitter_check_empty_sequence(emitter) { + return false + } + length += len(emitter.anchor_data.anchor) + + len(emitter.tag_data.handle) + + len(emitter.tag_data.suffix) + case yaml_MAPPING_START_EVENT: + if !yaml_emitter_check_empty_mapping(emitter) { + return false + } + length += len(emitter.anchor_data.anchor) + + len(emitter.tag_data.handle) + + len(emitter.tag_data.suffix) + default: + return false + } + return length <= 128 +} + +// Determine an acceptable scalar style. +func yaml_emitter_select_scalar_style(emitter *yaml_emitter_t, event *yaml_event_t) bool { + + no_tag := len(emitter.tag_data.handle) == 0 && len(emitter.tag_data.suffix) == 0 + if no_tag && !event.implicit && !event.quoted_implicit { + return yaml_emitter_set_emitter_error(emitter, "neither tag nor implicit flags are specified") + } + + style := event.scalar_style() + if style == yaml_ANY_SCALAR_STYLE { + style = yaml_PLAIN_SCALAR_STYLE + } + if emitter.canonical { + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + if emitter.simple_key_context && emitter.scalar_data.multiline { + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + + if style == yaml_PLAIN_SCALAR_STYLE { + if emitter.flow_level > 0 && !emitter.scalar_data.flow_plain_allowed || + emitter.flow_level == 0 && !emitter.scalar_data.block_plain_allowed { + style = yaml_SINGLE_QUOTED_SCALAR_STYLE + } + if len(emitter.scalar_data.value) == 0 && (emitter.flow_level > 0 || emitter.simple_key_context) { + style = yaml_SINGLE_QUOTED_SCALAR_STYLE + } + if no_tag && !event.implicit { + style = yaml_SINGLE_QUOTED_SCALAR_STYLE + } + } + if style == yaml_SINGLE_QUOTED_SCALAR_STYLE { + if !emitter.scalar_data.single_quoted_allowed { + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + } + if style == yaml_LITERAL_SCALAR_STYLE || style == yaml_FOLDED_SCALAR_STYLE { + if !emitter.scalar_data.block_allowed || emitter.flow_level > 0 || emitter.simple_key_context { + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + } + + if no_tag && !event.quoted_implicit && style != yaml_PLAIN_SCALAR_STYLE { + emitter.tag_data.handle = []byte{'!'} + } + emitter.scalar_data.style = style + return true +} + +// Write an anchor. +func yaml_emitter_process_anchor(emitter *yaml_emitter_t) bool { + if emitter.anchor_data.anchor == nil { + return true + } + c := []byte{'&'} + if emitter.anchor_data.alias { + c[0] = '*' + } + if !yaml_emitter_write_indicator(emitter, c, true, false, false) { + return false + } + return yaml_emitter_write_anchor(emitter, emitter.anchor_data.anchor) +} + +// Write a tag. +func yaml_emitter_process_tag(emitter *yaml_emitter_t) bool { + if len(emitter.tag_data.handle) == 0 && len(emitter.tag_data.suffix) == 0 { + return true + } + if len(emitter.tag_data.handle) > 0 { + if !yaml_emitter_write_tag_handle(emitter, emitter.tag_data.handle) { + return false + } + if len(emitter.tag_data.suffix) > 0 { + if !yaml_emitter_write_tag_content(emitter, emitter.tag_data.suffix, false) { + return false + } + } + } else { + // [Go] Allocate these slices elsewhere. + if !yaml_emitter_write_indicator(emitter, []byte("!<"), true, false, false) { + return false + } + if !yaml_emitter_write_tag_content(emitter, emitter.tag_data.suffix, false) { + return false + } + if !yaml_emitter_write_indicator(emitter, []byte{'>'}, false, false, false) { + return false + } + } + return true +} + +// Write a scalar. +func yaml_emitter_process_scalar(emitter *yaml_emitter_t) bool { + switch emitter.scalar_data.style { + case yaml_PLAIN_SCALAR_STYLE: + return yaml_emitter_write_plain_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context) + + case yaml_SINGLE_QUOTED_SCALAR_STYLE: + return yaml_emitter_write_single_quoted_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context) + + case yaml_DOUBLE_QUOTED_SCALAR_STYLE: + return yaml_emitter_write_double_quoted_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context) + + case yaml_LITERAL_SCALAR_STYLE: + return yaml_emitter_write_literal_scalar(emitter, emitter.scalar_data.value) + + case yaml_FOLDED_SCALAR_STYLE: + return yaml_emitter_write_folded_scalar(emitter, emitter.scalar_data.value) + } + panic("unknown scalar style") +} + +// Write a head comment. +func yaml_emitter_process_head_comment(emitter *yaml_emitter_t) bool { + if len(emitter.tail_comment) > 0 { + if !yaml_emitter_write_indent(emitter) { + return false + } + if !yaml_emitter_write_comment(emitter, emitter.tail_comment) { + return false + } + emitter.tail_comment = emitter.tail_comment[:0] + emitter.foot_indent = emitter.indent + if emitter.foot_indent < 0 { + emitter.foot_indent = 0 + } + } + + if len(emitter.head_comment) == 0 { + return true + } + if !yaml_emitter_write_indent(emitter) { + return false + } + if !yaml_emitter_write_comment(emitter, emitter.head_comment) { + return false + } + emitter.head_comment = emitter.head_comment[:0] + return true +} + +// Write an line comment. +func yaml_emitter_process_line_comment(emitter *yaml_emitter_t) bool { + if len(emitter.line_comment) == 0 { + return true + } + if !emitter.whitespace { + if !put(emitter, ' ') { + return false + } + } + if !yaml_emitter_write_comment(emitter, emitter.line_comment) { + return false + } + emitter.line_comment = emitter.line_comment[:0] + return true +} + +// Write a foot comment. +func yaml_emitter_process_foot_comment(emitter *yaml_emitter_t) bool { + if len(emitter.foot_comment) == 0 { + return true + } + if !yaml_emitter_write_indent(emitter) { + return false + } + if !yaml_emitter_write_comment(emitter, emitter.foot_comment) { + return false + } + emitter.foot_comment = emitter.foot_comment[:0] + emitter.foot_indent = emitter.indent + if emitter.foot_indent < 0 { + emitter.foot_indent = 0 + } + return true +} + +// Check if a %YAML directive is valid. +func yaml_emitter_analyze_version_directive(emitter *yaml_emitter_t, version_directive *yaml_version_directive_t) bool { + if version_directive.major != 1 || version_directive.minor != 1 { + return yaml_emitter_set_emitter_error(emitter, "incompatible %YAML directive") + } + return true +} + +// Check if a %TAG directive is valid. +func yaml_emitter_analyze_tag_directive(emitter *yaml_emitter_t, tag_directive *yaml_tag_directive_t) bool { + handle := tag_directive.handle + prefix := tag_directive.prefix + if len(handle) == 0 { + return yaml_emitter_set_emitter_error(emitter, "tag handle must not be empty") + } + if handle[0] != '!' { + return yaml_emitter_set_emitter_error(emitter, "tag handle must start with '!'") + } + if handle[len(handle)-1] != '!' { + return yaml_emitter_set_emitter_error(emitter, "tag handle must end with '!'") + } + for i := 1; i < len(handle)-1; i += width(handle[i]) { + if !is_alpha(handle, i) { + return yaml_emitter_set_emitter_error(emitter, "tag handle must contain alphanumerical characters only") + } + } + if len(prefix) == 0 { + return yaml_emitter_set_emitter_error(emitter, "tag prefix must not be empty") + } + return true +} + +// Check if an anchor is valid. +func yaml_emitter_analyze_anchor(emitter *yaml_emitter_t, anchor []byte, alias bool) bool { + if len(anchor) == 0 { + problem := "anchor value must not be empty" + if alias { + problem = "alias value must not be empty" + } + return yaml_emitter_set_emitter_error(emitter, problem) + } + for i := 0; i < len(anchor); i += width(anchor[i]) { + if !is_alpha(anchor, i) { + problem := "anchor value must contain alphanumerical characters only" + if alias { + problem = "alias value must contain alphanumerical characters only" + } + return yaml_emitter_set_emitter_error(emitter, problem) + } + } + emitter.anchor_data.anchor = anchor + emitter.anchor_data.alias = alias + return true +} + +// Check if a tag is valid. +func yaml_emitter_analyze_tag(emitter *yaml_emitter_t, tag []byte) bool { + if len(tag) == 0 { + return yaml_emitter_set_emitter_error(emitter, "tag value must not be empty") + } + for i := 0; i < len(emitter.tag_directives); i++ { + tag_directive := &emitter.tag_directives[i] + if bytes.HasPrefix(tag, tag_directive.prefix) { + emitter.tag_data.handle = tag_directive.handle + emitter.tag_data.suffix = tag[len(tag_directive.prefix):] + return true + } + } + emitter.tag_data.suffix = tag + return true +} + +// Check if a scalar is valid. +func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool { + var ( + block_indicators = false + flow_indicators = false + line_breaks = false + special_characters = false + tab_characters = false + + leading_space = false + leading_break = false + trailing_space = false + trailing_break = false + break_space = false + space_break = false + + preceded_by_whitespace = false + followed_by_whitespace = false + previous_space = false + previous_break = false + ) + + emitter.scalar_data.value = value + + if len(value) == 0 { + emitter.scalar_data.multiline = false + emitter.scalar_data.flow_plain_allowed = false + emitter.scalar_data.block_plain_allowed = true + emitter.scalar_data.single_quoted_allowed = true + emitter.scalar_data.block_allowed = false + return true + } + + if len(value) >= 3 && ((value[0] == '-' && value[1] == '-' && value[2] == '-') || (value[0] == '.' && value[1] == '.' && value[2] == '.')) { + block_indicators = true + flow_indicators = true + } + + preceded_by_whitespace = true + for i, w := 0, 0; i < len(value); i += w { + w = width(value[i]) + followed_by_whitespace = i+w >= len(value) || is_blank(value, i+w) + + if i == 0 { + switch value[i] { + case '#', ',', '[', ']', '{', '}', '&', '*', '!', '|', '>', '\'', '"', '%', '@', '`': + flow_indicators = true + block_indicators = true + case '?', ':': + flow_indicators = true + if followed_by_whitespace { + block_indicators = true + } + case '-': + if followed_by_whitespace { + flow_indicators = true + block_indicators = true + } + } + } else { + switch value[i] { + case ',', '?', '[', ']', '{', '}': + flow_indicators = true + case ':': + flow_indicators = true + if followed_by_whitespace { + block_indicators = true + } + case '#': + if preceded_by_whitespace { + flow_indicators = true + block_indicators = true + } + } + } + + if value[i] == '\t' { + tab_characters = true + } else if !is_printable(value, i) || !is_ascii(value, i) && !emitter.unicode { + special_characters = true + } + if is_space(value, i) { + if i == 0 { + leading_space = true + } + if i+width(value[i]) == len(value) { + trailing_space = true + } + if previous_break { + break_space = true + } + previous_space = true + previous_break = false + } else if is_break(value, i) { + line_breaks = true + if i == 0 { + leading_break = true + } + if i+width(value[i]) == len(value) { + trailing_break = true + } + if previous_space { + space_break = true + } + previous_space = false + previous_break = true + } else { + previous_space = false + previous_break = false + } + + // [Go]: Why 'z'? Couldn't be the end of the string as that's the loop condition. + preceded_by_whitespace = is_blankz(value, i) + } + + emitter.scalar_data.multiline = line_breaks + emitter.scalar_data.flow_plain_allowed = true + emitter.scalar_data.block_plain_allowed = true + emitter.scalar_data.single_quoted_allowed = true + emitter.scalar_data.block_allowed = true + + if leading_space || leading_break || trailing_space || trailing_break { + emitter.scalar_data.flow_plain_allowed = false + emitter.scalar_data.block_plain_allowed = false + } + if trailing_space { + emitter.scalar_data.block_allowed = false + } + if break_space { + emitter.scalar_data.flow_plain_allowed = false + emitter.scalar_data.block_plain_allowed = false + emitter.scalar_data.single_quoted_allowed = false + } + if space_break || tab_characters || special_characters { + emitter.scalar_data.flow_plain_allowed = false + emitter.scalar_data.block_plain_allowed = false + emitter.scalar_data.single_quoted_allowed = false + } + if space_break || special_characters { + emitter.scalar_data.block_allowed = false + } + if line_breaks { + emitter.scalar_data.flow_plain_allowed = false + emitter.scalar_data.block_plain_allowed = false + } + if flow_indicators { + emitter.scalar_data.flow_plain_allowed = false + } + if block_indicators { + emitter.scalar_data.block_plain_allowed = false + } + return true +} + +// Check if the event data is valid. +func yaml_emitter_analyze_event(emitter *yaml_emitter_t, event *yaml_event_t) bool { + + emitter.anchor_data.anchor = nil + emitter.tag_data.handle = nil + emitter.tag_data.suffix = nil + emitter.scalar_data.value = nil + + if len(event.head_comment) > 0 { + emitter.head_comment = event.head_comment + } + if len(event.line_comment) > 0 { + emitter.line_comment = event.line_comment + } + if len(event.foot_comment) > 0 { + emitter.foot_comment = event.foot_comment + } + if len(event.tail_comment) > 0 { + emitter.tail_comment = event.tail_comment + } + + switch event.typ { + case yaml_ALIAS_EVENT: + if !yaml_emitter_analyze_anchor(emitter, event.anchor, true) { + return false + } + + case yaml_SCALAR_EVENT: + if len(event.anchor) > 0 { + if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) { + return false + } + } + if len(event.tag) > 0 && (emitter.canonical || (!event.implicit && !event.quoted_implicit)) { + if !yaml_emitter_analyze_tag(emitter, event.tag) { + return false + } + } + if !yaml_emitter_analyze_scalar(emitter, event.value) { + return false + } + + case yaml_SEQUENCE_START_EVENT: + if len(event.anchor) > 0 { + if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) { + return false + } + } + if len(event.tag) > 0 && (emitter.canonical || !event.implicit) { + if !yaml_emitter_analyze_tag(emitter, event.tag) { + return false + } + } + + case yaml_MAPPING_START_EVENT: + if len(event.anchor) > 0 { + if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) { + return false + } + } + if len(event.tag) > 0 && (emitter.canonical || !event.implicit) { + if !yaml_emitter_analyze_tag(emitter, event.tag) { + return false + } + } + } + return true +} + +// Write the BOM character. +func yaml_emitter_write_bom(emitter *yaml_emitter_t) bool { + if !flush(emitter) { + return false + } + pos := emitter.buffer_pos + emitter.buffer[pos+0] = '\xEF' + emitter.buffer[pos+1] = '\xBB' + emitter.buffer[pos+2] = '\xBF' + emitter.buffer_pos += 3 + return true +} + +func yaml_emitter_write_indent(emitter *yaml_emitter_t) bool { + indent := emitter.indent + if indent < 0 { + indent = 0 + } + if !emitter.indention || emitter.column > indent || (emitter.column == indent && !emitter.whitespace) { + if !put_break(emitter) { + return false + } + } + if emitter.foot_indent == indent { + if !put_break(emitter) { + return false + } + } + for emitter.column < indent { + if !put(emitter, ' ') { + return false + } + } + emitter.whitespace = true + //emitter.indention = true + emitter.space_above = false + emitter.foot_indent = -1 + return true +} + +func yaml_emitter_write_indicator(emitter *yaml_emitter_t, indicator []byte, need_whitespace, is_whitespace, is_indention bool) bool { + if need_whitespace && !emitter.whitespace { + if !put(emitter, ' ') { + return false + } + } + if !write_all(emitter, indicator) { + return false + } + emitter.whitespace = is_whitespace + emitter.indention = (emitter.indention && is_indention) + emitter.open_ended = false + return true +} + +func yaml_emitter_write_anchor(emitter *yaml_emitter_t, value []byte) bool { + if !write_all(emitter, value) { + return false + } + emitter.whitespace = false + emitter.indention = false + return true +} + +func yaml_emitter_write_tag_handle(emitter *yaml_emitter_t, value []byte) bool { + if !emitter.whitespace { + if !put(emitter, ' ') { + return false + } + } + if !write_all(emitter, value) { + return false + } + emitter.whitespace = false + emitter.indention = false + return true +} + +func yaml_emitter_write_tag_content(emitter *yaml_emitter_t, value []byte, need_whitespace bool) bool { + if need_whitespace && !emitter.whitespace { + if !put(emitter, ' ') { + return false + } + } + for i := 0; i < len(value); { + var must_write bool + switch value[i] { + case ';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '_', '.', '~', '*', '\'', '(', ')', '[', ']': + must_write = true + default: + must_write = is_alpha(value, i) + } + if must_write { + if !write(emitter, value, &i) { + return false + } + } else { + w := width(value[i]) + for k := 0; k < w; k++ { + octet := value[i] + i++ + if !put(emitter, '%') { + return false + } + + c := octet >> 4 + if c < 10 { + c += '0' + } else { + c += 'A' - 10 + } + if !put(emitter, c) { + return false + } + + c = octet & 0x0f + if c < 10 { + c += '0' + } else { + c += 'A' - 10 + } + if !put(emitter, c) { + return false + } + } + } + } + emitter.whitespace = false + emitter.indention = false + return true +} + +func yaml_emitter_write_plain_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool { + if len(value) > 0 && !emitter.whitespace { + if !put(emitter, ' ') { + return false + } + } + + spaces := false + breaks := false + for i := 0; i < len(value); { + if is_space(value, i) { + if allow_breaks && !spaces && emitter.column > emitter.best_width && !is_space(value, i+1) { + if !yaml_emitter_write_indent(emitter) { + return false + } + i += width(value[i]) + } else { + if !write(emitter, value, &i) { + return false + } + } + spaces = true + } else if is_break(value, i) { + if !breaks && value[i] == '\n' { + if !put_break(emitter) { + return false + } + } + if !write_break(emitter, value, &i) { + return false + } + //emitter.indention = true + breaks = true + } else { + if breaks { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !write(emitter, value, &i) { + return false + } + emitter.indention = false + spaces = false + breaks = false + } + } + + if len(value) > 0 { + emitter.whitespace = false + } + emitter.indention = false + if emitter.root_context { + emitter.open_ended = true + } + + return true +} + +func yaml_emitter_write_single_quoted_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool { + + if !yaml_emitter_write_indicator(emitter, []byte{'\''}, true, false, false) { + return false + } + + spaces := false + breaks := false + for i := 0; i < len(value); { + if is_space(value, i) { + if allow_breaks && !spaces && emitter.column > emitter.best_width && i > 0 && i < len(value)-1 && !is_space(value, i+1) { + if !yaml_emitter_write_indent(emitter) { + return false + } + i += width(value[i]) + } else { + if !write(emitter, value, &i) { + return false + } + } + spaces = true + } else if is_break(value, i) { + if !breaks && value[i] == '\n' { + if !put_break(emitter) { + return false + } + } + if !write_break(emitter, value, &i) { + return false + } + //emitter.indention = true + breaks = true + } else { + if breaks { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if value[i] == '\'' { + if !put(emitter, '\'') { + return false + } + } + if !write(emitter, value, &i) { + return false + } + emitter.indention = false + spaces = false + breaks = false + } + } + if !yaml_emitter_write_indicator(emitter, []byte{'\''}, false, false, false) { + return false + } + emitter.whitespace = false + emitter.indention = false + return true +} + +func yaml_emitter_write_double_quoted_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool { + spaces := false + if !yaml_emitter_write_indicator(emitter, []byte{'"'}, true, false, false) { + return false + } + + for i := 0; i < len(value); { + if !is_printable(value, i) || (!emitter.unicode && !is_ascii(value, i)) || + is_bom(value, i) || is_break(value, i) || + value[i] == '"' || value[i] == '\\' { + + octet := value[i] + + var w int + var v rune + switch { + case octet&0x80 == 0x00: + w, v = 1, rune(octet&0x7F) + case octet&0xE0 == 0xC0: + w, v = 2, rune(octet&0x1F) + case octet&0xF0 == 0xE0: + w, v = 3, rune(octet&0x0F) + case octet&0xF8 == 0xF0: + w, v = 4, rune(octet&0x07) + } + for k := 1; k < w; k++ { + octet = value[i+k] + v = (v << 6) + (rune(octet) & 0x3F) + } + i += w + + if !put(emitter, '\\') { + return false + } + + var ok bool + switch v { + case 0x00: + ok = put(emitter, '0') + case 0x07: + ok = put(emitter, 'a') + case 0x08: + ok = put(emitter, 'b') + case 0x09: + ok = put(emitter, 't') + case 0x0A: + ok = put(emitter, 'n') + case 0x0b: + ok = put(emitter, 'v') + case 0x0c: + ok = put(emitter, 'f') + case 0x0d: + ok = put(emitter, 'r') + case 0x1b: + ok = put(emitter, 'e') + case 0x22: + ok = put(emitter, '"') + case 0x5c: + ok = put(emitter, '\\') + case 0x85: + ok = put(emitter, 'N') + case 0xA0: + ok = put(emitter, '_') + case 0x2028: + ok = put(emitter, 'L') + case 0x2029: + ok = put(emitter, 'P') + default: + if v <= 0xFF { + ok = put(emitter, 'x') + w = 2 + } else if v <= 0xFFFF { + ok = put(emitter, 'u') + w = 4 + } else { + ok = put(emitter, 'U') + w = 8 + } + for k := (w - 1) * 4; ok && k >= 0; k -= 4 { + digit := byte((v >> uint(k)) & 0x0F) + if digit < 10 { + ok = put(emitter, digit+'0') + } else { + ok = put(emitter, digit+'A'-10) + } + } + } + if !ok { + return false + } + spaces = false + } else if is_space(value, i) { + if allow_breaks && !spaces && emitter.column > emitter.best_width && i > 0 && i < len(value)-1 { + if !yaml_emitter_write_indent(emitter) { + return false + } + if is_space(value, i+1) { + if !put(emitter, '\\') { + return false + } + } + i += width(value[i]) + } else if !write(emitter, value, &i) { + return false + } + spaces = true + } else { + if !write(emitter, value, &i) { + return false + } + spaces = false + } + } + if !yaml_emitter_write_indicator(emitter, []byte{'"'}, false, false, false) { + return false + } + emitter.whitespace = false + emitter.indention = false + return true +} + +func yaml_emitter_write_block_scalar_hints(emitter *yaml_emitter_t, value []byte) bool { + if is_space(value, 0) || is_break(value, 0) { + indent_hint := []byte{'0' + byte(emitter.best_indent)} + if !yaml_emitter_write_indicator(emitter, indent_hint, false, false, false) { + return false + } + } + + emitter.open_ended = false + + var chomp_hint [1]byte + if len(value) == 0 { + chomp_hint[0] = '-' + } else { + i := len(value) - 1 + for value[i]&0xC0 == 0x80 { + i-- + } + if !is_break(value, i) { + chomp_hint[0] = '-' + } else if i == 0 { + chomp_hint[0] = '+' + emitter.open_ended = true + } else { + i-- + for value[i]&0xC0 == 0x80 { + i-- + } + if is_break(value, i) { + chomp_hint[0] = '+' + emitter.open_ended = true + } + } + } + if chomp_hint[0] != 0 { + if !yaml_emitter_write_indicator(emitter, chomp_hint[:], false, false, false) { + return false + } + } + return true +} + +func yaml_emitter_write_literal_scalar(emitter *yaml_emitter_t, value []byte) bool { + if !yaml_emitter_write_indicator(emitter, []byte{'|'}, true, false, false) { + return false + } + if !yaml_emitter_write_block_scalar_hints(emitter, value) { + return false + } + if !put_break(emitter) { + return false + } + //emitter.indention = true + emitter.whitespace = true + breaks := true + for i := 0; i < len(value); { + if is_break(value, i) { + if !write_break(emitter, value, &i) { + return false + } + //emitter.indention = true + breaks = true + } else { + if breaks { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !write(emitter, value, &i) { + return false + } + emitter.indention = false + breaks = false + } + } + + return true +} + +func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) bool { + if !yaml_emitter_write_indicator(emitter, []byte{'>'}, true, false, false) { + return false + } + if !yaml_emitter_write_block_scalar_hints(emitter, value) { + return false + } + + if !put_break(emitter) { + return false + } + //emitter.indention = true + emitter.whitespace = true + + breaks := true + leading_spaces := true + for i := 0; i < len(value); { + if is_break(value, i) { + if !breaks && !leading_spaces && value[i] == '\n' { + k := 0 + for is_break(value, k) { + k += width(value[k]) + } + if !is_blankz(value, k) { + if !put_break(emitter) { + return false + } + } + } + if !write_break(emitter, value, &i) { + return false + } + //emitter.indention = true + breaks = true + } else { + if breaks { + if !yaml_emitter_write_indent(emitter) { + return false + } + leading_spaces = is_blank(value, i) + } + if !breaks && is_space(value, i) && !is_space(value, i+1) && emitter.column > emitter.best_width { + if !yaml_emitter_write_indent(emitter) { + return false + } + i += width(value[i]) + } else { + if !write(emitter, value, &i) { + return false + } + } + emitter.indention = false + breaks = false + } + } + return true +} + +func yaml_emitter_write_comment(emitter *yaml_emitter_t, comment []byte) bool { + breaks := false + pound := false + for i := 0; i < len(comment); { + if is_break(comment, i) { + if !write_break(emitter, comment, &i) { + return false + } + //emitter.indention = true + breaks = true + pound = false + } else { + if breaks && !yaml_emitter_write_indent(emitter) { + return false + } + if !pound { + if comment[i] != '#' && (!put(emitter, '#') || !put(emitter, ' ')) { + return false + } + pound = true + } + if !write(emitter, comment, &i) { + return false + } + emitter.indention = false + breaks = false + } + } + if !breaks && !put_break(emitter) { + return false + } + + emitter.whitespace = true + //emitter.indention = true + return true +} diff --git a/vendor/gopkg.in/yaml.v3/encode.go b/vendor/gopkg.in/yaml.v3/encode.go new file mode 100644 index 0000000000..38882b50e1 --- /dev/null +++ b/vendor/gopkg.in/yaml.v3/encode.go @@ -0,0 +1,542 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// +// 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 yaml + +import ( + "encoding" + "fmt" + "io" + "reflect" + "regexp" + "sort" + "strconv" + "strings" + "time" + "unicode/utf8" +) + +type encoder struct { + emitter yaml_emitter_t + event yaml_event_t + out []byte + flow bool + indent int + doneInit bool +} + +func newEncoder() *encoder { + e := &encoder{} + yaml_emitter_initialize(&e.emitter) + yaml_emitter_set_output_string(&e.emitter, &e.out) + yaml_emitter_set_unicode(&e.emitter, true) + return e +} + +func newEncoderWithWriter(w io.Writer) *encoder { + e := &encoder{} + yaml_emitter_initialize(&e.emitter) + yaml_emitter_set_output_writer(&e.emitter, w) + yaml_emitter_set_unicode(&e.emitter, true) + return e +} + +func (e *encoder) init() { + if e.doneInit { + return + } + if e.indent == 0 { + e.indent = 4 + } + e.emitter.best_indent = e.indent + yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING) + e.emit() + e.doneInit = true +} + +func (e *encoder) finish() { + e.emitter.open_ended = false + yaml_stream_end_event_initialize(&e.event) + e.emit() +} + +func (e *encoder) destroy() { + yaml_emitter_delete(&e.emitter) +} + +func (e *encoder) emit() { + // This will internally delete the e.event value. + e.must(yaml_emitter_emit(&e.emitter, &e.event)) +} + +func (e *encoder) must(ok bool) { + if !ok { + msg := e.emitter.problem + if msg == "" { + msg = "unknown problem generating YAML content" + } + failf("%s", msg) + } +} + +func (e *encoder) marshalDoc(tag string, in reflect.Value) { + e.init() + var node *Node + if in.IsValid() { + node, _ = in.Interface().(*Node) + } + if node != nil && node.Kind == DocumentNode { + e.nodev(in) + } else { + yaml_document_start_event_initialize(&e.event, nil, nil, true) + e.emit() + e.marshal(tag, in) + yaml_document_end_event_initialize(&e.event, true) + e.emit() + } +} + +func (e *encoder) marshal(tag string, in reflect.Value) { + tag = shortTag(tag) + if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() { + e.nilv() + return + } + iface := in.Interface() + switch value := iface.(type) { + case *Node: + e.nodev(in) + return + case time.Time: + e.timev(tag, in) + return + case *time.Time: + e.timev(tag, in.Elem()) + return + case time.Duration: + e.stringv(tag, reflect.ValueOf(value.String())) + return + case Marshaler: + v, err := value.MarshalYAML() + if err != nil { + fail(err) + } + if v == nil { + e.nilv() + return + } + e.marshal(tag, reflect.ValueOf(v)) + return + case encoding.TextMarshaler: + text, err := value.MarshalText() + if err != nil { + fail(err) + } + in = reflect.ValueOf(string(text)) + case nil: + e.nilv() + return + } + switch in.Kind() { + case reflect.Interface: + e.marshal(tag, in.Elem()) + case reflect.Map: + e.mapv(tag, in) + case reflect.Ptr: + e.marshal(tag, in.Elem()) + case reflect.Struct: + e.structv(tag, in) + case reflect.Slice, reflect.Array: + e.slicev(tag, in) + case reflect.String: + e.stringv(tag, in) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + e.intv(tag, in) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + e.uintv(tag, in) + case reflect.Float32, reflect.Float64: + e.floatv(tag, in) + case reflect.Bool: + e.boolv(tag, in) + default: + panic("cannot marshal type: " + in.Type().String()) + } +} + +func (e *encoder) mapv(tag string, in reflect.Value) { + e.mappingv(tag, func() { + keys := keyList(in.MapKeys()) + sort.Sort(keys) + for _, k := range keys { + e.marshal("", k) + e.marshal("", in.MapIndex(k)) + } + }) +} + +func (e *encoder) fieldByIndex(v reflect.Value, index []int) (field reflect.Value) { + for _, num := range index { + for { + if v.Kind() == reflect.Ptr { + if v.IsNil() { + return reflect.Value{} + } + v = v.Elem() + continue + } + break + } + v = v.Field(num) + } + return v +} + +func (e *encoder) structv(tag string, in reflect.Value) { + sinfo, err := getStructInfo(in.Type()) + if err != nil { + panic(err) + } + e.mappingv(tag, func() { + for _, info := range sinfo.FieldsList { + var value reflect.Value + if info.Inline == nil { + value = in.Field(info.Num) + } else { + value = e.fieldByIndex(in, info.Inline) + if !value.IsValid() { + continue + } + } + if info.OmitEmpty && isZero(value) { + continue + } + e.marshal("", reflect.ValueOf(info.Key)) + e.flow = info.Flow + e.marshal("", value) + } + if sinfo.InlineMap >= 0 { + m := in.Field(sinfo.InlineMap) + if m.Len() > 0 { + e.flow = false + keys := keyList(m.MapKeys()) + sort.Sort(keys) + for _, k := range keys { + if _, found := sinfo.FieldsMap[k.String()]; found { + panic(fmt.Sprintf("cannot have key %q in inlined map: conflicts with struct field", k.String())) + } + e.marshal("", k) + e.flow = false + e.marshal("", m.MapIndex(k)) + } + } + } + }) +} + +func (e *encoder) mappingv(tag string, f func()) { + implicit := tag == "" + style := yaml_BLOCK_MAPPING_STYLE + if e.flow { + e.flow = false + style = yaml_FLOW_MAPPING_STYLE + } + yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style) + e.emit() + f() + yaml_mapping_end_event_initialize(&e.event) + e.emit() +} + +func (e *encoder) slicev(tag string, in reflect.Value) { + implicit := tag == "" + style := yaml_BLOCK_SEQUENCE_STYLE + if e.flow { + e.flow = false + style = yaml_FLOW_SEQUENCE_STYLE + } + e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) + e.emit() + n := in.Len() + for i := 0; i < n; i++ { + e.marshal("", in.Index(i)) + } + e.must(yaml_sequence_end_event_initialize(&e.event)) + e.emit() +} + +// isBase60 returns whether s is in base 60 notation as defined in YAML 1.1. +// +// The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported +// in YAML 1.2 and by this package, but these should be marshalled quoted for +// the time being for compatibility with other parsers. +func isBase60Float(s string) (result bool) { + // Fast path. + if s == "" { + return false + } + c := s[0] + if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 { + return false + } + // Do the full match. + return base60float.MatchString(s) +} + +// From http://yaml.org/type/float.html, except the regular expression there +// is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix. +var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`) + +func (e *encoder) stringv(tag string, in reflect.Value) { + var style yaml_scalar_style_t + s := in.String() + canUsePlain := true + switch { + case !utf8.ValidString(s): + if tag == binaryTag { + failf("explicitly tagged !!binary data must be base64-encoded") + } + if tag != "" { + failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag)) + } + // It can't be encoded directly as YAML so use a binary tag + // and encode it as base64. + tag = binaryTag + s = encodeBase64(s) + case tag == "": + // Check to see if it would resolve to a specific + // tag when encoded unquoted. If it doesn't, + // there's no need to quote it. + rtag, _ := resolve("", s) + canUsePlain = rtag == strTag && !isBase60Float(s) + } + // Note: it's possible for user code to emit invalid YAML + // if they explicitly specify a tag and a string containing + // text that's incompatible with that tag. + switch { + case strings.Contains(s, "\n"): + if e.flow { + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } else { + style = yaml_LITERAL_SCALAR_STYLE + } + case canUsePlain: + style = yaml_PLAIN_SCALAR_STYLE + default: + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + e.emitScalar(s, "", tag, style, nil, nil, nil, nil) +} + +func (e *encoder) boolv(tag string, in reflect.Value) { + var s string + if in.Bool() { + s = "true" + } else { + s = "false" + } + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) +} + +func (e *encoder) intv(tag string, in reflect.Value) { + s := strconv.FormatInt(in.Int(), 10) + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) +} + +func (e *encoder) uintv(tag string, in reflect.Value) { + s := strconv.FormatUint(in.Uint(), 10) + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) +} + +func (e *encoder) timev(tag string, in reflect.Value) { + t := in.Interface().(time.Time) + s := t.Format(time.RFC3339Nano) + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) +} + +func (e *encoder) floatv(tag string, in reflect.Value) { + // Issue #352: When formatting, use the precision of the underlying value + precision := 64 + if in.Kind() == reflect.Float32 { + precision = 32 + } + + s := strconv.FormatFloat(in.Float(), 'g', -1, precision) + switch s { + case "+Inf": + s = ".inf" + case "-Inf": + s = "-.inf" + case "NaN": + s = ".nan" + } + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) +} + +func (e *encoder) nilv() { + e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) +} + +func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t, head, line, foot, tail []byte) { + // TODO Kill this function. Replace all initialize calls by their underlining Go literals. + implicit := tag == "" + if !implicit { + tag = longTag(tag) + } + e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style)) + e.event.head_comment = head + e.event.line_comment = line + e.event.foot_comment = foot + e.event.tail_comment = tail + e.emit() +} + +func (e *encoder) nodev(in reflect.Value) { + e.node(in.Interface().(*Node), "") +} + +func (e *encoder) node(node *Node, tail string) { + // If the tag was not explicitly requested, and dropping it won't change the + // implicit tag of the value, don't include it in the presentation. + var tag = node.Tag + var stag = shortTag(tag) + var rtag string + var forceQuoting bool + if tag != "" && node.Style&TaggedStyle == 0 { + if node.Kind == ScalarNode { + if stag == strTag && node.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0 { + tag = "" + } else { + rtag, _ = resolve("", node.Value) + if rtag == stag { + tag = "" + } else if stag == strTag { + tag = "" + forceQuoting = true + } + } + } else { + switch node.Kind { + case MappingNode: + rtag = mapTag + case SequenceNode: + rtag = seqTag + } + if rtag == stag { + tag = "" + } + } + } + + switch node.Kind { + case DocumentNode: + yaml_document_start_event_initialize(&e.event, nil, nil, true) + e.event.head_comment = []byte(node.HeadComment) + e.emit() + for _, node := range node.Content { + e.node(node, "") + } + yaml_document_end_event_initialize(&e.event, true) + e.event.foot_comment = []byte(node.FootComment) + e.emit() + + case SequenceNode: + style := yaml_BLOCK_SEQUENCE_STYLE + if node.Style&FlowStyle != 0 { + style = yaml_FLOW_SEQUENCE_STYLE + } + e.must(yaml_sequence_start_event_initialize(&e.event, []byte(node.Anchor), []byte(tag), tag == "", style)) + e.event.head_comment = []byte(node.HeadComment) + e.emit() + for _, node := range node.Content { + e.node(node, "") + } + e.must(yaml_sequence_end_event_initialize(&e.event)) + e.event.line_comment = []byte(node.LineComment) + e.event.foot_comment = []byte(node.FootComment) + e.emit() + + case MappingNode: + style := yaml_BLOCK_MAPPING_STYLE + if node.Style&FlowStyle != 0 { + style = yaml_FLOW_MAPPING_STYLE + } + yaml_mapping_start_event_initialize(&e.event, []byte(node.Anchor), []byte(tag), tag == "", style) + e.event.tail_comment = []byte(tail) + e.event.head_comment = []byte(node.HeadComment) + e.emit() + + // The tail logic below moves the foot comment of prior keys to the following key, + // since the value for each key may be a nested structure and the foot needs to be + // processed only the entirety of the value is streamed. The last tail is processed + // with the mapping end event. + var tail string + for i := 0; i+1 < len(node.Content); i += 2 { + k := node.Content[i] + foot := k.FootComment + k.FootComment = "" + e.node(k, tail) + tail = foot + + v := node.Content[i+1] + e.node(v, "") + } + + yaml_mapping_end_event_initialize(&e.event) + e.event.tail_comment = []byte(tail) + e.event.line_comment = []byte(node.LineComment) + e.event.foot_comment = []byte(node.FootComment) + e.emit() + + case AliasNode: + yaml_alias_event_initialize(&e.event, []byte(node.Value)) + e.event.head_comment = []byte(node.HeadComment) + e.event.line_comment = []byte(node.LineComment) + e.event.foot_comment = []byte(node.FootComment) + e.emit() + + case ScalarNode: + value := node.Value + if !utf8.ValidString(value) { + if tag == binaryTag { + failf("explicitly tagged !!binary data must be base64-encoded") + } + if tag != "" { + failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag)) + } + // It can't be encoded directly as YAML so use a binary tag + // and encode it as base64. + tag = binaryTag + value = encodeBase64(value) + } + + style := yaml_PLAIN_SCALAR_STYLE + switch { + case node.Style&DoubleQuotedStyle != 0: + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + case node.Style&SingleQuotedStyle != 0: + style = yaml_SINGLE_QUOTED_SCALAR_STYLE + case node.Style&LiteralStyle != 0: + style = yaml_LITERAL_SCALAR_STYLE + case node.Style&FoldedStyle != 0: + style = yaml_FOLDED_SCALAR_STYLE + case strings.Contains(value, "\n"): + style = yaml_LITERAL_SCALAR_STYLE + case forceQuoting: + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + + e.emitScalar(value, node.Anchor, tag, style, []byte(node.HeadComment), []byte(node.LineComment), []byte(node.FootComment), []byte(tail)) + } +} diff --git a/vendor/gopkg.in/yaml.v3/go.mod b/vendor/gopkg.in/yaml.v3/go.mod new file mode 100644 index 0000000000..f407ea3213 --- /dev/null +++ b/vendor/gopkg.in/yaml.v3/go.mod @@ -0,0 +1,5 @@ +module "gopkg.in/yaml.v3" + +require ( + "gopkg.in/check.v1" v0.0.0-20161208181325-20d25e280405 +) diff --git a/vendor/gopkg.in/yaml.v3/parserc.go b/vendor/gopkg.in/yaml.v3/parserc.go new file mode 100644 index 0000000000..ec25faabc4 --- /dev/null +++ b/vendor/gopkg.in/yaml.v3/parserc.go @@ -0,0 +1,1210 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// Copyright (c) 2006-2010 Kirill Simonov +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package yaml + +import ( + "bytes" +) + +// The parser implements the following grammar: +// +// stream ::= STREAM-START implicit_document? explicit_document* STREAM-END +// implicit_document ::= block_node DOCUMENT-END* +// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +// block_node_or_indentless_sequence ::= +// ALIAS +// | properties (block_content | indentless_block_sequence)? +// | block_content +// | indentless_block_sequence +// block_node ::= ALIAS +// | properties block_content? +// | block_content +// flow_node ::= ALIAS +// | properties flow_content? +// | flow_content +// properties ::= TAG ANCHOR? | ANCHOR TAG? +// block_content ::= block_collection | flow_collection | SCALAR +// flow_content ::= flow_collection | SCALAR +// block_collection ::= block_sequence | block_mapping +// flow_collection ::= flow_sequence | flow_mapping +// block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END +// indentless_sequence ::= (BLOCK-ENTRY block_node?)+ +// block_mapping ::= BLOCK-MAPPING_START +// ((KEY block_node_or_indentless_sequence?)? +// (VALUE block_node_or_indentless_sequence?)?)* +// BLOCK-END +// flow_sequence ::= FLOW-SEQUENCE-START +// (flow_sequence_entry FLOW-ENTRY)* +// flow_sequence_entry? +// FLOW-SEQUENCE-END +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// flow_mapping ::= FLOW-MAPPING-START +// (flow_mapping_entry FLOW-ENTRY)* +// flow_mapping_entry? +// FLOW-MAPPING-END +// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? + +// Peek the next token in the token queue. +func peek_token(parser *yaml_parser_t) *yaml_token_t { + if parser.token_available || yaml_parser_fetch_more_tokens(parser) { + token := &parser.tokens[parser.tokens_head] + yaml_parser_unfold_comments(parser, token) + return token + } + return nil +} + +// yaml_parser_unfold_comments walks through the comments queue and joins all +// comments behind the position of the provided token into the respective +// top-level comment slices in the parser. +func yaml_parser_unfold_comments(parser *yaml_parser_t, token *yaml_token_t) { + for parser.comments_head < len(parser.comments) && token.start_mark.index >= parser.comments[parser.comments_head].token_mark.index { + comment := &parser.comments[parser.comments_head] + if len(comment.head) > 0 { + if token.typ == yaml_BLOCK_END_TOKEN { + // No heads on ends, so keep comment.head for a follow up token. + break + } + if len(parser.head_comment) > 0 { + parser.head_comment = append(parser.head_comment, '\n') + } + parser.head_comment = append(parser.head_comment, comment.head...) + } + if len(comment.foot) > 0 { + if len(parser.foot_comment) > 0 { + parser.foot_comment = append(parser.foot_comment, '\n') + } + parser.foot_comment = append(parser.foot_comment, comment.foot...) + } + if len(comment.line) > 0 { + if len(parser.line_comment) > 0 { + parser.line_comment = append(parser.line_comment, '\n') + } + parser.line_comment = append(parser.line_comment, comment.line...) + } + *comment = yaml_comment_t{} + parser.comments_head++ + } +} + +// Remove the next token from the queue (must be called after peek_token). +func skip_token(parser *yaml_parser_t) { + parser.token_available = false + parser.tokens_parsed++ + parser.stream_end_produced = parser.tokens[parser.tokens_head].typ == yaml_STREAM_END_TOKEN + parser.tokens_head++ +} + +// Get the next event. +func yaml_parser_parse(parser *yaml_parser_t, event *yaml_event_t) bool { + // Erase the event object. + *event = yaml_event_t{} + + // No events after the end of the stream or error. + if parser.stream_end_produced || parser.error != yaml_NO_ERROR || parser.state == yaml_PARSE_END_STATE { + return true + } + + // Generate the next event. + return yaml_parser_state_machine(parser, event) +} + +// Set parser error. +func yaml_parser_set_parser_error(parser *yaml_parser_t, problem string, problem_mark yaml_mark_t) bool { + parser.error = yaml_PARSER_ERROR + parser.problem = problem + parser.problem_mark = problem_mark + return false +} + +func yaml_parser_set_parser_error_context(parser *yaml_parser_t, context string, context_mark yaml_mark_t, problem string, problem_mark yaml_mark_t) bool { + parser.error = yaml_PARSER_ERROR + parser.context = context + parser.context_mark = context_mark + parser.problem = problem + parser.problem_mark = problem_mark + return false +} + +// State dispatcher. +func yaml_parser_state_machine(parser *yaml_parser_t, event *yaml_event_t) bool { + //trace("yaml_parser_state_machine", "state:", parser.state.String()) + + switch parser.state { + case yaml_PARSE_STREAM_START_STATE: + return yaml_parser_parse_stream_start(parser, event) + + case yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE: + return yaml_parser_parse_document_start(parser, event, true) + + case yaml_PARSE_DOCUMENT_START_STATE: + return yaml_parser_parse_document_start(parser, event, false) + + case yaml_PARSE_DOCUMENT_CONTENT_STATE: + return yaml_parser_parse_document_content(parser, event) + + case yaml_PARSE_DOCUMENT_END_STATE: + return yaml_parser_parse_document_end(parser, event) + + case yaml_PARSE_BLOCK_NODE_STATE: + return yaml_parser_parse_node(parser, event, true, false) + + case yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE: + return yaml_parser_parse_node(parser, event, true, true) + + case yaml_PARSE_FLOW_NODE_STATE: + return yaml_parser_parse_node(parser, event, false, false) + + case yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE: + return yaml_parser_parse_block_sequence_entry(parser, event, true) + + case yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE: + return yaml_parser_parse_block_sequence_entry(parser, event, false) + + case yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE: + return yaml_parser_parse_indentless_sequence_entry(parser, event) + + case yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE: + return yaml_parser_parse_block_mapping_key(parser, event, true) + + case yaml_PARSE_BLOCK_MAPPING_KEY_STATE: + return yaml_parser_parse_block_mapping_key(parser, event, false) + + case yaml_PARSE_BLOCK_MAPPING_VALUE_STATE: + return yaml_parser_parse_block_mapping_value(parser, event) + + case yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE: + return yaml_parser_parse_flow_sequence_entry(parser, event, true) + + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE: + return yaml_parser_parse_flow_sequence_entry(parser, event, false) + + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE: + return yaml_parser_parse_flow_sequence_entry_mapping_key(parser, event) + + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE: + return yaml_parser_parse_flow_sequence_entry_mapping_value(parser, event) + + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE: + return yaml_parser_parse_flow_sequence_entry_mapping_end(parser, event) + + case yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE: + return yaml_parser_parse_flow_mapping_key(parser, event, true) + + case yaml_PARSE_FLOW_MAPPING_KEY_STATE: + return yaml_parser_parse_flow_mapping_key(parser, event, false) + + case yaml_PARSE_FLOW_MAPPING_VALUE_STATE: + return yaml_parser_parse_flow_mapping_value(parser, event, false) + + case yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE: + return yaml_parser_parse_flow_mapping_value(parser, event, true) + + default: + panic("invalid parser state") + } +} + +// Parse the production: +// stream ::= STREAM-START implicit_document? explicit_document* STREAM-END +// ************ +func yaml_parser_parse_stream_start(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_STREAM_START_TOKEN { + return yaml_parser_set_parser_error(parser, "did not find expected ", token.start_mark) + } + parser.state = yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE + *event = yaml_event_t{ + typ: yaml_STREAM_START_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + encoding: token.encoding, + } + skip_token(parser) + return true +} + +// Parse the productions: +// implicit_document ::= block_node DOCUMENT-END* +// * +// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +// ************************* +func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t, implicit bool) bool { + + token := peek_token(parser) + if token == nil { + return false + } + + // Parse extra document end indicators. + if !implicit { + for token.typ == yaml_DOCUMENT_END_TOKEN { + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } + } + + if implicit && token.typ != yaml_VERSION_DIRECTIVE_TOKEN && + token.typ != yaml_TAG_DIRECTIVE_TOKEN && + token.typ != yaml_DOCUMENT_START_TOKEN && + token.typ != yaml_STREAM_END_TOKEN { + // Parse an implicit document. + if !yaml_parser_process_directives(parser, nil, nil) { + return false + } + parser.states = append(parser.states, yaml_PARSE_DOCUMENT_END_STATE) + parser.state = yaml_PARSE_BLOCK_NODE_STATE + + var head_comment []byte + if len(parser.head_comment) > 0 { + // [Go] Scan the header comment backwards, and if an empty line is found, break + // the header so the part before the last empty line goes into the + // document header, while the bottom of it goes into a follow up event. + for i := len(parser.head_comment) - 1; i > 0; i-- { + if parser.head_comment[i] == '\n' { + if i == len(parser.head_comment)-1 { + head_comment = parser.head_comment[:i] + parser.head_comment = parser.head_comment[i+1:] + break + } else if parser.head_comment[i-1] == '\n' { + head_comment = parser.head_comment[:i-1] + parser.head_comment = parser.head_comment[i+1:] + break + } + } + } + } + + *event = yaml_event_t{ + typ: yaml_DOCUMENT_START_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + + head_comment: head_comment, + } + + } else if token.typ != yaml_STREAM_END_TOKEN { + // Parse an explicit document. + var version_directive *yaml_version_directive_t + var tag_directives []yaml_tag_directive_t + start_mark := token.start_mark + if !yaml_parser_process_directives(parser, &version_directive, &tag_directives) { + return false + } + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_DOCUMENT_START_TOKEN { + yaml_parser_set_parser_error(parser, + "did not find expected ", token.start_mark) + return false + } + parser.states = append(parser.states, yaml_PARSE_DOCUMENT_END_STATE) + parser.state = yaml_PARSE_DOCUMENT_CONTENT_STATE + end_mark := token.end_mark + + *event = yaml_event_t{ + typ: yaml_DOCUMENT_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + version_directive: version_directive, + tag_directives: tag_directives, + implicit: false, + } + skip_token(parser) + + } else { + // Parse the stream end. + parser.state = yaml_PARSE_END_STATE + *event = yaml_event_t{ + typ: yaml_STREAM_END_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + skip_token(parser) + } + + return true +} + +// Parse the productions: +// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +// *********** +// +func yaml_parser_parse_document_content(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + + if token.typ == yaml_VERSION_DIRECTIVE_TOKEN || + token.typ == yaml_TAG_DIRECTIVE_TOKEN || + token.typ == yaml_DOCUMENT_START_TOKEN || + token.typ == yaml_DOCUMENT_END_TOKEN || + token.typ == yaml_STREAM_END_TOKEN { + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + return yaml_parser_process_empty_scalar(parser, event, + token.start_mark) + } + return yaml_parser_parse_node(parser, event, true, false) +} + +// Parse the productions: +// implicit_document ::= block_node DOCUMENT-END* +// ************* +// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +// +func yaml_parser_parse_document_end(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + + start_mark := token.start_mark + end_mark := token.start_mark + + implicit := true + if token.typ == yaml_DOCUMENT_END_TOKEN { + end_mark = token.end_mark + skip_token(parser) + implicit = false + } + + parser.tag_directives = parser.tag_directives[:0] + + parser.state = yaml_PARSE_DOCUMENT_START_STATE + *event = yaml_event_t{ + typ: yaml_DOCUMENT_END_EVENT, + start_mark: start_mark, + end_mark: end_mark, + implicit: implicit, + } + yaml_parser_set_event_comments(parser, event) + if len(event.head_comment) > 0 && len(event.foot_comment) == 0 { + event.foot_comment = event.head_comment + event.head_comment = nil + } + return true +} + +func yaml_parser_set_event_comments(parser *yaml_parser_t, event *yaml_event_t) { + event.head_comment = parser.head_comment + event.line_comment = parser.line_comment + event.foot_comment = parser.foot_comment + parser.head_comment = nil + parser.line_comment = nil + parser.foot_comment = nil + parser.tail_comment = nil +} + +// Parse the productions: +// block_node_or_indentless_sequence ::= +// ALIAS +// ***** +// | properties (block_content | indentless_block_sequence)? +// ********** * +// | block_content | indentless_block_sequence +// * +// block_node ::= ALIAS +// ***** +// | properties block_content? +// ********** * +// | block_content +// * +// flow_node ::= ALIAS +// ***** +// | properties flow_content? +// ********** * +// | flow_content +// * +// properties ::= TAG ANCHOR? | ANCHOR TAG? +// ************************* +// block_content ::= block_collection | flow_collection | SCALAR +// ****** +// flow_content ::= flow_collection | SCALAR +// ****** +func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, indentless_sequence bool) bool { + //defer trace("yaml_parser_parse_node", "block:", block, "indentless_sequence:", indentless_sequence)() + + token := peek_token(parser) + if token == nil { + return false + } + + if token.typ == yaml_ALIAS_TOKEN { + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + *event = yaml_event_t{ + typ: yaml_ALIAS_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + anchor: token.value, + } + yaml_parser_set_event_comments(parser, event) + skip_token(parser) + return true + } + + start_mark := token.start_mark + end_mark := token.start_mark + + var tag_token bool + var tag_handle, tag_suffix, anchor []byte + var tag_mark yaml_mark_t + if token.typ == yaml_ANCHOR_TOKEN { + anchor = token.value + start_mark = token.start_mark + end_mark = token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ == yaml_TAG_TOKEN { + tag_token = true + tag_handle = token.value + tag_suffix = token.suffix + tag_mark = token.start_mark + end_mark = token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } + } else if token.typ == yaml_TAG_TOKEN { + tag_token = true + tag_handle = token.value + tag_suffix = token.suffix + start_mark = token.start_mark + tag_mark = token.start_mark + end_mark = token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ == yaml_ANCHOR_TOKEN { + anchor = token.value + end_mark = token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } + } + + var tag []byte + if tag_token { + if len(tag_handle) == 0 { + tag = tag_suffix + tag_suffix = nil + } else { + for i := range parser.tag_directives { + if bytes.Equal(parser.tag_directives[i].handle, tag_handle) { + tag = append([]byte(nil), parser.tag_directives[i].prefix...) + tag = append(tag, tag_suffix...) + break + } + } + if len(tag) == 0 { + yaml_parser_set_parser_error_context(parser, + "while parsing a node", start_mark, + "found undefined tag handle", tag_mark) + return false + } + } + } + + implicit := len(tag) == 0 + if indentless_sequence && token.typ == yaml_BLOCK_ENTRY_TOKEN { + end_mark = token.end_mark + parser.state = yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE + *event = yaml_event_t{ + typ: yaml_SEQUENCE_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(yaml_BLOCK_SEQUENCE_STYLE), + } + return true + } + if token.typ == yaml_SCALAR_TOKEN { + var plain_implicit, quoted_implicit bool + end_mark = token.end_mark + if (len(tag) == 0 && token.style == yaml_PLAIN_SCALAR_STYLE) || (len(tag) == 1 && tag[0] == '!') { + plain_implicit = true + } else if len(tag) == 0 { + quoted_implicit = true + } + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + + *event = yaml_event_t{ + typ: yaml_SCALAR_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + value: token.value, + implicit: plain_implicit, + quoted_implicit: quoted_implicit, + style: yaml_style_t(token.style), + } + yaml_parser_set_event_comments(parser, event) + skip_token(parser) + return true + } + if token.typ == yaml_FLOW_SEQUENCE_START_TOKEN { + // [Go] Some of the events below can be merged as they differ only on style. + end_mark = token.end_mark + parser.state = yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE + *event = yaml_event_t{ + typ: yaml_SEQUENCE_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(yaml_FLOW_SEQUENCE_STYLE), + } + yaml_parser_set_event_comments(parser, event) + return true + } + if token.typ == yaml_FLOW_MAPPING_START_TOKEN { + end_mark = token.end_mark + parser.state = yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE + *event = yaml_event_t{ + typ: yaml_MAPPING_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(yaml_FLOW_MAPPING_STYLE), + } + yaml_parser_set_event_comments(parser, event) + return true + } + if block && token.typ == yaml_BLOCK_SEQUENCE_START_TOKEN { + end_mark = token.end_mark + parser.state = yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE + *event = yaml_event_t{ + typ: yaml_SEQUENCE_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(yaml_BLOCK_SEQUENCE_STYLE), + } + return true + } + if block && token.typ == yaml_BLOCK_MAPPING_START_TOKEN { + end_mark = token.end_mark + parser.state = yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE + *event = yaml_event_t{ + typ: yaml_MAPPING_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(yaml_BLOCK_MAPPING_STYLE), + } + return true + } + if len(anchor) > 0 || len(tag) > 0 { + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + + *event = yaml_event_t{ + typ: yaml_SCALAR_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + quoted_implicit: false, + style: yaml_style_t(yaml_PLAIN_SCALAR_STYLE), + } + return true + } + + context := "while parsing a flow node" + if block { + context = "while parsing a block node" + } + yaml_parser_set_parser_error_context(parser, context, start_mark, + "did not find expected node content", token.start_mark) + return false +} + +// Parse the productions: +// block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END +// ******************** *********** * ********* +// +func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { + if first { + token := peek_token(parser) + parser.marks = append(parser.marks, token.start_mark) + skip_token(parser) + } + + token := peek_token(parser) + if token == nil { + return false + } + + if token.typ == yaml_BLOCK_ENTRY_TOKEN { + mark := token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_BLOCK_ENTRY_TOKEN && token.typ != yaml_BLOCK_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE) + return yaml_parser_parse_node(parser, event, true, false) + } else { + parser.state = yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE + return yaml_parser_process_empty_scalar(parser, event, mark) + } + } + if token.typ == yaml_BLOCK_END_TOKEN { + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + + *event = yaml_event_t{ + typ: yaml_SEQUENCE_END_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + + skip_token(parser) + return true + } + + context_mark := parser.marks[len(parser.marks)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + return yaml_parser_set_parser_error_context(parser, + "while parsing a block collection", context_mark, + "did not find expected '-' indicator", token.start_mark) +} + +// Parse the productions: +// indentless_sequence ::= (BLOCK-ENTRY block_node?)+ +// *********** * +func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + + if token.typ == yaml_BLOCK_ENTRY_TOKEN { + mark := token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_BLOCK_ENTRY_TOKEN && + token.typ != yaml_KEY_TOKEN && + token.typ != yaml_VALUE_TOKEN && + token.typ != yaml_BLOCK_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE) + return yaml_parser_parse_node(parser, event, true, false) + } + parser.state = yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE + return yaml_parser_process_empty_scalar(parser, event, mark) + } + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + + *event = yaml_event_t{ + typ: yaml_SEQUENCE_END_EVENT, + start_mark: token.start_mark, + end_mark: token.start_mark, // [Go] Shouldn't this be token.end_mark? + } + return true +} + +// Parse the productions: +// block_mapping ::= BLOCK-MAPPING_START +// ******************* +// ((KEY block_node_or_indentless_sequence?)? +// *** * +// (VALUE block_node_or_indentless_sequence?)?)* +// +// BLOCK-END +// ********* +// +func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { + if first { + token := peek_token(parser) + parser.marks = append(parser.marks, token.start_mark) + skip_token(parser) + } + + token := peek_token(parser) + if token == nil { + return false + } + + // [Go] A tail comment was left from the prior mapping value processed. Emit an event + // as it needs to be processed with that value and not the following key. + if len(parser.tail_comment) > 0 { + *event = yaml_event_t{ + typ: yaml_TAIL_COMMENT_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + foot_comment: parser.tail_comment, + } + parser.tail_comment = nil + return true + } + + if token.typ == yaml_KEY_TOKEN { + mark := token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_KEY_TOKEN && + token.typ != yaml_VALUE_TOKEN && + token.typ != yaml_BLOCK_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_BLOCK_MAPPING_VALUE_STATE) + return yaml_parser_parse_node(parser, event, true, true) + } else { + parser.state = yaml_PARSE_BLOCK_MAPPING_VALUE_STATE + return yaml_parser_process_empty_scalar(parser, event, mark) + } + } else if token.typ == yaml_BLOCK_END_TOKEN { + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + *event = yaml_event_t{ + typ: yaml_MAPPING_END_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + yaml_parser_set_event_comments(parser, event) + skip_token(parser) + return true + } + + context_mark := parser.marks[len(parser.marks)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + return yaml_parser_set_parser_error_context(parser, + "while parsing a block mapping", context_mark, + "did not find expected key", token.start_mark) +} + +// Parse the productions: +// block_mapping ::= BLOCK-MAPPING_START +// +// ((KEY block_node_or_indentless_sequence?)? +// +// (VALUE block_node_or_indentless_sequence?)?)* +// ***** * +// BLOCK-END +// +// +func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + if token.typ == yaml_VALUE_TOKEN { + mark := token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_KEY_TOKEN && + token.typ != yaml_VALUE_TOKEN && + token.typ != yaml_BLOCK_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_BLOCK_MAPPING_KEY_STATE) + return yaml_parser_parse_node(parser, event, true, true) + } + parser.state = yaml_PARSE_BLOCK_MAPPING_KEY_STATE + return yaml_parser_process_empty_scalar(parser, event, mark) + } + parser.state = yaml_PARSE_BLOCK_MAPPING_KEY_STATE + return yaml_parser_process_empty_scalar(parser, event, token.start_mark) +} + +// Parse the productions: +// flow_sequence ::= FLOW-SEQUENCE-START +// ******************* +// (flow_sequence_entry FLOW-ENTRY)* +// * ********** +// flow_sequence_entry? +// * +// FLOW-SEQUENCE-END +// ***************** +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * +// +func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { + if first { + token := peek_token(parser) + parser.marks = append(parser.marks, token.start_mark) + skip_token(parser) + } + token := peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { + if !first { + if token.typ == yaml_FLOW_ENTRY_TOKEN { + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } else { + context_mark := parser.marks[len(parser.marks)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + return yaml_parser_set_parser_error_context(parser, + "while parsing a flow sequence", context_mark, + "did not find expected ',' or ']'", token.start_mark) + } + } + + if token.typ == yaml_KEY_TOKEN { + parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE + *event = yaml_event_t{ + typ: yaml_MAPPING_START_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + implicit: true, + style: yaml_style_t(yaml_FLOW_MAPPING_STYLE), + } + skip_token(parser) + return true + } else if token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } + } + + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + + *event = yaml_event_t{ + typ: yaml_SEQUENCE_END_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + yaml_parser_set_event_comments(parser, event) + + skip_token(parser) + return true +} + +// +// Parse the productions: +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// *** * +// +func yaml_parser_parse_flow_sequence_entry_mapping_key(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_VALUE_TOKEN && + token.typ != yaml_FLOW_ENTRY_TOKEN && + token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } + mark := token.end_mark + skip_token(parser) + parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE + return yaml_parser_process_empty_scalar(parser, event, mark) +} + +// Parse the productions: +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// ***** * +// +func yaml_parser_parse_flow_sequence_entry_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + if token.typ == yaml_VALUE_TOKEN { + skip_token(parser) + token := peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_FLOW_ENTRY_TOKEN && token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } + } + parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE + return yaml_parser_process_empty_scalar(parser, event, token.start_mark) +} + +// Parse the productions: +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * +// +func yaml_parser_parse_flow_sequence_entry_mapping_end(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE + *event = yaml_event_t{ + typ: yaml_MAPPING_END_EVENT, + start_mark: token.start_mark, + end_mark: token.start_mark, // [Go] Shouldn't this be end_mark? + } + return true +} + +// Parse the productions: +// flow_mapping ::= FLOW-MAPPING-START +// ****************** +// (flow_mapping_entry FLOW-ENTRY)* +// * ********** +// flow_mapping_entry? +// ****************** +// FLOW-MAPPING-END +// **************** +// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * *** * +// +func yaml_parser_parse_flow_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { + if first { + token := peek_token(parser) + parser.marks = append(parser.marks, token.start_mark) + skip_token(parser) + } + + token := peek_token(parser) + if token == nil { + return false + } + + if token.typ != yaml_FLOW_MAPPING_END_TOKEN { + if !first { + if token.typ == yaml_FLOW_ENTRY_TOKEN { + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } else { + context_mark := parser.marks[len(parser.marks)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + return yaml_parser_set_parser_error_context(parser, + "while parsing a flow mapping", context_mark, + "did not find expected ',' or '}'", token.start_mark) + } + } + + if token.typ == yaml_KEY_TOKEN { + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_VALUE_TOKEN && + token.typ != yaml_FLOW_ENTRY_TOKEN && + token.typ != yaml_FLOW_MAPPING_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_VALUE_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } else { + parser.state = yaml_PARSE_FLOW_MAPPING_VALUE_STATE + return yaml_parser_process_empty_scalar(parser, event, token.start_mark) + } + } else if token.typ != yaml_FLOW_MAPPING_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } + } + + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + *event = yaml_event_t{ + typ: yaml_MAPPING_END_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + yaml_parser_set_event_comments(parser, event) + skip_token(parser) + return true +} + +// Parse the productions: +// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * ***** * +// +func yaml_parser_parse_flow_mapping_value(parser *yaml_parser_t, event *yaml_event_t, empty bool) bool { + token := peek_token(parser) + if token == nil { + return false + } + if empty { + parser.state = yaml_PARSE_FLOW_MAPPING_KEY_STATE + return yaml_parser_process_empty_scalar(parser, event, token.start_mark) + } + if token.typ == yaml_VALUE_TOKEN { + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_FLOW_ENTRY_TOKEN && token.typ != yaml_FLOW_MAPPING_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_KEY_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } + } + parser.state = yaml_PARSE_FLOW_MAPPING_KEY_STATE + return yaml_parser_process_empty_scalar(parser, event, token.start_mark) +} + +// Generate an empty scalar event. +func yaml_parser_process_empty_scalar(parser *yaml_parser_t, event *yaml_event_t, mark yaml_mark_t) bool { + *event = yaml_event_t{ + typ: yaml_SCALAR_EVENT, + start_mark: mark, + end_mark: mark, + value: nil, // Empty + implicit: true, + style: yaml_style_t(yaml_PLAIN_SCALAR_STYLE), + } + return true +} + +var default_tag_directives = []yaml_tag_directive_t{ + {[]byte("!"), []byte("!")}, + {[]byte("!!"), []byte("tag:yaml.org,2002:")}, +} + +// Parse directives. +func yaml_parser_process_directives(parser *yaml_parser_t, + version_directive_ref **yaml_version_directive_t, + tag_directives_ref *[]yaml_tag_directive_t) bool { + + var version_directive *yaml_version_directive_t + var tag_directives []yaml_tag_directive_t + + token := peek_token(parser) + if token == nil { + return false + } + + for token.typ == yaml_VERSION_DIRECTIVE_TOKEN || token.typ == yaml_TAG_DIRECTIVE_TOKEN { + if token.typ == yaml_VERSION_DIRECTIVE_TOKEN { + if version_directive != nil { + yaml_parser_set_parser_error(parser, + "found duplicate %YAML directive", token.start_mark) + return false + } + if token.major != 1 || token.minor != 1 { + yaml_parser_set_parser_error(parser, + "found incompatible YAML document", token.start_mark) + return false + } + version_directive = &yaml_version_directive_t{ + major: token.major, + minor: token.minor, + } + } else if token.typ == yaml_TAG_DIRECTIVE_TOKEN { + value := yaml_tag_directive_t{ + handle: token.value, + prefix: token.prefix, + } + if !yaml_parser_append_tag_directive(parser, value, false, token.start_mark) { + return false + } + tag_directives = append(tag_directives, value) + } + + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } + + for i := range default_tag_directives { + if !yaml_parser_append_tag_directive(parser, default_tag_directives[i], true, token.start_mark) { + return false + } + } + + if version_directive_ref != nil { + *version_directive_ref = version_directive + } + if tag_directives_ref != nil { + *tag_directives_ref = tag_directives + } + return true +} + +// Append a tag directive to the directives stack. +func yaml_parser_append_tag_directive(parser *yaml_parser_t, value yaml_tag_directive_t, allow_duplicates bool, mark yaml_mark_t) bool { + for i := range parser.tag_directives { + if bytes.Equal(value.handle, parser.tag_directives[i].handle) { + if allow_duplicates { + return true + } + return yaml_parser_set_parser_error(parser, "found duplicate %TAG directive", mark) + } + } + + // [Go] I suspect the copy is unnecessary. This was likely done + // because there was no way to track ownership of the data. + value_copy := yaml_tag_directive_t{ + handle: make([]byte, len(value.handle)), + prefix: make([]byte, len(value.prefix)), + } + copy(value_copy.handle, value.handle) + copy(value_copy.prefix, value.prefix) + parser.tag_directives = append(parser.tag_directives, value_copy) + return true +} diff --git a/vendor/gopkg.in/yaml.v3/readerc.go b/vendor/gopkg.in/yaml.v3/readerc.go new file mode 100644 index 0000000000..b7de0a89c4 --- /dev/null +++ b/vendor/gopkg.in/yaml.v3/readerc.go @@ -0,0 +1,434 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// Copyright (c) 2006-2010 Kirill Simonov +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package yaml + +import ( + "io" +) + +// Set the reader error and return 0. +func yaml_parser_set_reader_error(parser *yaml_parser_t, problem string, offset int, value int) bool { + parser.error = yaml_READER_ERROR + parser.problem = problem + parser.problem_offset = offset + parser.problem_value = value + return false +} + +// Byte order marks. +const ( + bom_UTF8 = "\xef\xbb\xbf" + bom_UTF16LE = "\xff\xfe" + bom_UTF16BE = "\xfe\xff" +) + +// Determine the input stream encoding by checking the BOM symbol. If no BOM is +// found, the UTF-8 encoding is assumed. Return 1 on success, 0 on failure. +func yaml_parser_determine_encoding(parser *yaml_parser_t) bool { + // Ensure that we had enough bytes in the raw buffer. + for !parser.eof && len(parser.raw_buffer)-parser.raw_buffer_pos < 3 { + if !yaml_parser_update_raw_buffer(parser) { + return false + } + } + + // Determine the encoding. + buf := parser.raw_buffer + pos := parser.raw_buffer_pos + avail := len(buf) - pos + if avail >= 2 && buf[pos] == bom_UTF16LE[0] && buf[pos+1] == bom_UTF16LE[1] { + parser.encoding = yaml_UTF16LE_ENCODING + parser.raw_buffer_pos += 2 + parser.offset += 2 + } else if avail >= 2 && buf[pos] == bom_UTF16BE[0] && buf[pos+1] == bom_UTF16BE[1] { + parser.encoding = yaml_UTF16BE_ENCODING + parser.raw_buffer_pos += 2 + parser.offset += 2 + } else if avail >= 3 && buf[pos] == bom_UTF8[0] && buf[pos+1] == bom_UTF8[1] && buf[pos+2] == bom_UTF8[2] { + parser.encoding = yaml_UTF8_ENCODING + parser.raw_buffer_pos += 3 + parser.offset += 3 + } else { + parser.encoding = yaml_UTF8_ENCODING + } + return true +} + +// Update the raw buffer. +func yaml_parser_update_raw_buffer(parser *yaml_parser_t) bool { + size_read := 0 + + // Return if the raw buffer is full. + if parser.raw_buffer_pos == 0 && len(parser.raw_buffer) == cap(parser.raw_buffer) { + return true + } + + // Return on EOF. + if parser.eof { + return true + } + + // Move the remaining bytes in the raw buffer to the beginning. + if parser.raw_buffer_pos > 0 && parser.raw_buffer_pos < len(parser.raw_buffer) { + copy(parser.raw_buffer, parser.raw_buffer[parser.raw_buffer_pos:]) + } + parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)-parser.raw_buffer_pos] + parser.raw_buffer_pos = 0 + + // Call the read handler to fill the buffer. + size_read, err := parser.read_handler(parser, parser.raw_buffer[len(parser.raw_buffer):cap(parser.raw_buffer)]) + parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)+size_read] + if err == io.EOF { + parser.eof = true + } else if err != nil { + return yaml_parser_set_reader_error(parser, "input error: "+err.Error(), parser.offset, -1) + } + return true +} + +// Ensure that the buffer contains at least `length` characters. +// Return true on success, false on failure. +// +// The length is supposed to be significantly less that the buffer size. +func yaml_parser_update_buffer(parser *yaml_parser_t, length int) bool { + if parser.read_handler == nil { + panic("read handler must be set") + } + + // [Go] This function was changed to guarantee the requested length size at EOF. + // The fact we need to do this is pretty awful, but the description above implies + // for that to be the case, and there are tests + + // If the EOF flag is set and the raw buffer is empty, do nothing. + if parser.eof && parser.raw_buffer_pos == len(parser.raw_buffer) { + // [Go] ACTUALLY! Read the documentation of this function above. + // This is just broken. To return true, we need to have the + // given length in the buffer. Not doing that means every single + // check that calls this function to make sure the buffer has a + // given length is Go) panicking; or C) accessing invalid memory. + //return true + } + + // Return if the buffer contains enough characters. + if parser.unread >= length { + return true + } + + // Determine the input encoding if it is not known yet. + if parser.encoding == yaml_ANY_ENCODING { + if !yaml_parser_determine_encoding(parser) { + return false + } + } + + // Move the unread characters to the beginning of the buffer. + buffer_len := len(parser.buffer) + if parser.buffer_pos > 0 && parser.buffer_pos < buffer_len { + copy(parser.buffer, parser.buffer[parser.buffer_pos:]) + buffer_len -= parser.buffer_pos + parser.buffer_pos = 0 + } else if parser.buffer_pos == buffer_len { + buffer_len = 0 + parser.buffer_pos = 0 + } + + // Open the whole buffer for writing, and cut it before returning. + parser.buffer = parser.buffer[:cap(parser.buffer)] + + // Fill the buffer until it has enough characters. + first := true + for parser.unread < length { + + // Fill the raw buffer if necessary. + if !first || parser.raw_buffer_pos == len(parser.raw_buffer) { + if !yaml_parser_update_raw_buffer(parser) { + parser.buffer = parser.buffer[:buffer_len] + return false + } + } + first = false + + // Decode the raw buffer. + inner: + for parser.raw_buffer_pos != len(parser.raw_buffer) { + var value rune + var width int + + raw_unread := len(parser.raw_buffer) - parser.raw_buffer_pos + + // Decode the next character. + switch parser.encoding { + case yaml_UTF8_ENCODING: + // Decode a UTF-8 character. Check RFC 3629 + // (http://www.ietf.org/rfc/rfc3629.txt) for more details. + // + // The following table (taken from the RFC) is used for + // decoding. + // + // Char. number range | UTF-8 octet sequence + // (hexadecimal) | (binary) + // --------------------+------------------------------------ + // 0000 0000-0000 007F | 0xxxxxxx + // 0000 0080-0000 07FF | 110xxxxx 10xxxxxx + // 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx + // 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + // + // Additionally, the characters in the range 0xD800-0xDFFF + // are prohibited as they are reserved for use with UTF-16 + // surrogate pairs. + + // Determine the length of the UTF-8 sequence. + octet := parser.raw_buffer[parser.raw_buffer_pos] + switch { + case octet&0x80 == 0x00: + width = 1 + case octet&0xE0 == 0xC0: + width = 2 + case octet&0xF0 == 0xE0: + width = 3 + case octet&0xF8 == 0xF0: + width = 4 + default: + // The leading octet is invalid. + return yaml_parser_set_reader_error(parser, + "invalid leading UTF-8 octet", + parser.offset, int(octet)) + } + + // Check if the raw buffer contains an incomplete character. + if width > raw_unread { + if parser.eof { + return yaml_parser_set_reader_error(parser, + "incomplete UTF-8 octet sequence", + parser.offset, -1) + } + break inner + } + + // Decode the leading octet. + switch { + case octet&0x80 == 0x00: + value = rune(octet & 0x7F) + case octet&0xE0 == 0xC0: + value = rune(octet & 0x1F) + case octet&0xF0 == 0xE0: + value = rune(octet & 0x0F) + case octet&0xF8 == 0xF0: + value = rune(octet & 0x07) + default: + value = 0 + } + + // Check and decode the trailing octets. + for k := 1; k < width; k++ { + octet = parser.raw_buffer[parser.raw_buffer_pos+k] + + // Check if the octet is valid. + if (octet & 0xC0) != 0x80 { + return yaml_parser_set_reader_error(parser, + "invalid trailing UTF-8 octet", + parser.offset+k, int(octet)) + } + + // Decode the octet. + value = (value << 6) + rune(octet&0x3F) + } + + // Check the length of the sequence against the value. + switch { + case width == 1: + case width == 2 && value >= 0x80: + case width == 3 && value >= 0x800: + case width == 4 && value >= 0x10000: + default: + return yaml_parser_set_reader_error(parser, + "invalid length of a UTF-8 sequence", + parser.offset, -1) + } + + // Check the range of the value. + if value >= 0xD800 && value <= 0xDFFF || value > 0x10FFFF { + return yaml_parser_set_reader_error(parser, + "invalid Unicode character", + parser.offset, int(value)) + } + + case yaml_UTF16LE_ENCODING, yaml_UTF16BE_ENCODING: + var low, high int + if parser.encoding == yaml_UTF16LE_ENCODING { + low, high = 0, 1 + } else { + low, high = 1, 0 + } + + // The UTF-16 encoding is not as simple as one might + // naively think. Check RFC 2781 + // (http://www.ietf.org/rfc/rfc2781.txt). + // + // Normally, two subsequent bytes describe a Unicode + // character. However a special technique (called a + // surrogate pair) is used for specifying character + // values larger than 0xFFFF. + // + // A surrogate pair consists of two pseudo-characters: + // high surrogate area (0xD800-0xDBFF) + // low surrogate area (0xDC00-0xDFFF) + // + // The following formulas are used for decoding + // and encoding characters using surrogate pairs: + // + // U = U' + 0x10000 (0x01 00 00 <= U <= 0x10 FF FF) + // U' = yyyyyyyyyyxxxxxxxxxx (0 <= U' <= 0x0F FF FF) + // W1 = 110110yyyyyyyyyy + // W2 = 110111xxxxxxxxxx + // + // where U is the character value, W1 is the high surrogate + // area, W2 is the low surrogate area. + + // Check for incomplete UTF-16 character. + if raw_unread < 2 { + if parser.eof { + return yaml_parser_set_reader_error(parser, + "incomplete UTF-16 character", + parser.offset, -1) + } + break inner + } + + // Get the character. + value = rune(parser.raw_buffer[parser.raw_buffer_pos+low]) + + (rune(parser.raw_buffer[parser.raw_buffer_pos+high]) << 8) + + // Check for unexpected low surrogate area. + if value&0xFC00 == 0xDC00 { + return yaml_parser_set_reader_error(parser, + "unexpected low surrogate area", + parser.offset, int(value)) + } + + // Check for a high surrogate area. + if value&0xFC00 == 0xD800 { + width = 4 + + // Check for incomplete surrogate pair. + if raw_unread < 4 { + if parser.eof { + return yaml_parser_set_reader_error(parser, + "incomplete UTF-16 surrogate pair", + parser.offset, -1) + } + break inner + } + + // Get the next character. + value2 := rune(parser.raw_buffer[parser.raw_buffer_pos+low+2]) + + (rune(parser.raw_buffer[parser.raw_buffer_pos+high+2]) << 8) + + // Check for a low surrogate area. + if value2&0xFC00 != 0xDC00 { + return yaml_parser_set_reader_error(parser, + "expected low surrogate area", + parser.offset+2, int(value2)) + } + + // Generate the value of the surrogate pair. + value = 0x10000 + ((value & 0x3FF) << 10) + (value2 & 0x3FF) + } else { + width = 2 + } + + default: + panic("impossible") + } + + // Check if the character is in the allowed range: + // #x9 | #xA | #xD | [#x20-#x7E] (8 bit) + // | #x85 | [#xA0-#xD7FF] | [#xE000-#xFFFD] (16 bit) + // | [#x10000-#x10FFFF] (32 bit) + switch { + case value == 0x09: + case value == 0x0A: + case value == 0x0D: + case value >= 0x20 && value <= 0x7E: + case value == 0x85: + case value >= 0xA0 && value <= 0xD7FF: + case value >= 0xE000 && value <= 0xFFFD: + case value >= 0x10000 && value <= 0x10FFFF: + default: + return yaml_parser_set_reader_error(parser, + "control characters are not allowed", + parser.offset, int(value)) + } + + // Move the raw pointers. + parser.raw_buffer_pos += width + parser.offset += width + + // Finally put the character into the buffer. + if value <= 0x7F { + // 0000 0000-0000 007F . 0xxxxxxx + parser.buffer[buffer_len+0] = byte(value) + buffer_len += 1 + } else if value <= 0x7FF { + // 0000 0080-0000 07FF . 110xxxxx 10xxxxxx + parser.buffer[buffer_len+0] = byte(0xC0 + (value >> 6)) + parser.buffer[buffer_len+1] = byte(0x80 + (value & 0x3F)) + buffer_len += 2 + } else if value <= 0xFFFF { + // 0000 0800-0000 FFFF . 1110xxxx 10xxxxxx 10xxxxxx + parser.buffer[buffer_len+0] = byte(0xE0 + (value >> 12)) + parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 6) & 0x3F)) + parser.buffer[buffer_len+2] = byte(0x80 + (value & 0x3F)) + buffer_len += 3 + } else { + // 0001 0000-0010 FFFF . 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + parser.buffer[buffer_len+0] = byte(0xF0 + (value >> 18)) + parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 12) & 0x3F)) + parser.buffer[buffer_len+2] = byte(0x80 + ((value >> 6) & 0x3F)) + parser.buffer[buffer_len+3] = byte(0x80 + (value & 0x3F)) + buffer_len += 4 + } + + parser.unread++ + } + + // On EOF, put NUL into the buffer and return. + if parser.eof { + parser.buffer[buffer_len] = 0 + buffer_len++ + parser.unread++ + break + } + } + // [Go] Read the documentation of this function above. To return true, + // we need to have the given length in the buffer. Not doing that means + // every single check that calls this function to make sure the buffer + // has a given length is Go) panicking; or C) accessing invalid memory. + // This happens here due to the EOF above breaking early. + for buffer_len < length { + parser.buffer[buffer_len] = 0 + buffer_len++ + } + parser.buffer = parser.buffer[:buffer_len] + return true +} diff --git a/vendor/gopkg.in/yaml.v3/resolve.go b/vendor/gopkg.in/yaml.v3/resolve.go new file mode 100644 index 0000000000..64ae888057 --- /dev/null +++ b/vendor/gopkg.in/yaml.v3/resolve.go @@ -0,0 +1,326 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// +// 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 yaml + +import ( + "encoding/base64" + "math" + "regexp" + "strconv" + "strings" + "time" +) + +type resolveMapItem struct { + value interface{} + tag string +} + +var resolveTable = make([]byte, 256) +var resolveMap = make(map[string]resolveMapItem) + +func init() { + t := resolveTable + t[int('+')] = 'S' // Sign + t[int('-')] = 'S' + for _, c := range "0123456789" { + t[int(c)] = 'D' // Digit + } + for _, c := range "yYnNtTfFoO~" { + t[int(c)] = 'M' // In map + } + t[int('.')] = '.' // Float (potentially in map) + + var resolveMapList = []struct { + v interface{} + tag string + l []string + }{ + {true, boolTag, []string{"true", "True", "TRUE"}}, + {false, boolTag, []string{"false", "False", "FALSE"}}, + {nil, nullTag, []string{"", "~", "null", "Null", "NULL"}}, + {math.NaN(), floatTag, []string{".nan", ".NaN", ".NAN"}}, + {math.Inf(+1), floatTag, []string{".inf", ".Inf", ".INF"}}, + {math.Inf(+1), floatTag, []string{"+.inf", "+.Inf", "+.INF"}}, + {math.Inf(-1), floatTag, []string{"-.inf", "-.Inf", "-.INF"}}, + {"<<", mergeTag, []string{"<<"}}, + } + + m := resolveMap + for _, item := range resolveMapList { + for _, s := range item.l { + m[s] = resolveMapItem{item.v, item.tag} + } + } +} + +const ( + nullTag = "!!null" + boolTag = "!!bool" + strTag = "!!str" + intTag = "!!int" + floatTag = "!!float" + timestampTag = "!!timestamp" + seqTag = "!!seq" + mapTag = "!!map" + binaryTag = "!!binary" + mergeTag = "!!merge" +) + +var longTags = make(map[string]string) +var shortTags = make(map[string]string) + +func init() { + for _, stag := range []string{nullTag, boolTag, strTag, intTag, floatTag, timestampTag, seqTag, mapTag, binaryTag, mergeTag} { + ltag := longTag(stag) + longTags[stag] = ltag + shortTags[ltag] = stag + } +} + +const longTagPrefix = "tag:yaml.org,2002:" + +func shortTag(tag string) string { + if strings.HasPrefix(tag, longTagPrefix) { + if stag, ok := shortTags[tag]; ok { + return stag + } + return "!!" + tag[len(longTagPrefix):] + } + return tag +} + +func longTag(tag string) string { + if strings.HasPrefix(tag, "!!") { + if ltag, ok := longTags[tag]; ok { + return ltag + } + return longTagPrefix + tag[2:] + } + return tag +} + +func resolvableTag(tag string) bool { + switch tag { + case "", strTag, boolTag, intTag, floatTag, nullTag, timestampTag: + return true + } + return false +} + +var yamlStyleFloat = regexp.MustCompile(`^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$`) + +func resolve(tag string, in string) (rtag string, out interface{}) { + tag = shortTag(tag) + if !resolvableTag(tag) { + return tag, in + } + + defer func() { + switch tag { + case "", rtag, strTag, binaryTag: + return + case floatTag: + if rtag == intTag { + switch v := out.(type) { + case int64: + rtag = floatTag + out = float64(v) + return + case int: + rtag = floatTag + out = float64(v) + return + } + } + } + failf("cannot decode %s `%s` as a %s", shortTag(rtag), in, shortTag(tag)) + }() + + // Any data is accepted as a !!str or !!binary. + // Otherwise, the prefix is enough of a hint about what it might be. + hint := byte('N') + if in != "" { + hint = resolveTable[in[0]] + } + if hint != 0 && tag != strTag && tag != binaryTag { + // Handle things we can lookup in a map. + if item, ok := resolveMap[in]; ok { + return item.tag, item.value + } + + // Base 60 floats are a bad idea, were dropped in YAML 1.2, and + // are purposefully unsupported here. They're still quoted on + // the way out for compatibility with other parser, though. + + switch hint { + case 'M': + // We've already checked the map above. + + case '.': + // Not in the map, so maybe a normal float. + floatv, err := strconv.ParseFloat(in, 64) + if err == nil { + return floatTag, floatv + } + + case 'D', 'S': + // Int, float, or timestamp. + // Only try values as a timestamp if the value is unquoted or there's an explicit + // !!timestamp tag. + if tag == "" || tag == timestampTag { + t, ok := parseTimestamp(in) + if ok { + return timestampTag, t + } + } + + plain := strings.Replace(in, "_", "", -1) + intv, err := strconv.ParseInt(plain, 0, 64) + if err == nil { + if intv == int64(int(intv)) { + return intTag, int(intv) + } else { + return intTag, intv + } + } + uintv, err := strconv.ParseUint(plain, 0, 64) + if err == nil { + return intTag, uintv + } + if yamlStyleFloat.MatchString(plain) { + floatv, err := strconv.ParseFloat(plain, 64) + if err == nil { + return floatTag, floatv + } + } + if strings.HasPrefix(plain, "0b") { + intv, err := strconv.ParseInt(plain[2:], 2, 64) + if err == nil { + if intv == int64(int(intv)) { + return intTag, int(intv) + } else { + return intTag, intv + } + } + uintv, err := strconv.ParseUint(plain[2:], 2, 64) + if err == nil { + return intTag, uintv + } + } else if strings.HasPrefix(plain, "-0b") { + intv, err := strconv.ParseInt("-"+plain[3:], 2, 64) + if err == nil { + if true || intv == int64(int(intv)) { + return intTag, int(intv) + } else { + return intTag, intv + } + } + } + // Octals as introduced in version 1.2 of the spec. + // Octals from the 1.1 spec, spelled as 0777, are still + // decoded by default in v3 as well for compatibility. + // May be dropped in v4 depending on how usage evolves. + if strings.HasPrefix(plain, "0o") { + intv, err := strconv.ParseInt(plain[2:], 8, 64) + if err == nil { + if intv == int64(int(intv)) { + return intTag, int(intv) + } else { + return intTag, intv + } + } + uintv, err := strconv.ParseUint(plain[2:], 8, 64) + if err == nil { + return intTag, uintv + } + } else if strings.HasPrefix(plain, "-0o") { + intv, err := strconv.ParseInt("-"+plain[3:], 8, 64) + if err == nil { + if true || intv == int64(int(intv)) { + return intTag, int(intv) + } else { + return intTag, intv + } + } + } + default: + panic("internal error: missing handler for resolver table: " + string(rune(hint)) + " (with " + in + ")") + } + } + return strTag, in +} + +// encodeBase64 encodes s as base64 that is broken up into multiple lines +// as appropriate for the resulting length. +func encodeBase64(s string) string { + const lineLen = 70 + encLen := base64.StdEncoding.EncodedLen(len(s)) + lines := encLen/lineLen + 1 + buf := make([]byte, encLen*2+lines) + in := buf[0:encLen] + out := buf[encLen:] + base64.StdEncoding.Encode(in, []byte(s)) + k := 0 + for i := 0; i < len(in); i += lineLen { + j := i + lineLen + if j > len(in) { + j = len(in) + } + k += copy(out[k:], in[i:j]) + if lines > 1 { + out[k] = '\n' + k++ + } + } + return string(out[:k]) +} + +// This is a subset of the formats allowed by the regular expression +// defined at http://yaml.org/type/timestamp.html. +var allowedTimestampFormats = []string{ + "2006-1-2T15:4:5.999999999Z07:00", // RCF3339Nano with short date fields. + "2006-1-2t15:4:5.999999999Z07:00", // RFC3339Nano with short date fields and lower-case "t". + "2006-1-2 15:4:5.999999999", // space separated with no time zone + "2006-1-2", // date only + // Notable exception: time.Parse cannot handle: "2001-12-14 21:59:43.10 -5" + // from the set of examples. +} + +// parseTimestamp parses s as a timestamp string and +// returns the timestamp and reports whether it succeeded. +// Timestamp formats are defined at http://yaml.org/type/timestamp.html +func parseTimestamp(s string) (time.Time, bool) { + // TODO write code to check all the formats supported by + // http://yaml.org/type/timestamp.html instead of using time.Parse. + + // Quick check: all date formats start with YYYY-. + i := 0 + for ; i < len(s); i++ { + if c := s[i]; c < '0' || c > '9' { + break + } + } + if i != 4 || i == len(s) || s[i] != '-' { + return time.Time{}, false + } + for _, format := range allowedTimestampFormats { + if t, err := time.Parse(format, s); err == nil { + return t, true + } + } + return time.Time{}, false +} diff --git a/vendor/gopkg.in/yaml.v3/scannerc.go b/vendor/gopkg.in/yaml.v3/scannerc.go new file mode 100644 index 0000000000..4a9c656460 --- /dev/null +++ b/vendor/gopkg.in/yaml.v3/scannerc.go @@ -0,0 +1,2974 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// Copyright (c) 2006-2010 Kirill Simonov +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package yaml + +import ( + "bytes" + "fmt" +) + +// Introduction +// ************ +// +// The following notes assume that you are familiar with the YAML specification +// (http://yaml.org/spec/1.2/spec.html). We mostly follow it, although in +// some cases we are less restrictive that it requires. +// +// The process of transforming a YAML stream into a sequence of events is +// divided on two steps: Scanning and Parsing. +// +// The Scanner transforms the input stream into a sequence of tokens, while the +// parser transform the sequence of tokens produced by the Scanner into a +// sequence of parsing events. +// +// The Scanner is rather clever and complicated. The Parser, on the contrary, +// is a straightforward implementation of a recursive-descendant parser (or, +// LL(1) parser, as it is usually called). +// +// Actually there are two issues of Scanning that might be called "clever", the +// rest is quite straightforward. The issues are "block collection start" and +// "simple keys". Both issues are explained below in details. +// +// Here the Scanning step is explained and implemented. We start with the list +// of all the tokens produced by the Scanner together with short descriptions. +// +// Now, tokens: +// +// STREAM-START(encoding) # The stream start. +// STREAM-END # The stream end. +// VERSION-DIRECTIVE(major,minor) # The '%YAML' directive. +// TAG-DIRECTIVE(handle,prefix) # The '%TAG' directive. +// DOCUMENT-START # '---' +// DOCUMENT-END # '...' +// BLOCK-SEQUENCE-START # Indentation increase denoting a block +// BLOCK-MAPPING-START # sequence or a block mapping. +// BLOCK-END # Indentation decrease. +// FLOW-SEQUENCE-START # '[' +// FLOW-SEQUENCE-END # ']' +// BLOCK-SEQUENCE-START # '{' +// BLOCK-SEQUENCE-END # '}' +// BLOCK-ENTRY # '-' +// FLOW-ENTRY # ',' +// KEY # '?' or nothing (simple keys). +// VALUE # ':' +// ALIAS(anchor) # '*anchor' +// ANCHOR(anchor) # '&anchor' +// TAG(handle,suffix) # '!handle!suffix' +// SCALAR(value,style) # A scalar. +// +// The following two tokens are "virtual" tokens denoting the beginning and the +// end of the stream: +// +// STREAM-START(encoding) +// STREAM-END +// +// We pass the information about the input stream encoding with the +// STREAM-START token. +// +// The next two tokens are responsible for tags: +// +// VERSION-DIRECTIVE(major,minor) +// TAG-DIRECTIVE(handle,prefix) +// +// Example: +// +// %YAML 1.1 +// %TAG ! !foo +// %TAG !yaml! tag:yaml.org,2002: +// --- +// +// The correspoding sequence of tokens: +// +// STREAM-START(utf-8) +// VERSION-DIRECTIVE(1,1) +// TAG-DIRECTIVE("!","!foo") +// TAG-DIRECTIVE("!yaml","tag:yaml.org,2002:") +// DOCUMENT-START +// STREAM-END +// +// Note that the VERSION-DIRECTIVE and TAG-DIRECTIVE tokens occupy a whole +// line. +// +// The document start and end indicators are represented by: +// +// DOCUMENT-START +// DOCUMENT-END +// +// Note that if a YAML stream contains an implicit document (without '---' +// and '...' indicators), no DOCUMENT-START and DOCUMENT-END tokens will be +// produced. +// +// In the following examples, we present whole documents together with the +// produced tokens. +// +// 1. An implicit document: +// +// 'a scalar' +// +// Tokens: +// +// STREAM-START(utf-8) +// SCALAR("a scalar",single-quoted) +// STREAM-END +// +// 2. An explicit document: +// +// --- +// 'a scalar' +// ... +// +// Tokens: +// +// STREAM-START(utf-8) +// DOCUMENT-START +// SCALAR("a scalar",single-quoted) +// DOCUMENT-END +// STREAM-END +// +// 3. Several documents in a stream: +// +// 'a scalar' +// --- +// 'another scalar' +// --- +// 'yet another scalar' +// +// Tokens: +// +// STREAM-START(utf-8) +// SCALAR("a scalar",single-quoted) +// DOCUMENT-START +// SCALAR("another scalar",single-quoted) +// DOCUMENT-START +// SCALAR("yet another scalar",single-quoted) +// STREAM-END +// +// We have already introduced the SCALAR token above. The following tokens are +// used to describe aliases, anchors, tag, and scalars: +// +// ALIAS(anchor) +// ANCHOR(anchor) +// TAG(handle,suffix) +// SCALAR(value,style) +// +// The following series of examples illustrate the usage of these tokens: +// +// 1. A recursive sequence: +// +// &A [ *A ] +// +// Tokens: +// +// STREAM-START(utf-8) +// ANCHOR("A") +// FLOW-SEQUENCE-START +// ALIAS("A") +// FLOW-SEQUENCE-END +// STREAM-END +// +// 2. A tagged scalar: +// +// !!float "3.14" # A good approximation. +// +// Tokens: +// +// STREAM-START(utf-8) +// TAG("!!","float") +// SCALAR("3.14",double-quoted) +// STREAM-END +// +// 3. Various scalar styles: +// +// --- # Implicit empty plain scalars do not produce tokens. +// --- a plain scalar +// --- 'a single-quoted scalar' +// --- "a double-quoted scalar" +// --- |- +// a literal scalar +// --- >- +// a folded +// scalar +// +// Tokens: +// +// STREAM-START(utf-8) +// DOCUMENT-START +// DOCUMENT-START +// SCALAR("a plain scalar",plain) +// DOCUMENT-START +// SCALAR("a single-quoted scalar",single-quoted) +// DOCUMENT-START +// SCALAR("a double-quoted scalar",double-quoted) +// DOCUMENT-START +// SCALAR("a literal scalar",literal) +// DOCUMENT-START +// SCALAR("a folded scalar",folded) +// STREAM-END +// +// Now it's time to review collection-related tokens. We will start with +// flow collections: +// +// FLOW-SEQUENCE-START +// FLOW-SEQUENCE-END +// FLOW-MAPPING-START +// FLOW-MAPPING-END +// FLOW-ENTRY +// KEY +// VALUE +// +// The tokens FLOW-SEQUENCE-START, FLOW-SEQUENCE-END, FLOW-MAPPING-START, and +// FLOW-MAPPING-END represent the indicators '[', ']', '{', and '}' +// correspondingly. FLOW-ENTRY represent the ',' indicator. Finally the +// indicators '?' and ':', which are used for denoting mapping keys and values, +// are represented by the KEY and VALUE tokens. +// +// The following examples show flow collections: +// +// 1. A flow sequence: +// +// [item 1, item 2, item 3] +// +// Tokens: +// +// STREAM-START(utf-8) +// FLOW-SEQUENCE-START +// SCALAR("item 1",plain) +// FLOW-ENTRY +// SCALAR("item 2",plain) +// FLOW-ENTRY +// SCALAR("item 3",plain) +// FLOW-SEQUENCE-END +// STREAM-END +// +// 2. A flow mapping: +// +// { +// a simple key: a value, # Note that the KEY token is produced. +// ? a complex key: another value, +// } +// +// Tokens: +// +// STREAM-START(utf-8) +// FLOW-MAPPING-START +// KEY +// SCALAR("a simple key",plain) +// VALUE +// SCALAR("a value",plain) +// FLOW-ENTRY +// KEY +// SCALAR("a complex key",plain) +// VALUE +// SCALAR("another value",plain) +// FLOW-ENTRY +// FLOW-MAPPING-END +// STREAM-END +// +// A simple key is a key which is not denoted by the '?' indicator. Note that +// the Scanner still produce the KEY token whenever it encounters a simple key. +// +// For scanning block collections, the following tokens are used (note that we +// repeat KEY and VALUE here): +// +// BLOCK-SEQUENCE-START +// BLOCK-MAPPING-START +// BLOCK-END +// BLOCK-ENTRY +// KEY +// VALUE +// +// The tokens BLOCK-SEQUENCE-START and BLOCK-MAPPING-START denote indentation +// increase that precedes a block collection (cf. the INDENT token in Python). +// The token BLOCK-END denote indentation decrease that ends a block collection +// (cf. the DEDENT token in Python). However YAML has some syntax pecularities +// that makes detections of these tokens more complex. +// +// The tokens BLOCK-ENTRY, KEY, and VALUE are used to represent the indicators +// '-', '?', and ':' correspondingly. +// +// The following examples show how the tokens BLOCK-SEQUENCE-START, +// BLOCK-MAPPING-START, and BLOCK-END are emitted by the Scanner: +// +// 1. Block sequences: +// +// - item 1 +// - item 2 +// - +// - item 3.1 +// - item 3.2 +// - +// key 1: value 1 +// key 2: value 2 +// +// Tokens: +// +// STREAM-START(utf-8) +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// SCALAR("item 1",plain) +// BLOCK-ENTRY +// SCALAR("item 2",plain) +// BLOCK-ENTRY +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// SCALAR("item 3.1",plain) +// BLOCK-ENTRY +// SCALAR("item 3.2",plain) +// BLOCK-END +// BLOCK-ENTRY +// BLOCK-MAPPING-START +// KEY +// SCALAR("key 1",plain) +// VALUE +// SCALAR("value 1",plain) +// KEY +// SCALAR("key 2",plain) +// VALUE +// SCALAR("value 2",plain) +// BLOCK-END +// BLOCK-END +// STREAM-END +// +// 2. Block mappings: +// +// a simple key: a value # The KEY token is produced here. +// ? a complex key +// : another value +// a mapping: +// key 1: value 1 +// key 2: value 2 +// a sequence: +// - item 1 +// - item 2 +// +// Tokens: +// +// STREAM-START(utf-8) +// BLOCK-MAPPING-START +// KEY +// SCALAR("a simple key",plain) +// VALUE +// SCALAR("a value",plain) +// KEY +// SCALAR("a complex key",plain) +// VALUE +// SCALAR("another value",plain) +// KEY +// SCALAR("a mapping",plain) +// BLOCK-MAPPING-START +// KEY +// SCALAR("key 1",plain) +// VALUE +// SCALAR("value 1",plain) +// KEY +// SCALAR("key 2",plain) +// VALUE +// SCALAR("value 2",plain) +// BLOCK-END +// KEY +// SCALAR("a sequence",plain) +// VALUE +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// SCALAR("item 1",plain) +// BLOCK-ENTRY +// SCALAR("item 2",plain) +// BLOCK-END +// BLOCK-END +// STREAM-END +// +// YAML does not always require to start a new block collection from a new +// line. If the current line contains only '-', '?', and ':' indicators, a new +// block collection may start at the current line. The following examples +// illustrate this case: +// +// 1. Collections in a sequence: +// +// - - item 1 +// - item 2 +// - key 1: value 1 +// key 2: value 2 +// - ? complex key +// : complex value +// +// Tokens: +// +// STREAM-START(utf-8) +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// SCALAR("item 1",plain) +// BLOCK-ENTRY +// SCALAR("item 2",plain) +// BLOCK-END +// BLOCK-ENTRY +// BLOCK-MAPPING-START +// KEY +// SCALAR("key 1",plain) +// VALUE +// SCALAR("value 1",plain) +// KEY +// SCALAR("key 2",plain) +// VALUE +// SCALAR("value 2",plain) +// BLOCK-END +// BLOCK-ENTRY +// BLOCK-MAPPING-START +// KEY +// SCALAR("complex key") +// VALUE +// SCALAR("complex value") +// BLOCK-END +// BLOCK-END +// STREAM-END +// +// 2. Collections in a mapping: +// +// ? a sequence +// : - item 1 +// - item 2 +// ? a mapping +// : key 1: value 1 +// key 2: value 2 +// +// Tokens: +// +// STREAM-START(utf-8) +// BLOCK-MAPPING-START +// KEY +// SCALAR("a sequence",plain) +// VALUE +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// SCALAR("item 1",plain) +// BLOCK-ENTRY +// SCALAR("item 2",plain) +// BLOCK-END +// KEY +// SCALAR("a mapping",plain) +// VALUE +// BLOCK-MAPPING-START +// KEY +// SCALAR("key 1",plain) +// VALUE +// SCALAR("value 1",plain) +// KEY +// SCALAR("key 2",plain) +// VALUE +// SCALAR("value 2",plain) +// BLOCK-END +// BLOCK-END +// STREAM-END +// +// YAML also permits non-indented sequences if they are included into a block +// mapping. In this case, the token BLOCK-SEQUENCE-START is not produced: +// +// key: +// - item 1 # BLOCK-SEQUENCE-START is NOT produced here. +// - item 2 +// +// Tokens: +// +// STREAM-START(utf-8) +// BLOCK-MAPPING-START +// KEY +// SCALAR("key",plain) +// VALUE +// BLOCK-ENTRY +// SCALAR("item 1",plain) +// BLOCK-ENTRY +// SCALAR("item 2",plain) +// BLOCK-END +// + +// Ensure that the buffer contains the required number of characters. +// Return true on success, false on failure (reader error or memory error). +func cache(parser *yaml_parser_t, length int) bool { + // [Go] This was inlined: !cache(A, B) -> unread < B && !update(A, B) + return parser.unread >= length || yaml_parser_update_buffer(parser, length) +} + +// Advance the buffer pointer. +func skip(parser *yaml_parser_t) { + if !is_blank(parser.buffer, parser.buffer_pos) { + parser.newlines = 0 + } + parser.mark.index++ + parser.mark.column++ + parser.unread-- + parser.buffer_pos += width(parser.buffer[parser.buffer_pos]) +} + +func skip_line(parser *yaml_parser_t) { + if is_crlf(parser.buffer, parser.buffer_pos) { + parser.mark.index += 2 + parser.mark.column = 0 + parser.mark.line++ + parser.unread -= 2 + parser.buffer_pos += 2 + parser.newlines++ + } else if is_break(parser.buffer, parser.buffer_pos) { + parser.mark.index++ + parser.mark.column = 0 + parser.mark.line++ + parser.unread-- + parser.buffer_pos += width(parser.buffer[parser.buffer_pos]) + parser.newlines++ + } +} + +// Copy a character to a string buffer and advance pointers. +func read(parser *yaml_parser_t, s []byte) []byte { + if !is_blank(parser.buffer, parser.buffer_pos) { + parser.newlines = 0 + } + w := width(parser.buffer[parser.buffer_pos]) + if w == 0 { + panic("invalid character sequence") + } + if len(s) == 0 { + s = make([]byte, 0, 32) + } + if w == 1 && len(s)+w <= cap(s) { + s = s[:len(s)+1] + s[len(s)-1] = parser.buffer[parser.buffer_pos] + parser.buffer_pos++ + } else { + s = append(s, parser.buffer[parser.buffer_pos:parser.buffer_pos+w]...) + parser.buffer_pos += w + } + parser.mark.index++ + parser.mark.column++ + parser.unread-- + return s +} + +// Copy a line break character to a string buffer and advance pointers. +func read_line(parser *yaml_parser_t, s []byte) []byte { + buf := parser.buffer + pos := parser.buffer_pos + switch { + case buf[pos] == '\r' && buf[pos+1] == '\n': + // CR LF . LF + s = append(s, '\n') + parser.buffer_pos += 2 + parser.mark.index++ + parser.unread-- + case buf[pos] == '\r' || buf[pos] == '\n': + // CR|LF . LF + s = append(s, '\n') + parser.buffer_pos += 1 + case buf[pos] == '\xC2' && buf[pos+1] == '\x85': + // NEL . LF + s = append(s, '\n') + parser.buffer_pos += 2 + case buf[pos] == '\xE2' && buf[pos+1] == '\x80' && (buf[pos+2] == '\xA8' || buf[pos+2] == '\xA9'): + // LS|PS . LS|PS + s = append(s, buf[parser.buffer_pos:pos+3]...) + parser.buffer_pos += 3 + default: + return s + } + parser.mark.index++ + parser.mark.column = 0 + parser.mark.line++ + parser.unread-- + parser.newlines++ + return s +} + +// Get the next token. +func yaml_parser_scan(parser *yaml_parser_t, token *yaml_token_t) bool { + // Erase the token object. + *token = yaml_token_t{} // [Go] Is this necessary? + + // No tokens after STREAM-END or error. + if parser.stream_end_produced || parser.error != yaml_NO_ERROR { + return true + } + + // Ensure that the tokens queue contains enough tokens. + if !parser.token_available { + if !yaml_parser_fetch_more_tokens(parser) { + return false + } + } + + // Fetch the next token from the queue. + *token = parser.tokens[parser.tokens_head] + parser.tokens_head++ + parser.tokens_parsed++ + parser.token_available = false + + if token.typ == yaml_STREAM_END_TOKEN { + parser.stream_end_produced = true + } + return true +} + +// Set the scanner error and return false. +func yaml_parser_set_scanner_error(parser *yaml_parser_t, context string, context_mark yaml_mark_t, problem string) bool { + parser.error = yaml_SCANNER_ERROR + parser.context = context + parser.context_mark = context_mark + parser.problem = problem + parser.problem_mark = parser.mark + return false +} + +func yaml_parser_set_scanner_tag_error(parser *yaml_parser_t, directive bool, context_mark yaml_mark_t, problem string) bool { + context := "while parsing a tag" + if directive { + context = "while parsing a %TAG directive" + } + return yaml_parser_set_scanner_error(parser, context, context_mark, problem) +} + +func trace(args ...interface{}) func() { + pargs := append([]interface{}{"+++"}, args...) + fmt.Println(pargs...) + pargs = append([]interface{}{"---"}, args...) + return func() { fmt.Println(pargs...) } +} + +// Ensure that the tokens queue contains at least one token which can be +// returned to the Parser. +func yaml_parser_fetch_more_tokens(parser *yaml_parser_t) bool { + // While we need more tokens to fetch, do it. + for { + // Check if we really need to fetch more tokens. + need_more_tokens := false + + // [Go] The comment parsing logic requires a lookahead of one token + // in block style or two tokens in flow style so that the foot + // comments may be parsed in time of associating them with the tokens + // that are parsed before them. + if parser.tokens_head >= len(parser.tokens)-1 || parser.flow_level > 0 && parser.tokens_head >= len(parser.tokens)-2 { + need_more_tokens = true + } else { + // Check if any potential simple key may occupy the head position. + if !yaml_parser_stale_simple_keys(parser) { + return false + } + + for i := range parser.simple_keys { + simple_key := &parser.simple_keys[i] + if simple_key.possible && simple_key.token_number == parser.tokens_parsed { + need_more_tokens = true + break + } + } + } + + // We are finished. + if !need_more_tokens { + break + } + // Fetch the next token. + if !yaml_parser_fetch_next_token(parser) { + return false + } + } + + parser.token_available = true + return true +} + +// The dispatcher for token fetchers. +func yaml_parser_fetch_next_token(parser *yaml_parser_t) (ok bool) { + // Ensure that the buffer is initialized. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + // Check if we just started scanning. Fetch STREAM-START then. + if !parser.stream_start_produced { + return yaml_parser_fetch_stream_start(parser) + } + + scan_mark := parser.mark + + // Eat whitespaces and comments until we reach the next token. + if !yaml_parser_scan_to_next_token(parser) { + return false + } + + // Remove obsolete potential simple keys. + if !yaml_parser_stale_simple_keys(parser) { + return false + } + + // [Go] While unrolling indents, transform the head comments of prior + // indentation levels observed after scan_start into foot comments at + // the respective indexes. + + // Check the indentation level against the current column. + if !yaml_parser_unroll_indent(parser, parser.mark.column, scan_mark) { + return false + } + + // Ensure that the buffer contains at least 4 characters. 4 is the length + // of the longest indicators ('--- ' and '... '). + if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) { + return false + } + + // Is it the end of the stream? + if is_z(parser.buffer, parser.buffer_pos) { + return yaml_parser_fetch_stream_end(parser) + } + + // Is it a directive? + if parser.mark.column == 0 && parser.buffer[parser.buffer_pos] == '%' { + return yaml_parser_fetch_directive(parser) + } + + buf := parser.buffer + pos := parser.buffer_pos + + // Is it the document start indicator? + if parser.mark.column == 0 && buf[pos] == '-' && buf[pos+1] == '-' && buf[pos+2] == '-' && is_blankz(buf, pos+3) { + return yaml_parser_fetch_document_indicator(parser, yaml_DOCUMENT_START_TOKEN) + } + + // Is it the document end indicator? + if parser.mark.column == 0 && buf[pos] == '.' && buf[pos+1] == '.' && buf[pos+2] == '.' && is_blankz(buf, pos+3) { + return yaml_parser_fetch_document_indicator(parser, yaml_DOCUMENT_END_TOKEN) + } + + comment_mark := parser.mark + if len(parser.tokens) > 0 && (parser.flow_level == 0 && buf[pos] == ':' || parser.flow_level > 0 && buf[pos] == ',') { + // Associate any following comments with the prior token. + comment_mark = parser.tokens[len(parser.tokens)-1].start_mark + } + defer func() { + if !ok { + return + } + if !yaml_parser_scan_line_comment(parser, comment_mark) { + ok = false + return + } + }() + + // Is it the flow sequence start indicator? + if buf[pos] == '[' { + return yaml_parser_fetch_flow_collection_start(parser, yaml_FLOW_SEQUENCE_START_TOKEN) + } + + // Is it the flow mapping start indicator? + if parser.buffer[parser.buffer_pos] == '{' { + return yaml_parser_fetch_flow_collection_start(parser, yaml_FLOW_MAPPING_START_TOKEN) + } + + // Is it the flow sequence end indicator? + if parser.buffer[parser.buffer_pos] == ']' { + return yaml_parser_fetch_flow_collection_end(parser, + yaml_FLOW_SEQUENCE_END_TOKEN) + } + + // Is it the flow mapping end indicator? + if parser.buffer[parser.buffer_pos] == '}' { + return yaml_parser_fetch_flow_collection_end(parser, + yaml_FLOW_MAPPING_END_TOKEN) + } + + // Is it the flow entry indicator? + if parser.buffer[parser.buffer_pos] == ',' { + return yaml_parser_fetch_flow_entry(parser) + } + + // Is it the block entry indicator? + if parser.buffer[parser.buffer_pos] == '-' && is_blankz(parser.buffer, parser.buffer_pos+1) { + return yaml_parser_fetch_block_entry(parser) + } + + // Is it the key indicator? + if parser.buffer[parser.buffer_pos] == '?' && (parser.flow_level > 0 || is_blankz(parser.buffer, parser.buffer_pos+1)) { + return yaml_parser_fetch_key(parser) + } + + // Is it the value indicator? + if parser.buffer[parser.buffer_pos] == ':' && (parser.flow_level > 0 || is_blankz(parser.buffer, parser.buffer_pos+1)) { + return yaml_parser_fetch_value(parser) + } + + // Is it an alias? + if parser.buffer[parser.buffer_pos] == '*' { + return yaml_parser_fetch_anchor(parser, yaml_ALIAS_TOKEN) + } + + // Is it an anchor? + if parser.buffer[parser.buffer_pos] == '&' { + return yaml_parser_fetch_anchor(parser, yaml_ANCHOR_TOKEN) + } + + // Is it a tag? + if parser.buffer[parser.buffer_pos] == '!' { + return yaml_parser_fetch_tag(parser) + } + + // Is it a literal scalar? + if parser.buffer[parser.buffer_pos] == '|' && parser.flow_level == 0 { + return yaml_parser_fetch_block_scalar(parser, true) + } + + // Is it a folded scalar? + if parser.buffer[parser.buffer_pos] == '>' && parser.flow_level == 0 { + return yaml_parser_fetch_block_scalar(parser, false) + } + + // Is it a single-quoted scalar? + if parser.buffer[parser.buffer_pos] == '\'' { + return yaml_parser_fetch_flow_scalar(parser, true) + } + + // Is it a double-quoted scalar? + if parser.buffer[parser.buffer_pos] == '"' { + return yaml_parser_fetch_flow_scalar(parser, false) + } + + // Is it a plain scalar? + // + // A plain scalar may start with any non-blank characters except + // + // '-', '?', ':', ',', '[', ']', '{', '}', + // '#', '&', '*', '!', '|', '>', '\'', '\"', + // '%', '@', '`'. + // + // In the block context (and, for the '-' indicator, in the flow context + // too), it may also start with the characters + // + // '-', '?', ':' + // + // if it is followed by a non-space character. + // + // The last rule is more restrictive than the specification requires. + // [Go] TODO Make this logic more reasonable. + //switch parser.buffer[parser.buffer_pos] { + //case '-', '?', ':', ',', '?', '-', ',', ':', ']', '[', '}', '{', '&', '#', '!', '*', '>', '|', '"', '\'', '@', '%', '-', '`': + //} + if !(is_blankz(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == '-' || + parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == ':' || + parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == '[' || + parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '{' || + parser.buffer[parser.buffer_pos] == '}' || parser.buffer[parser.buffer_pos] == '#' || + parser.buffer[parser.buffer_pos] == '&' || parser.buffer[parser.buffer_pos] == '*' || + parser.buffer[parser.buffer_pos] == '!' || parser.buffer[parser.buffer_pos] == '|' || + parser.buffer[parser.buffer_pos] == '>' || parser.buffer[parser.buffer_pos] == '\'' || + parser.buffer[parser.buffer_pos] == '"' || parser.buffer[parser.buffer_pos] == '%' || + parser.buffer[parser.buffer_pos] == '@' || parser.buffer[parser.buffer_pos] == '`') || + (parser.buffer[parser.buffer_pos] == '-' && !is_blank(parser.buffer, parser.buffer_pos+1)) || + (parser.flow_level == 0 && + (parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == ':') && + !is_blankz(parser.buffer, parser.buffer_pos+1)) { + return yaml_parser_fetch_plain_scalar(parser) + } + + // If we don't determine the token type so far, it is an error. + return yaml_parser_set_scanner_error(parser, + "while scanning for the next token", parser.mark, + "found character that cannot start any token") +} + +// Check the list of potential simple keys and remove the positions that +// cannot contain simple keys anymore. +func yaml_parser_stale_simple_keys(parser *yaml_parser_t) bool { + // Check for a potential simple key for each flow level. + for i := range parser.simple_keys { + simple_key := &parser.simple_keys[i] + + // The specification requires that a simple key + // + // - is limited to a single line, + // - is shorter than 1024 characters. + if simple_key.possible && (simple_key.mark.line < parser.mark.line || simple_key.mark.index+1024 < parser.mark.index) { + + // Check if the potential simple key to be removed is required. + if simple_key.required { + return yaml_parser_set_scanner_error(parser, + "while scanning a simple key", simple_key.mark, + "could not find expected ':'") + } + simple_key.possible = false + } + } + return true +} + +// Check if a simple key may start at the current position and add it if +// needed. +func yaml_parser_save_simple_key(parser *yaml_parser_t) bool { + // A simple key is required at the current position if the scanner is in + // the block context and the current column coincides with the indentation + // level. + + required := parser.flow_level == 0 && parser.indent == parser.mark.column + + // + // If the current position may start a simple key, save it. + // + if parser.simple_key_allowed { + simple_key := yaml_simple_key_t{ + possible: true, + required: required, + token_number: parser.tokens_parsed + (len(parser.tokens) - parser.tokens_head), + } + simple_key.mark = parser.mark + + if !yaml_parser_remove_simple_key(parser) { + return false + } + parser.simple_keys[len(parser.simple_keys)-1] = simple_key + } + return true +} + +// Remove a potential simple key at the current flow level. +func yaml_parser_remove_simple_key(parser *yaml_parser_t) bool { + i := len(parser.simple_keys) - 1 + if parser.simple_keys[i].possible { + // If the key is required, it is an error. + if parser.simple_keys[i].required { + return yaml_parser_set_scanner_error(parser, + "while scanning a simple key", parser.simple_keys[i].mark, + "could not find expected ':'") + } + } + // Remove the key from the stack. + parser.simple_keys[i].possible = false + return true +} + +// Increase the flow level and resize the simple key list if needed. +func yaml_parser_increase_flow_level(parser *yaml_parser_t) bool { + // Reset the simple key on the next level. + parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{}) + + // Increase the flow level. + parser.flow_level++ + return true +} + +// Decrease the flow level. +func yaml_parser_decrease_flow_level(parser *yaml_parser_t) bool { + if parser.flow_level > 0 { + parser.flow_level-- + parser.simple_keys = parser.simple_keys[:len(parser.simple_keys)-1] + } + return true +} + +// Push the current indentation level to the stack and set the new level +// the current column is greater than the indentation level. In this case, +// append or insert the specified token into the token queue. +func yaml_parser_roll_indent(parser *yaml_parser_t, column, number int, typ yaml_token_type_t, mark yaml_mark_t) bool { + // In the flow context, do nothing. + if parser.flow_level > 0 { + return true + } + + if parser.indent < column { + // Push the current indentation level to the stack and set the new + // indentation level. + parser.indents = append(parser.indents, parser.indent) + parser.indent = column + + // Create a token and insert it into the queue. + token := yaml_token_t{ + typ: typ, + start_mark: mark, + end_mark: mark, + } + if number > -1 { + number -= parser.tokens_parsed + } + yaml_insert_token(parser, number, &token) + } + return true +} + +// Pop indentation levels from the indents stack until the current level +// becomes less or equal to the column. For each indentation level, append +// the BLOCK-END token. +func yaml_parser_unroll_indent(parser *yaml_parser_t, column int, scan_mark yaml_mark_t) bool { + // In the flow context, do nothing. + if parser.flow_level > 0 { + return true + } + + block_mark := scan_mark + block_mark.index-- + + // Loop through the indentation levels in the stack. + for parser.indent > column { + + // [Go] Reposition the end token before potential following + // foot comments of parent blocks. For that, search + // backwards for recent comments that were at the same + // indent as the block that is ending now. + stop_index := block_mark.index + for i := len(parser.comments) - 1; i >= 0; i-- { + comment := &parser.comments[i] + + if comment.end_mark.index < stop_index { + // Don't go back beyond the start of the comment/whitespace scan, unless column < 0. + // If requested indent column is < 0, then the document is over and everything else + // is a foot anyway. + break + } + if comment.start_mark.column == parser.indent+1 { + // This is a good match. But maybe there's a former comment + // at that same indent level, so keep searching. + block_mark = comment.start_mark + } + + // While the end of the former comment matches with + // the start of the following one, we know there's + // nothing in between and scanning is still safe. + stop_index = comment.scan_mark.index + } + + // Create a token and append it to the queue. + token := yaml_token_t{ + typ: yaml_BLOCK_END_TOKEN, + start_mark: block_mark, + end_mark: block_mark, + } + yaml_insert_token(parser, -1, &token) + + // Pop the indentation level. + parser.indent = parser.indents[len(parser.indents)-1] + parser.indents = parser.indents[:len(parser.indents)-1] + } + return true +} + +// Initialize the scanner and produce the STREAM-START token. +func yaml_parser_fetch_stream_start(parser *yaml_parser_t) bool { + + // Set the initial indentation. + parser.indent = -1 + + // Initialize the simple key stack. + parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{}) + + // A simple key is allowed at the beginning of the stream. + parser.simple_key_allowed = true + + // We have started. + parser.stream_start_produced = true + + // Create the STREAM-START token and append it to the queue. + token := yaml_token_t{ + typ: yaml_STREAM_START_TOKEN, + start_mark: parser.mark, + end_mark: parser.mark, + encoding: parser.encoding, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the STREAM-END token and shut down the scanner. +func yaml_parser_fetch_stream_end(parser *yaml_parser_t) bool { + + // Force new line. + if parser.mark.column != 0 { + parser.mark.column = 0 + parser.mark.line++ + } + + // Reset the indentation level. + if !yaml_parser_unroll_indent(parser, -1, parser.mark) { + return false + } + + // Reset simple keys. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + parser.simple_key_allowed = false + + // Create the STREAM-END token and append it to the queue. + token := yaml_token_t{ + typ: yaml_STREAM_END_TOKEN, + start_mark: parser.mark, + end_mark: parser.mark, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce a VERSION-DIRECTIVE or TAG-DIRECTIVE token. +func yaml_parser_fetch_directive(parser *yaml_parser_t) bool { + // Reset the indentation level. + if !yaml_parser_unroll_indent(parser, -1, parser.mark) { + return false + } + + // Reset simple keys. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + parser.simple_key_allowed = false + + // Create the YAML-DIRECTIVE or TAG-DIRECTIVE token. + token := yaml_token_t{} + if !yaml_parser_scan_directive(parser, &token) { + return false + } + // Append the token to the queue. + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the DOCUMENT-START or DOCUMENT-END token. +func yaml_parser_fetch_document_indicator(parser *yaml_parser_t, typ yaml_token_type_t) bool { + // Reset the indentation level. + if !yaml_parser_unroll_indent(parser, -1, parser.mark) { + return false + } + + // Reset simple keys. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + parser.simple_key_allowed = false + + // Consume the token. + start_mark := parser.mark + + skip(parser) + skip(parser) + skip(parser) + + end_mark := parser.mark + + // Create the DOCUMENT-START or DOCUMENT-END token. + token := yaml_token_t{ + typ: typ, + start_mark: start_mark, + end_mark: end_mark, + } + // Append the token to the queue. + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the FLOW-SEQUENCE-START or FLOW-MAPPING-START token. +func yaml_parser_fetch_flow_collection_start(parser *yaml_parser_t, typ yaml_token_type_t) bool { + + // The indicators '[' and '{' may start a simple key. + if !yaml_parser_save_simple_key(parser) { + return false + } + + // Increase the flow level. + if !yaml_parser_increase_flow_level(parser) { + return false + } + + // A simple key may follow the indicators '[' and '{'. + parser.simple_key_allowed = true + + // Consume the token. + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the FLOW-SEQUENCE-START of FLOW-MAPPING-START token. + token := yaml_token_t{ + typ: typ, + start_mark: start_mark, + end_mark: end_mark, + } + // Append the token to the queue. + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the FLOW-SEQUENCE-END or FLOW-MAPPING-END token. +func yaml_parser_fetch_flow_collection_end(parser *yaml_parser_t, typ yaml_token_type_t) bool { + // Reset any potential simple key on the current flow level. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + // Decrease the flow level. + if !yaml_parser_decrease_flow_level(parser) { + return false + } + + // No simple keys after the indicators ']' and '}'. + parser.simple_key_allowed = false + + // Consume the token. + + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the FLOW-SEQUENCE-END of FLOW-MAPPING-END token. + token := yaml_token_t{ + typ: typ, + start_mark: start_mark, + end_mark: end_mark, + } + // Append the token to the queue. + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the FLOW-ENTRY token. +func yaml_parser_fetch_flow_entry(parser *yaml_parser_t) bool { + // Reset any potential simple keys on the current flow level. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + // Simple keys are allowed after ','. + parser.simple_key_allowed = true + + // Consume the token. + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the FLOW-ENTRY token and append it to the queue. + token := yaml_token_t{ + typ: yaml_FLOW_ENTRY_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the BLOCK-ENTRY token. +func yaml_parser_fetch_block_entry(parser *yaml_parser_t) bool { + // Check if the scanner is in the block context. + if parser.flow_level == 0 { + // Check if we are allowed to start a new entry. + if !parser.simple_key_allowed { + return yaml_parser_set_scanner_error(parser, "", parser.mark, + "block sequence entries are not allowed in this context") + } + // Add the BLOCK-SEQUENCE-START token if needed. + if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_SEQUENCE_START_TOKEN, parser.mark) { + return false + } + } else { + // It is an error for the '-' indicator to occur in the flow context, + // but we let the Parser detect and report about it because the Parser + // is able to point to the context. + } + + // Reset any potential simple keys on the current flow level. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + // Simple keys are allowed after '-'. + parser.simple_key_allowed = true + + // Consume the token. + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the BLOCK-ENTRY token and append it to the queue. + token := yaml_token_t{ + typ: yaml_BLOCK_ENTRY_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the KEY token. +func yaml_parser_fetch_key(parser *yaml_parser_t) bool { + + // In the block context, additional checks are required. + if parser.flow_level == 0 { + // Check if we are allowed to start a new key (not nessesary simple). + if !parser.simple_key_allowed { + return yaml_parser_set_scanner_error(parser, "", parser.mark, + "mapping keys are not allowed in this context") + } + // Add the BLOCK-MAPPING-START token if needed. + if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_MAPPING_START_TOKEN, parser.mark) { + return false + } + } + + // Reset any potential simple keys on the current flow level. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + // Simple keys are allowed after '?' in the block context. + parser.simple_key_allowed = parser.flow_level == 0 + + // Consume the token. + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the KEY token and append it to the queue. + token := yaml_token_t{ + typ: yaml_KEY_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the VALUE token. +func yaml_parser_fetch_value(parser *yaml_parser_t) bool { + + simple_key := &parser.simple_keys[len(parser.simple_keys)-1] + + // Have we found a simple key? + if simple_key.possible { + // Create the KEY token and insert it into the queue. + token := yaml_token_t{ + typ: yaml_KEY_TOKEN, + start_mark: simple_key.mark, + end_mark: simple_key.mark, + } + yaml_insert_token(parser, simple_key.token_number-parser.tokens_parsed, &token) + + // In the block context, we may need to add the BLOCK-MAPPING-START token. + if !yaml_parser_roll_indent(parser, simple_key.mark.column, + simple_key.token_number, + yaml_BLOCK_MAPPING_START_TOKEN, simple_key.mark) { + return false + } + + // Remove the simple key. + simple_key.possible = false + + // A simple key cannot follow another simple key. + parser.simple_key_allowed = false + + } else { + // The ':' indicator follows a complex key. + + // In the block context, extra checks are required. + if parser.flow_level == 0 { + + // Check if we are allowed to start a complex value. + if !parser.simple_key_allowed { + return yaml_parser_set_scanner_error(parser, "", parser.mark, + "mapping values are not allowed in this context") + } + + // Add the BLOCK-MAPPING-START token if needed. + if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_MAPPING_START_TOKEN, parser.mark) { + return false + } + } + + // Simple keys after ':' are allowed in the block context. + parser.simple_key_allowed = parser.flow_level == 0 + } + + // Consume the token. + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the VALUE token and append it to the queue. + token := yaml_token_t{ + typ: yaml_VALUE_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the ALIAS or ANCHOR token. +func yaml_parser_fetch_anchor(parser *yaml_parser_t, typ yaml_token_type_t) bool { + // An anchor or an alias could be a simple key. + if !yaml_parser_save_simple_key(parser) { + return false + } + + // A simple key cannot follow an anchor or an alias. + parser.simple_key_allowed = false + + // Create the ALIAS or ANCHOR token and append it to the queue. + var token yaml_token_t + if !yaml_parser_scan_anchor(parser, &token, typ) { + return false + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the TAG token. +func yaml_parser_fetch_tag(parser *yaml_parser_t) bool { + // A tag could be a simple key. + if !yaml_parser_save_simple_key(parser) { + return false + } + + // A simple key cannot follow a tag. + parser.simple_key_allowed = false + + // Create the TAG token and append it to the queue. + var token yaml_token_t + if !yaml_parser_scan_tag(parser, &token) { + return false + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the SCALAR(...,literal) or SCALAR(...,folded) tokens. +func yaml_parser_fetch_block_scalar(parser *yaml_parser_t, literal bool) bool { + // Remove any potential simple keys. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + // A simple key may follow a block scalar. + parser.simple_key_allowed = true + + // Create the SCALAR token and append it to the queue. + var token yaml_token_t + if !yaml_parser_scan_block_scalar(parser, &token, literal) { + return false + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the SCALAR(...,single-quoted) or SCALAR(...,double-quoted) tokens. +func yaml_parser_fetch_flow_scalar(parser *yaml_parser_t, single bool) bool { + // A plain scalar could be a simple key. + if !yaml_parser_save_simple_key(parser) { + return false + } + + // A simple key cannot follow a flow scalar. + parser.simple_key_allowed = false + + // Create the SCALAR token and append it to the queue. + var token yaml_token_t + if !yaml_parser_scan_flow_scalar(parser, &token, single) { + return false + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the SCALAR(...,plain) token. +func yaml_parser_fetch_plain_scalar(parser *yaml_parser_t) bool { + // A plain scalar could be a simple key. + if !yaml_parser_save_simple_key(parser) { + return false + } + + // A simple key cannot follow a flow scalar. + parser.simple_key_allowed = false + + // Create the SCALAR token and append it to the queue. + var token yaml_token_t + if !yaml_parser_scan_plain_scalar(parser, &token) { + return false + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Eat whitespaces and comments until the next token is found. +func yaml_parser_scan_to_next_token(parser *yaml_parser_t) bool { + + scan_mark := parser.mark + + // Until the next token is not found. + for { + // Allow the BOM mark to start a line. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if parser.mark.column == 0 && is_bom(parser.buffer, parser.buffer_pos) { + skip(parser) + } + + // Eat whitespaces. + // Tabs are allowed: + // - in the flow context + // - in the block context, but not at the beginning of the line or + // after '-', '?', or ':' (complex value). + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + for parser.buffer[parser.buffer_pos] == ' ' || ((parser.flow_level > 0 || !parser.simple_key_allowed) && parser.buffer[parser.buffer_pos] == '\t') { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Eat a comment until a line break. + if parser.buffer[parser.buffer_pos] == '#' { + if !yaml_parser_scan_comments(parser, scan_mark) { + return false + } + } + + // If it is a line break, eat it. + if is_break(parser.buffer, parser.buffer_pos) { + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + skip_line(parser) + + // In the block context, a new line may start a simple key. + if parser.flow_level == 0 { + parser.simple_key_allowed = true + } + } else { + break // We have found a token. + } + } + + return true +} + +// Scan a YAML-DIRECTIVE or TAG-DIRECTIVE token. +// +// Scope: +// %YAML 1.1 # a comment \n +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// %TAG !yaml! tag:yaml.org,2002: \n +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// +func yaml_parser_scan_directive(parser *yaml_parser_t, token *yaml_token_t) bool { + // Eat '%'. + start_mark := parser.mark + skip(parser) + + // Scan the directive name. + var name []byte + if !yaml_parser_scan_directive_name(parser, start_mark, &name) { + return false + } + + // Is it a YAML directive? + if bytes.Equal(name, []byte("YAML")) { + // Scan the VERSION directive value. + var major, minor int8 + if !yaml_parser_scan_version_directive_value(parser, start_mark, &major, &minor) { + return false + } + end_mark := parser.mark + + // Create a VERSION-DIRECTIVE token. + *token = yaml_token_t{ + typ: yaml_VERSION_DIRECTIVE_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + major: major, + minor: minor, + } + + // Is it a TAG directive? + } else if bytes.Equal(name, []byte("TAG")) { + // Scan the TAG directive value. + var handle, prefix []byte + if !yaml_parser_scan_tag_directive_value(parser, start_mark, &handle, &prefix) { + return false + } + end_mark := parser.mark + + // Create a TAG-DIRECTIVE token. + *token = yaml_token_t{ + typ: yaml_TAG_DIRECTIVE_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + value: handle, + prefix: prefix, + } + + // Unknown directive. + } else { + yaml_parser_set_scanner_error(parser, "while scanning a directive", + start_mark, "found unknown directive name") + return false + } + + // Eat the rest of the line including any comments. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + for is_blank(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + if parser.buffer[parser.buffer_pos] == '#' { + // [Go] Discard this inline comment for the time being. + //if !yaml_parser_scan_line_comment(parser, start_mark) { + // return false + //} + for !is_breakz(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + } + + // Check if we are at the end of the line. + if !is_breakz(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a directive", + start_mark, "did not find expected comment or line break") + return false + } + + // Eat a line break. + if is_break(parser.buffer, parser.buffer_pos) { + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + skip_line(parser) + } + + return true +} + +// Scan the directive name. +// +// Scope: +// %YAML 1.1 # a comment \n +// ^^^^ +// %TAG !yaml! tag:yaml.org,2002: \n +// ^^^ +// +func yaml_parser_scan_directive_name(parser *yaml_parser_t, start_mark yaml_mark_t, name *[]byte) bool { + // Consume the directive name. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + var s []byte + for is_alpha(parser.buffer, parser.buffer_pos) { + s = read(parser, s) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Check if the name is empty. + if len(s) == 0 { + yaml_parser_set_scanner_error(parser, "while scanning a directive", + start_mark, "could not find expected directive name") + return false + } + + // Check for an blank character after the name. + if !is_blankz(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a directive", + start_mark, "found unexpected non-alphabetical character") + return false + } + *name = s + return true +} + +// Scan the value of VERSION-DIRECTIVE. +// +// Scope: +// %YAML 1.1 # a comment \n +// ^^^^^^ +func yaml_parser_scan_version_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, major, minor *int8) bool { + // Eat whitespaces. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + for is_blank(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Consume the major version number. + if !yaml_parser_scan_version_directive_number(parser, start_mark, major) { + return false + } + + // Eat '.'. + if parser.buffer[parser.buffer_pos] != '.' { + return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive", + start_mark, "did not find expected digit or '.' character") + } + + skip(parser) + + // Consume the minor version number. + if !yaml_parser_scan_version_directive_number(parser, start_mark, minor) { + return false + } + return true +} + +const max_number_length = 2 + +// Scan the version number of VERSION-DIRECTIVE. +// +// Scope: +// %YAML 1.1 # a comment \n +// ^ +// %YAML 1.1 # a comment \n +// ^ +func yaml_parser_scan_version_directive_number(parser *yaml_parser_t, start_mark yaml_mark_t, number *int8) bool { + + // Repeat while the next character is digit. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + var value, length int8 + for is_digit(parser.buffer, parser.buffer_pos) { + // Check if the number is too long. + length++ + if length > max_number_length { + return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive", + start_mark, "found extremely long version number") + } + value = value*10 + int8(as_digit(parser.buffer, parser.buffer_pos)) + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Check if the number was present. + if length == 0 { + return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive", + start_mark, "did not find expected version number") + } + *number = value + return true +} + +// Scan the value of a TAG-DIRECTIVE token. +// +// Scope: +// %TAG !yaml! tag:yaml.org,2002: \n +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// +func yaml_parser_scan_tag_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, handle, prefix *[]byte) bool { + var handle_value, prefix_value []byte + + // Eat whitespaces. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + for is_blank(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Scan a handle. + if !yaml_parser_scan_tag_handle(parser, true, start_mark, &handle_value) { + return false + } + + // Expect a whitespace. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if !is_blank(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a %TAG directive", + start_mark, "did not find expected whitespace") + return false + } + + // Eat whitespaces. + for is_blank(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Scan a prefix. + if !yaml_parser_scan_tag_uri(parser, true, nil, start_mark, &prefix_value) { + return false + } + + // Expect a whitespace or line break. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if !is_blankz(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a %TAG directive", + start_mark, "did not find expected whitespace or line break") + return false + } + + *handle = handle_value + *prefix = prefix_value + return true +} + +func yaml_parser_scan_anchor(parser *yaml_parser_t, token *yaml_token_t, typ yaml_token_type_t) bool { + var s []byte + + // Eat the indicator character. + start_mark := parser.mark + skip(parser) + + // Consume the value. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + for is_alpha(parser.buffer, parser.buffer_pos) { + s = read(parser, s) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + end_mark := parser.mark + + /* + * Check if length of the anchor is greater than 0 and it is followed by + * a whitespace character or one of the indicators: + * + * '?', ':', ',', ']', '}', '%', '@', '`'. + */ + + if len(s) == 0 || + !(is_blankz(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == '?' || + parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == ',' || + parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '}' || + parser.buffer[parser.buffer_pos] == '%' || parser.buffer[parser.buffer_pos] == '@' || + parser.buffer[parser.buffer_pos] == '`') { + context := "while scanning an alias" + if typ == yaml_ANCHOR_TOKEN { + context = "while scanning an anchor" + } + yaml_parser_set_scanner_error(parser, context, start_mark, + "did not find expected alphabetic or numeric character") + return false + } + + // Create a token. + *token = yaml_token_t{ + typ: typ, + start_mark: start_mark, + end_mark: end_mark, + value: s, + } + + return true +} + +/* + * Scan a TAG token. + */ + +func yaml_parser_scan_tag(parser *yaml_parser_t, token *yaml_token_t) bool { + var handle, suffix []byte + + start_mark := parser.mark + + // Check if the tag is in the canonical form. + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + + if parser.buffer[parser.buffer_pos+1] == '<' { + // Keep the handle as '' + + // Eat '!<' + skip(parser) + skip(parser) + + // Consume the tag value. + if !yaml_parser_scan_tag_uri(parser, false, nil, start_mark, &suffix) { + return false + } + + // Check for '>' and eat it. + if parser.buffer[parser.buffer_pos] != '>' { + yaml_parser_set_scanner_error(parser, "while scanning a tag", + start_mark, "did not find the expected '>'") + return false + } + + skip(parser) + } else { + // The tag has either the '!suffix' or the '!handle!suffix' form. + + // First, try to scan a handle. + if !yaml_parser_scan_tag_handle(parser, false, start_mark, &handle) { + return false + } + + // Check if it is, indeed, handle. + if handle[0] == '!' && len(handle) > 1 && handle[len(handle)-1] == '!' { + // Scan the suffix now. + if !yaml_parser_scan_tag_uri(parser, false, nil, start_mark, &suffix) { + return false + } + } else { + // It wasn't a handle after all. Scan the rest of the tag. + if !yaml_parser_scan_tag_uri(parser, false, handle, start_mark, &suffix) { + return false + } + + // Set the handle to '!'. + handle = []byte{'!'} + + // A special case: the '!' tag. Set the handle to '' and the + // suffix to '!'. + if len(suffix) == 0 { + handle, suffix = suffix, handle + } + } + } + + // Check the character which ends the tag. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if !is_blankz(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a tag", + start_mark, "did not find expected whitespace or line break") + return false + } + + end_mark := parser.mark + + // Create a token. + *token = yaml_token_t{ + typ: yaml_TAG_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + value: handle, + suffix: suffix, + } + return true +} + +// Scan a tag handle. +func yaml_parser_scan_tag_handle(parser *yaml_parser_t, directive bool, start_mark yaml_mark_t, handle *[]byte) bool { + // Check the initial '!' character. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if parser.buffer[parser.buffer_pos] != '!' { + yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "did not find expected '!'") + return false + } + + var s []byte + + // Copy the '!' character. + s = read(parser, s) + + // Copy all subsequent alphabetical and numerical characters. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + for is_alpha(parser.buffer, parser.buffer_pos) { + s = read(parser, s) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Check if the trailing character is '!' and copy it. + if parser.buffer[parser.buffer_pos] == '!' { + s = read(parser, s) + } else { + // It's either the '!' tag or not really a tag handle. If it's a %TAG + // directive, it's an error. If it's a tag token, it must be a part of URI. + if directive && string(s) != "!" { + yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "did not find expected '!'") + return false + } + } + + *handle = s + return true +} + +// Scan a tag. +func yaml_parser_scan_tag_uri(parser *yaml_parser_t, directive bool, head []byte, start_mark yaml_mark_t, uri *[]byte) bool { + //size_t length = head ? strlen((char *)head) : 0 + var s []byte + hasTag := len(head) > 0 + + // Copy the head if needed. + // + // Note that we don't copy the leading '!' character. + if len(head) > 1 { + s = append(s, head[1:]...) + } + + // Scan the tag. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + // The set of characters that may appear in URI is as follows: + // + // '0'-'9', 'A'-'Z', 'a'-'z', '_', '-', ';', '/', '?', ':', '@', '&', + // '=', '+', '$', ',', '.', '!', '~', '*', '\'', '(', ')', '[', ']', + // '%'. + // [Go] TODO Convert this into more reasonable logic. + for is_alpha(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == ';' || + parser.buffer[parser.buffer_pos] == '/' || parser.buffer[parser.buffer_pos] == '?' || + parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == '@' || + parser.buffer[parser.buffer_pos] == '&' || parser.buffer[parser.buffer_pos] == '=' || + parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '$' || + parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == '.' || + parser.buffer[parser.buffer_pos] == '!' || parser.buffer[parser.buffer_pos] == '~' || + parser.buffer[parser.buffer_pos] == '*' || parser.buffer[parser.buffer_pos] == '\'' || + parser.buffer[parser.buffer_pos] == '(' || parser.buffer[parser.buffer_pos] == ')' || + parser.buffer[parser.buffer_pos] == '[' || parser.buffer[parser.buffer_pos] == ']' || + parser.buffer[parser.buffer_pos] == '%' { + // Check if it is a URI-escape sequence. + if parser.buffer[parser.buffer_pos] == '%' { + if !yaml_parser_scan_uri_escapes(parser, directive, start_mark, &s) { + return false + } + } else { + s = read(parser, s) + } + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + hasTag = true + } + + if !hasTag { + yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "did not find expected tag URI") + return false + } + *uri = s + return true +} + +// Decode an URI-escape sequence corresponding to a single UTF-8 character. +func yaml_parser_scan_uri_escapes(parser *yaml_parser_t, directive bool, start_mark yaml_mark_t, s *[]byte) bool { + + // Decode the required number of characters. + w := 1024 + for w > 0 { + // Check for a URI-escaped octet. + if parser.unread < 3 && !yaml_parser_update_buffer(parser, 3) { + return false + } + + if !(parser.buffer[parser.buffer_pos] == '%' && + is_hex(parser.buffer, parser.buffer_pos+1) && + is_hex(parser.buffer, parser.buffer_pos+2)) { + return yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "did not find URI escaped octet") + } + + // Get the octet. + octet := byte((as_hex(parser.buffer, parser.buffer_pos+1) << 4) + as_hex(parser.buffer, parser.buffer_pos+2)) + + // If it is the leading octet, determine the length of the UTF-8 sequence. + if w == 1024 { + w = width(octet) + if w == 0 { + return yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "found an incorrect leading UTF-8 octet") + } + } else { + // Check if the trailing octet is correct. + if octet&0xC0 != 0x80 { + return yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "found an incorrect trailing UTF-8 octet") + } + } + + // Copy the octet and move the pointers. + *s = append(*s, octet) + skip(parser) + skip(parser) + skip(parser) + w-- + } + return true +} + +// Scan a block scalar. +func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, literal bool) bool { + // Eat the indicator '|' or '>'. + start_mark := parser.mark + skip(parser) + + // Scan the additional block scalar indicators. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + // Check for a chomping indicator. + var chomping, increment int + if parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '-' { + // Set the chomping method and eat the indicator. + if parser.buffer[parser.buffer_pos] == '+' { + chomping = +1 + } else { + chomping = -1 + } + skip(parser) + + // Check for an indentation indicator. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if is_digit(parser.buffer, parser.buffer_pos) { + // Check that the indentation is greater than 0. + if parser.buffer[parser.buffer_pos] == '0' { + yaml_parser_set_scanner_error(parser, "while scanning a block scalar", + start_mark, "found an indentation indicator equal to 0") + return false + } + + // Get the indentation level and eat the indicator. + increment = as_digit(parser.buffer, parser.buffer_pos) + skip(parser) + } + + } else if is_digit(parser.buffer, parser.buffer_pos) { + // Do the same as above, but in the opposite order. + + if parser.buffer[parser.buffer_pos] == '0' { + yaml_parser_set_scanner_error(parser, "while scanning a block scalar", + start_mark, "found an indentation indicator equal to 0") + return false + } + increment = as_digit(parser.buffer, parser.buffer_pos) + skip(parser) + + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '-' { + if parser.buffer[parser.buffer_pos] == '+' { + chomping = +1 + } else { + chomping = -1 + } + skip(parser) + } + } + + // Eat whitespaces and comments to the end of the line. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + for is_blank(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + if parser.buffer[parser.buffer_pos] == '#' { + if !yaml_parser_scan_line_comment(parser, start_mark) { + return false + } + } + + // Check if we are at the end of the line. + if !is_breakz(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a block scalar", + start_mark, "did not find expected comment or line break") + return false + } + + // Eat a line break. + if is_break(parser.buffer, parser.buffer_pos) { + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + skip_line(parser) + } + + end_mark := parser.mark + + // Set the indentation level if it was specified. + var indent int + if increment > 0 { + if parser.indent >= 0 { + indent = parser.indent + increment + } else { + indent = increment + } + } + + // Scan the leading line breaks and determine the indentation level if needed. + var s, leading_break, trailing_breaks []byte + if !yaml_parser_scan_block_scalar_breaks(parser, &indent, &trailing_breaks, start_mark, &end_mark) { + return false + } + + // Scan the block scalar content. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + var leading_blank, trailing_blank bool + for parser.mark.column == indent && !is_z(parser.buffer, parser.buffer_pos) { + // We are at the beginning of a non-empty line. + + // Is it a trailing whitespace? + trailing_blank = is_blank(parser.buffer, parser.buffer_pos) + + // Check if we need to fold the leading line break. + if !literal && !leading_blank && !trailing_blank && len(leading_break) > 0 && leading_break[0] == '\n' { + // Do we need to join the lines by space? + if len(trailing_breaks) == 0 { + s = append(s, ' ') + } + } else { + s = append(s, leading_break...) + } + leading_break = leading_break[:0] + + // Append the remaining line breaks. + s = append(s, trailing_breaks...) + trailing_breaks = trailing_breaks[:0] + + // Is it a leading whitespace? + leading_blank = is_blank(parser.buffer, parser.buffer_pos) + + // Consume the current line. + for !is_breakz(parser.buffer, parser.buffer_pos) { + s = read(parser, s) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Consume the line break. + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + + leading_break = read_line(parser, leading_break) + + // Eat the following indentation spaces and line breaks. + if !yaml_parser_scan_block_scalar_breaks(parser, &indent, &trailing_breaks, start_mark, &end_mark) { + return false + } + } + + // Chomp the tail. + if chomping != -1 { + s = append(s, leading_break...) + } + if chomping == 1 { + s = append(s, trailing_breaks...) + } + + // Create a token. + *token = yaml_token_t{ + typ: yaml_SCALAR_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + value: s, + style: yaml_LITERAL_SCALAR_STYLE, + } + if !literal { + token.style = yaml_FOLDED_SCALAR_STYLE + } + return true +} + +// Scan indentation spaces and line breaks for a block scalar. Determine the +// indentation level if needed. +func yaml_parser_scan_block_scalar_breaks(parser *yaml_parser_t, indent *int, breaks *[]byte, start_mark yaml_mark_t, end_mark *yaml_mark_t) bool { + *end_mark = parser.mark + + // Eat the indentation spaces and line breaks. + max_indent := 0 + for { + // Eat the indentation spaces. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + for (*indent == 0 || parser.mark.column < *indent) && is_space(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + if parser.mark.column > max_indent { + max_indent = parser.mark.column + } + + // Check for a tab character messing the indentation. + if (*indent == 0 || parser.mark.column < *indent) && is_tab(parser.buffer, parser.buffer_pos) { + return yaml_parser_set_scanner_error(parser, "while scanning a block scalar", + start_mark, "found a tab character where an indentation space is expected") + } + + // Have we found a non-empty line? + if !is_break(parser.buffer, parser.buffer_pos) { + break + } + + // Consume the line break. + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + // [Go] Should really be returning breaks instead. + *breaks = read_line(parser, *breaks) + *end_mark = parser.mark + } + + // Determine the indentation level if needed. + if *indent == 0 { + *indent = max_indent + if *indent < parser.indent+1 { + *indent = parser.indent + 1 + } + if *indent < 1 { + *indent = 1 + } + } + return true +} + +// Scan a quoted scalar. +func yaml_parser_scan_flow_scalar(parser *yaml_parser_t, token *yaml_token_t, single bool) bool { + // Eat the left quote. + start_mark := parser.mark + skip(parser) + + // Consume the content of the quoted scalar. + var s, leading_break, trailing_breaks, whitespaces []byte + for { + // Check that there are no document indicators at the beginning of the line. + if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) { + return false + } + + if parser.mark.column == 0 && + ((parser.buffer[parser.buffer_pos+0] == '-' && + parser.buffer[parser.buffer_pos+1] == '-' && + parser.buffer[parser.buffer_pos+2] == '-') || + (parser.buffer[parser.buffer_pos+0] == '.' && + parser.buffer[parser.buffer_pos+1] == '.' && + parser.buffer[parser.buffer_pos+2] == '.')) && + is_blankz(parser.buffer, parser.buffer_pos+3) { + yaml_parser_set_scanner_error(parser, "while scanning a quoted scalar", + start_mark, "found unexpected document indicator") + return false + } + + // Check for EOF. + if is_z(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a quoted scalar", + start_mark, "found unexpected end of stream") + return false + } + + // Consume non-blank characters. + leading_blanks := false + for !is_blankz(parser.buffer, parser.buffer_pos) { + if single && parser.buffer[parser.buffer_pos] == '\'' && parser.buffer[parser.buffer_pos+1] == '\'' { + // Is is an escaped single quote. + s = append(s, '\'') + skip(parser) + skip(parser) + + } else if single && parser.buffer[parser.buffer_pos] == '\'' { + // It is a right single quote. + break + } else if !single && parser.buffer[parser.buffer_pos] == '"' { + // It is a right double quote. + break + + } else if !single && parser.buffer[parser.buffer_pos] == '\\' && is_break(parser.buffer, parser.buffer_pos+1) { + // It is an escaped line break. + if parser.unread < 3 && !yaml_parser_update_buffer(parser, 3) { + return false + } + skip(parser) + skip_line(parser) + leading_blanks = true + break + + } else if !single && parser.buffer[parser.buffer_pos] == '\\' { + // It is an escape sequence. + code_length := 0 + + // Check the escape character. + switch parser.buffer[parser.buffer_pos+1] { + case '0': + s = append(s, 0) + case 'a': + s = append(s, '\x07') + case 'b': + s = append(s, '\x08') + case 't', '\t': + s = append(s, '\x09') + case 'n': + s = append(s, '\x0A') + case 'v': + s = append(s, '\x0B') + case 'f': + s = append(s, '\x0C') + case 'r': + s = append(s, '\x0D') + case 'e': + s = append(s, '\x1B') + case ' ': + s = append(s, '\x20') + case '"': + s = append(s, '"') + case '\'': + s = append(s, '\'') + case '\\': + s = append(s, '\\') + case 'N': // NEL (#x85) + s = append(s, '\xC2') + s = append(s, '\x85') + case '_': // #xA0 + s = append(s, '\xC2') + s = append(s, '\xA0') + case 'L': // LS (#x2028) + s = append(s, '\xE2') + s = append(s, '\x80') + s = append(s, '\xA8') + case 'P': // PS (#x2029) + s = append(s, '\xE2') + s = append(s, '\x80') + s = append(s, '\xA9') + case 'x': + code_length = 2 + case 'u': + code_length = 4 + case 'U': + code_length = 8 + default: + yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar", + start_mark, "found unknown escape character") + return false + } + + skip(parser) + skip(parser) + + // Consume an arbitrary escape code. + if code_length > 0 { + var value int + + // Scan the character value. + if parser.unread < code_length && !yaml_parser_update_buffer(parser, code_length) { + return false + } + for k := 0; k < code_length; k++ { + if !is_hex(parser.buffer, parser.buffer_pos+k) { + yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar", + start_mark, "did not find expected hexdecimal number") + return false + } + value = (value << 4) + as_hex(parser.buffer, parser.buffer_pos+k) + } + + // Check the value and write the character. + if (value >= 0xD800 && value <= 0xDFFF) || value > 0x10FFFF { + yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar", + start_mark, "found invalid Unicode character escape code") + return false + } + if value <= 0x7F { + s = append(s, byte(value)) + } else if value <= 0x7FF { + s = append(s, byte(0xC0+(value>>6))) + s = append(s, byte(0x80+(value&0x3F))) + } else if value <= 0xFFFF { + s = append(s, byte(0xE0+(value>>12))) + s = append(s, byte(0x80+((value>>6)&0x3F))) + s = append(s, byte(0x80+(value&0x3F))) + } else { + s = append(s, byte(0xF0+(value>>18))) + s = append(s, byte(0x80+((value>>12)&0x3F))) + s = append(s, byte(0x80+((value>>6)&0x3F))) + s = append(s, byte(0x80+(value&0x3F))) + } + + // Advance the pointer. + for k := 0; k < code_length; k++ { + skip(parser) + } + } + } else { + // It is a non-escaped non-blank character. + s = read(parser, s) + } + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + } + + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + // Check if we are at the end of the scalar. + if single { + if parser.buffer[parser.buffer_pos] == '\'' { + break + } + } else { + if parser.buffer[parser.buffer_pos] == '"' { + break + } + } + + // Consume blank characters. + for is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos) { + if is_blank(parser.buffer, parser.buffer_pos) { + // Consume a space or a tab character. + if !leading_blanks { + whitespaces = read(parser, whitespaces) + } else { + skip(parser) + } + } else { + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + + // Check if it is a first line break. + if !leading_blanks { + whitespaces = whitespaces[:0] + leading_break = read_line(parser, leading_break) + leading_blanks = true + } else { + trailing_breaks = read_line(parser, trailing_breaks) + } + } + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Join the whitespaces or fold line breaks. + if leading_blanks { + // Do we need to fold line breaks? + if len(leading_break) > 0 && leading_break[0] == '\n' { + if len(trailing_breaks) == 0 { + s = append(s, ' ') + } else { + s = append(s, trailing_breaks...) + } + } else { + s = append(s, leading_break...) + s = append(s, trailing_breaks...) + } + trailing_breaks = trailing_breaks[:0] + leading_break = leading_break[:0] + } else { + s = append(s, whitespaces...) + whitespaces = whitespaces[:0] + } + } + + // Eat the right quote. + skip(parser) + end_mark := parser.mark + + // Create a token. + *token = yaml_token_t{ + typ: yaml_SCALAR_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + value: s, + style: yaml_SINGLE_QUOTED_SCALAR_STYLE, + } + if !single { + token.style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + return true +} + +// Scan a plain scalar. +func yaml_parser_scan_plain_scalar(parser *yaml_parser_t, token *yaml_token_t) bool { + + var s, leading_break, trailing_breaks, whitespaces []byte + var leading_blanks bool + var indent = parser.indent + 1 + + start_mark := parser.mark + end_mark := parser.mark + + // Consume the content of the plain scalar. + for { + // Check for a document indicator. + if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) { + return false + } + if parser.mark.column == 0 && + ((parser.buffer[parser.buffer_pos+0] == '-' && + parser.buffer[parser.buffer_pos+1] == '-' && + parser.buffer[parser.buffer_pos+2] == '-') || + (parser.buffer[parser.buffer_pos+0] == '.' && + parser.buffer[parser.buffer_pos+1] == '.' && + parser.buffer[parser.buffer_pos+2] == '.')) && + is_blankz(parser.buffer, parser.buffer_pos+3) { + break + } + + // Check for a comment. + if parser.buffer[parser.buffer_pos] == '#' { + break + } + + // Consume non-blank characters. + for !is_blankz(parser.buffer, parser.buffer_pos) { + + // Check for indicators that may end a plain scalar. + if (parser.buffer[parser.buffer_pos] == ':' && is_blankz(parser.buffer, parser.buffer_pos+1)) || + (parser.flow_level > 0 && + (parser.buffer[parser.buffer_pos] == ',' || + parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == '[' || + parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '{' || + parser.buffer[parser.buffer_pos] == '}')) { + break + } + + // Check if we need to join whitespaces and breaks. + if leading_blanks || len(whitespaces) > 0 { + if leading_blanks { + // Do we need to fold line breaks? + if leading_break[0] == '\n' { + if len(trailing_breaks) == 0 { + s = append(s, ' ') + } else { + s = append(s, trailing_breaks...) + } + } else { + s = append(s, leading_break...) + s = append(s, trailing_breaks...) + } + trailing_breaks = trailing_breaks[:0] + leading_break = leading_break[:0] + leading_blanks = false + } else { + s = append(s, whitespaces...) + whitespaces = whitespaces[:0] + } + } + + // Copy the character. + s = read(parser, s) + + end_mark = parser.mark + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + } + + // Is it the end? + if !(is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos)) { + break + } + + // Consume blank characters. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + for is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos) { + if is_blank(parser.buffer, parser.buffer_pos) { + + // Check for tab characters that abuse indentation. + if leading_blanks && parser.mark.column < indent && is_tab(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a plain scalar", + start_mark, "found a tab character that violates indentation") + return false + } + + // Consume a space or a tab character. + if !leading_blanks { + whitespaces = read(parser, whitespaces) + } else { + skip(parser) + } + } else { + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + + // Check if it is a first line break. + if !leading_blanks { + whitespaces = whitespaces[:0] + leading_break = read_line(parser, leading_break) + leading_blanks = true + } else { + trailing_breaks = read_line(parser, trailing_breaks) + } + } + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Check indentation level. + if parser.flow_level == 0 && parser.mark.column < indent { + break + } + } + + // Create a token. + *token = yaml_token_t{ + typ: yaml_SCALAR_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + value: s, + style: yaml_PLAIN_SCALAR_STYLE, + } + + // Note that we change the 'simple_key_allowed' flag. + if leading_blanks { + parser.simple_key_allowed = true + } + return true +} + +func yaml_parser_scan_line_comment(parser *yaml_parser_t, token_mark yaml_mark_t) bool { + if parser.newlines > 0 { + return true + } + + parser.comments = append(parser.comments, yaml_comment_t{token_mark: token_mark}) + comment := &parser.comments[len(parser.comments)-1].line + + for peek := 0; peek < 512; peek++ { + if parser.unread < peek+1 && !yaml_parser_update_buffer(parser, peek+1) { + break + } + if is_blank(parser.buffer, parser.buffer_pos+peek) { + continue + } + if parser.buffer[parser.buffer_pos+peek] == '#' { + if len(*comment) > 0 { + *comment = append(*comment, '\n') + } + + // Consume until after the consumed comment line. + seen := parser.mark.index+peek + for { + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if is_breakz(parser.buffer, parser.buffer_pos) { + if parser.mark.index >= seen { + break + } + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + skip_line(parser) + } else { + if parser.mark.index >= seen { + *comment = append(*comment, parser.buffer[parser.buffer_pos]) + } + skip(parser) + } + } + } + break + } + return true +} + +func yaml_parser_scan_comments(parser *yaml_parser_t, scan_mark yaml_mark_t) bool { + token := parser.tokens[len(parser.tokens)-1] + + if token.typ == yaml_FLOW_ENTRY_TOKEN && len(parser.tokens) > 1 { + token = parser.tokens[len(parser.tokens)-2] + } + + var token_mark = token.start_mark + var start_mark yaml_mark_t + + var recent_empty = false + var first_empty = parser.newlines <= 1 + + var line = parser.mark.line + var column = parser.mark.column + + var text []byte + + // The foot line is the place where a comment must start to + // still be considered as a foot of the prior content. + // If there's some content in the currently parsed line, then + // the foot is the line below it. + var foot_line = -1 + if scan_mark.line > 0 { + foot_line = parser.mark.line-parser.newlines+1 + if parser.newlines == 0 && parser.mark.column > 1 { + foot_line++ + } + } + + var peek = 0 + for ; peek < 512; peek++ { + if parser.unread < peek+1 && !yaml_parser_update_buffer(parser, peek+1) { + break + } + column++ + if is_blank(parser.buffer, parser.buffer_pos+peek) { + continue + } + c := parser.buffer[parser.buffer_pos+peek] + if is_breakz(parser.buffer, parser.buffer_pos+peek) || parser.flow_level > 0 && (c == ']' || c == '}') { + // Got line break or terminator. + if !recent_empty { + if first_empty && (start_mark.line == foot_line || start_mark.column-1 < parser.indent) { + // This is the first empty line and there were no empty lines before, + // so this initial part of the comment is a foot of the prior token + // instead of being a head for the following one. Split it up. + if len(text) > 0 { + if start_mark.column-1 < parser.indent { + // If dedented it's unrelated to the prior token. + token_mark = start_mark + } + parser.comments = append(parser.comments, yaml_comment_t{ + scan_mark: scan_mark, + token_mark: token_mark, + start_mark: start_mark, + end_mark: yaml_mark_t{parser.mark.index + peek, line, column}, + foot: text, + }) + scan_mark = yaml_mark_t{parser.mark.index + peek, line, column} + token_mark = scan_mark + text = nil + } + } else { + if len(text) > 0 && parser.buffer[parser.buffer_pos+peek] != 0 { + text = append(text, '\n') + } + } + } + if !is_break(parser.buffer, parser.buffer_pos+peek) { + break + } + first_empty = false + recent_empty = true + column = 0 + line++ + continue + } + + if len(text) > 0 && column < parser.indent+1 && column != start_mark.column { + // The comment at the different indentation is a foot of the + // preceding data rather than a head of the upcoming one. + parser.comments = append(parser.comments, yaml_comment_t{ + scan_mark: scan_mark, + token_mark: token_mark, + start_mark: start_mark, + end_mark: yaml_mark_t{parser.mark.index + peek, line, column}, + foot: text, + }) + scan_mark = yaml_mark_t{parser.mark.index + peek, line, column} + token_mark = scan_mark + text = nil + } + + if parser.buffer[parser.buffer_pos+peek] != '#' { + break + } + + if len(text) == 0 { + start_mark = yaml_mark_t{parser.mark.index + peek, line, column} + } else { + text = append(text, '\n') + } + + recent_empty = false + + // Consume until after the consumed comment line. + seen := parser.mark.index+peek + for { + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if is_breakz(parser.buffer, parser.buffer_pos) { + if parser.mark.index >= seen { + break + } + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + skip_line(parser) + } else { + if parser.mark.index >= seen { + text = append(text, parser.buffer[parser.buffer_pos]) + } + skip(parser) + } + } + + peek = 0 + column = 0 + line = parser.mark.line + } + + if len(text) > 0 { + parser.comments = append(parser.comments, yaml_comment_t{ + scan_mark: scan_mark, + token_mark: start_mark, + start_mark: start_mark, + end_mark: yaml_mark_t{parser.mark.index + peek - 1, line, column}, + head: text, + }) + } + return true +} diff --git a/vendor/gopkg.in/yaml.v3/sorter.go b/vendor/gopkg.in/yaml.v3/sorter.go new file mode 100644 index 0000000000..9210ece7e9 --- /dev/null +++ b/vendor/gopkg.in/yaml.v3/sorter.go @@ -0,0 +1,134 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// +// 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 yaml + +import ( + "reflect" + "unicode" +) + +type keyList []reflect.Value + +func (l keyList) Len() int { return len(l) } +func (l keyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } +func (l keyList) Less(i, j int) bool { + a := l[i] + b := l[j] + ak := a.Kind() + bk := b.Kind() + for (ak == reflect.Interface || ak == reflect.Ptr) && !a.IsNil() { + a = a.Elem() + ak = a.Kind() + } + for (bk == reflect.Interface || bk == reflect.Ptr) && !b.IsNil() { + b = b.Elem() + bk = b.Kind() + } + af, aok := keyFloat(a) + bf, bok := keyFloat(b) + if aok && bok { + if af != bf { + return af < bf + } + if ak != bk { + return ak < bk + } + return numLess(a, b) + } + if ak != reflect.String || bk != reflect.String { + return ak < bk + } + ar, br := []rune(a.String()), []rune(b.String()) + digits := false + for i := 0; i < len(ar) && i < len(br); i++ { + if ar[i] == br[i] { + digits = unicode.IsDigit(ar[i]) + continue + } + al := unicode.IsLetter(ar[i]) + bl := unicode.IsLetter(br[i]) + if al && bl { + return ar[i] < br[i] + } + if al || bl { + if digits { + return al + } else { + return bl + } + } + var ai, bi int + var an, bn int64 + if ar[i] == '0' || br[i] == '0' { + for j := i - 1; j >= 0 && unicode.IsDigit(ar[j]); j-- { + if ar[j] != '0' { + an = 1 + bn = 1 + break + } + } + } + for ai = i; ai < len(ar) && unicode.IsDigit(ar[ai]); ai++ { + an = an*10 + int64(ar[ai]-'0') + } + for bi = i; bi < len(br) && unicode.IsDigit(br[bi]); bi++ { + bn = bn*10 + int64(br[bi]-'0') + } + if an != bn { + return an < bn + } + if ai != bi { + return ai < bi + } + return ar[i] < br[i] + } + return len(ar) < len(br) +} + +// keyFloat returns a float value for v if it is a number/bool +// and whether it is a number/bool or not. +func keyFloat(v reflect.Value) (f float64, ok bool) { + switch v.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return float64(v.Int()), true + case reflect.Float32, reflect.Float64: + return v.Float(), true + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return float64(v.Uint()), true + case reflect.Bool: + if v.Bool() { + return 1, true + } + return 0, true + } + return 0, false +} + +// numLess returns whether a < b. +// a and b must necessarily have the same kind. +func numLess(a, b reflect.Value) bool { + switch a.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return a.Int() < b.Int() + case reflect.Float32, reflect.Float64: + return a.Float() < b.Float() + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return a.Uint() < b.Uint() + case reflect.Bool: + return !a.Bool() && b.Bool() + } + panic("not a number") +} diff --git a/vendor/gopkg.in/yaml.v3/writerc.go b/vendor/gopkg.in/yaml.v3/writerc.go new file mode 100644 index 0000000000..b8a116bf9a --- /dev/null +++ b/vendor/gopkg.in/yaml.v3/writerc.go @@ -0,0 +1,48 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// Copyright (c) 2006-2010 Kirill Simonov +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package yaml + +// Set the writer error and return false. +func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool { + emitter.error = yaml_WRITER_ERROR + emitter.problem = problem + return false +} + +// Flush the output buffer. +func yaml_emitter_flush(emitter *yaml_emitter_t) bool { + if emitter.write_handler == nil { + panic("write handler not set") + } + + // Check if the buffer is empty. + if emitter.buffer_pos == 0 { + return true + } + + if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil { + return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error()) + } + emitter.buffer_pos = 0 + return true +} diff --git a/vendor/gopkg.in/yaml.v3/yaml.go b/vendor/gopkg.in/yaml.v3/yaml.go new file mode 100644 index 0000000000..b5d35a50de --- /dev/null +++ b/vendor/gopkg.in/yaml.v3/yaml.go @@ -0,0 +1,662 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// +// 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 yaml implements YAML support for the Go language. +// +// Source code and other details for the project are available at GitHub: +// +// https://github.com/go-yaml/yaml +// +package yaml + +import ( + "errors" + "fmt" + "io" + "reflect" + "strings" + "sync" + "unicode/utf8" +) + +// The Unmarshaler interface may be implemented by types to customize their +// behavior when being unmarshaled from a YAML document. +type Unmarshaler interface { + UnmarshalYAML(value *Node) error +} + +type obsoleteUnmarshaler interface { + UnmarshalYAML(unmarshal func(interface{}) error) error +} + +// The Marshaler interface may be implemented by types to customize their +// behavior when being marshaled into a YAML document. The returned value +// is marshaled in place of the original value implementing Marshaler. +// +// If an error is returned by MarshalYAML, the marshaling procedure stops +// and returns with the provided error. +type Marshaler interface { + MarshalYAML() (interface{}, error) +} + +// Unmarshal decodes the first document found within the in byte slice +// and assigns decoded values into the out value. +// +// Maps and pointers (to a struct, string, int, etc) are accepted as out +// values. If an internal pointer within a struct is not initialized, +// the yaml package will initialize it if necessary for unmarshalling +// the provided data. The out parameter must not be nil. +// +// The type of the decoded values should be compatible with the respective +// values in out. If one or more values cannot be decoded due to a type +// mismatches, decoding continues partially until the end of the YAML +// content, and a *yaml.TypeError is returned with details for all +// missed values. +// +// Struct fields are only unmarshalled if they are exported (have an +// upper case first letter), and are unmarshalled using the field name +// lowercased as the default key. Custom keys may be defined via the +// "yaml" name in the field tag: the content preceding the first comma +// is used as the key, and the following comma-separated options are +// used to tweak the marshalling process (see Marshal). +// Conflicting names result in a runtime error. +// +// For example: +// +// type T struct { +// F int `yaml:"a,omitempty"` +// B int +// } +// var t T +// yaml.Unmarshal([]byte("a: 1\nb: 2"), &t) +// +// See the documentation of Marshal for the format of tags and a list of +// supported tag options. +// +func Unmarshal(in []byte, out interface{}) (err error) { + return unmarshal(in, out, false) +} + +// A Decorder reads and decodes YAML values from an input stream. +type Decoder struct { + parser *parser + knownFields bool +} + +// NewDecoder returns a new decoder that reads from r. +// +// The decoder introduces its own buffering and may read +// data from r beyond the YAML values requested. +func NewDecoder(r io.Reader) *Decoder { + return &Decoder{ + parser: newParserFromReader(r), + } +} + +// KnownFields ensures that the keys in decoded mappings to +// exist as fields in the struct being decoded into. +func (dec *Decoder) KnownFields(enable bool) { + dec.knownFields = enable +} + +// Decode reads the next YAML-encoded value from its input +// and stores it in the value pointed to by v. +// +// See the documentation for Unmarshal for details about the +// conversion of YAML into a Go value. +func (dec *Decoder) Decode(v interface{}) (err error) { + d := newDecoder() + d.knownFields = dec.knownFields + defer handleErr(&err) + node := dec.parser.parse() + if node == nil { + return io.EOF + } + out := reflect.ValueOf(v) + if out.Kind() == reflect.Ptr && !out.IsNil() { + out = out.Elem() + } + d.unmarshal(node, out) + if len(d.terrors) > 0 { + return &TypeError{d.terrors} + } + return nil +} + +// Decode decodes the node and stores its data into the value pointed to by v. +// +// See the documentation for Unmarshal for details about the +// conversion of YAML into a Go value. +func (n *Node) Decode(v interface{}) (err error) { + d := newDecoder() + defer handleErr(&err) + out := reflect.ValueOf(v) + if out.Kind() == reflect.Ptr && !out.IsNil() { + out = out.Elem() + } + d.unmarshal(n, out) + if len(d.terrors) > 0 { + return &TypeError{d.terrors} + } + return nil +} + +func unmarshal(in []byte, out interface{}, strict bool) (err error) { + defer handleErr(&err) + d := newDecoder() + p := newParser(in) + defer p.destroy() + node := p.parse() + if node != nil { + v := reflect.ValueOf(out) + if v.Kind() == reflect.Ptr && !v.IsNil() { + v = v.Elem() + } + d.unmarshal(node, v) + } + if len(d.terrors) > 0 { + return &TypeError{d.terrors} + } + return nil +} + +// Marshal serializes the value provided into a YAML document. The structure +// of the generated document will reflect the structure of the value itself. +// Maps and pointers (to struct, string, int, etc) are accepted as the in value. +// +// Struct fields are only marshalled if they are exported (have an upper case +// first letter), and are marshalled using the field name lowercased as the +// default key. Custom keys may be defined via the "yaml" name in the field +// tag: the content preceding the first comma is used as the key, and the +// following comma-separated options are used to tweak the marshalling process. +// Conflicting names result in a runtime error. +// +// The field tag format accepted is: +// +// `(...) yaml:"[][,[,]]" (...)` +// +// The following flags are currently supported: +// +// omitempty Only include the field if it's not set to the zero +// value for the type or to empty slices or maps. +// Zero valued structs will be omitted if all their public +// fields are zero, unless they implement an IsZero +// method (see the IsZeroer interface type), in which +// case the field will be included if that method returns true. +// +// flow Marshal using a flow style (useful for structs, +// sequences and maps). +// +// inline Inline the field, which must be a struct or a map, +// causing all of its fields or keys to be processed as if +// they were part of the outer struct. For maps, keys must +// not conflict with the yaml keys of other struct fields. +// +// In addition, if the key is "-", the field is ignored. +// +// For example: +// +// type T struct { +// F int `yaml:"a,omitempty"` +// B int +// } +// yaml.Marshal(&T{B: 2}) // Returns "b: 2\n" +// yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n" +// +func Marshal(in interface{}) (out []byte, err error) { + defer handleErr(&err) + e := newEncoder() + defer e.destroy() + e.marshalDoc("", reflect.ValueOf(in)) + e.finish() + out = e.out + return +} + +// An Encoder writes YAML values to an output stream. +type Encoder struct { + encoder *encoder +} + +// NewEncoder returns a new encoder that writes to w. +// The Encoder should be closed after use to flush all data +// to w. +func NewEncoder(w io.Writer) *Encoder { + return &Encoder{ + encoder: newEncoderWithWriter(w), + } +} + +// Encode writes the YAML encoding of v to the stream. +// If multiple items are encoded to the stream, the +// second and subsequent document will be preceded +// with a "---" document separator, but the first will not. +// +// See the documentation for Marshal for details about the conversion of Go +// values to YAML. +func (e *Encoder) Encode(v interface{}) (err error) { + defer handleErr(&err) + e.encoder.marshalDoc("", reflect.ValueOf(v)) + return nil +} + +// SetIndent changes the used indentation used when encoding. +func (e *Encoder) SetIndent(spaces int) { + if spaces < 0 { + panic("yaml: cannot indent to a negative number of spaces") + } + e.encoder.indent = spaces +} + +// Close closes the encoder by writing any remaining data. +// It does not write a stream terminating string "...". +func (e *Encoder) Close() (err error) { + defer handleErr(&err) + e.encoder.finish() + return nil +} + +func handleErr(err *error) { + if v := recover(); v != nil { + if e, ok := v.(yamlError); ok { + *err = e.err + } else { + panic(v) + } + } +} + +type yamlError struct { + err error +} + +func fail(err error) { + panic(yamlError{err}) +} + +func failf(format string, args ...interface{}) { + panic(yamlError{fmt.Errorf("yaml: "+format, args...)}) +} + +// A TypeError is returned by Unmarshal when one or more fields in +// the YAML document cannot be properly decoded into the requested +// types. When this error is returned, the value is still +// unmarshaled partially. +type TypeError struct { + Errors []string +} + +func (e *TypeError) Error() string { + return fmt.Sprintf("yaml: unmarshal errors:\n %s", strings.Join(e.Errors, "\n ")) +} + +type Kind uint32 + +const ( + DocumentNode Kind = 1 << iota + SequenceNode + MappingNode + ScalarNode + AliasNode +) + +type Style uint32 + +const ( + TaggedStyle Style = 1 << iota + DoubleQuotedStyle + SingleQuotedStyle + LiteralStyle + FoldedStyle + FlowStyle +) + +// Node represents an element in the YAML document hierarchy. While documents +// are typically encoded and decoded into higher level types, such as structs +// and maps, Node is an intermediate representation that allows detailed +// control over the content being decoded or encoded. +// +// Values that make use of the Node type interact with the yaml package in the +// same way any other type would do, by encoding and decoding yaml data +// directly or indirectly into them. +// +// For example: +// +// var person struct { +// Name string +// Address yaml.Node +// } +// err := yaml.Unmarshal(data, &person) +// +// Or by itself: +// +// var person Node +// err := yaml.Unmarshal(data, &person) +// +type Node struct { + // Kind defines whether the node is a document, a mapping, a sequence, + // a scalar value, or an alias to another node. The specific data type of + // scalar nodes may be obtained via the ShortTag and LongTag methods. + Kind Kind + + // Style allows customizing the apperance of the node in the tree. + Style Style + + // Tag holds the YAML tag defining the data type for the value. + // When decoding, this field will always be set to the resolved tag, + // even when it wasn't explicitly provided in the YAML content. + // When encoding, if this field is unset the value type will be + // implied from the node properties, and if it is set, it will only + // be serialized into the representation if TaggedStyle is used or + // the implicit tag diverges from the provided one. + Tag string + + // Value holds the unescaped and unquoted represenation of the value. + Value string + + // Anchor holds the anchor name for this node, which allows aliases to point to it. + Anchor string + + // Alias holds the node that this alias points to. Only valid when Kind is AliasNode. + Alias *Node + + // Content holds contained nodes for documents, mappings, and sequences. + Content []*Node + + // HeadComment holds any comments in the lines preceding the node and + // not separated by an empty line. + HeadComment string + + // LineComment holds any comments at the end of the line where the node is in. + LineComment string + + // FootComment holds any comments following the node and before empty lines. + FootComment string + + // Line and Column hold the node position in the decoded YAML text. + // These fields are not respected when encoding the node. + Line int + Column int +} + +// LongTag returns the long form of the tag that indicates the data type for +// the node. If the Tag field isn't explicitly defined, one will be computed +// based on the node properties. +func (n *Node) LongTag() string { + return longTag(n.ShortTag()) +} + +// ShortTag returns the short form of the YAML tag that indicates data type for +// the node. If the Tag field isn't explicitly defined, one will be computed +// based on the node properties. +func (n *Node) ShortTag() string { + if n.indicatedString() { + return strTag + } + if n.Tag == "" || n.Tag == "!" { + switch n.Kind { + case MappingNode: + return mapTag + case SequenceNode: + return seqTag + case AliasNode: + if n.Alias != nil { + return n.Alias.ShortTag() + } + case ScalarNode: + tag, _ := resolve("", n.Value) + return tag + } + return "" + } + return shortTag(n.Tag) +} + +func (n *Node) indicatedString() bool { + return n.Kind == ScalarNode && + (shortTag(n.Tag) == strTag || + (n.Tag == "" || n.Tag == "!") && n.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0) +} + +// SetString is a convenience function that sets the node to a string value +// and defines its style in a pleasant way depending on its content. +func (n *Node) SetString(s string) { + n.Kind = ScalarNode + if utf8.ValidString(s) { + n.Value = s + n.Tag = strTag + } else { + n.Value = encodeBase64(s) + n.Tag = binaryTag + } + if strings.Contains(n.Value, "\n") { + n.Style = LiteralStyle + } +} + +// -------------------------------------------------------------------------- +// Maintain a mapping of keys to structure field indexes + +// The code in this section was copied from mgo/bson. + +// structInfo holds details for the serialization of fields of +// a given struct. +type structInfo struct { + FieldsMap map[string]fieldInfo + FieldsList []fieldInfo + + // InlineMap is the number of the field in the struct that + // contains an ,inline map, or -1 if there's none. + InlineMap int + + // InlineUnmarshalers holds indexes to inlined fields that + // contain unmarshaler values. + InlineUnmarshalers [][]int +} + +type fieldInfo struct { + Key string + Num int + OmitEmpty bool + Flow bool + // Id holds the unique field identifier, so we can cheaply + // check for field duplicates without maintaining an extra map. + Id int + + // Inline holds the field index if the field is part of an inlined struct. + Inline []int +} + +var structMap = make(map[reflect.Type]*structInfo) +var fieldMapMutex sync.RWMutex +var unmarshalerType reflect.Type + +func init() { + var v Unmarshaler + unmarshalerType = reflect.ValueOf(&v).Elem().Type() +} + +func getStructInfo(st reflect.Type) (*structInfo, error) { + fieldMapMutex.RLock() + sinfo, found := structMap[st] + fieldMapMutex.RUnlock() + if found { + return sinfo, nil + } + + n := st.NumField() + fieldsMap := make(map[string]fieldInfo) + fieldsList := make([]fieldInfo, 0, n) + inlineMap := -1 + inlineUnmarshalers := [][]int(nil) + for i := 0; i != n; i++ { + field := st.Field(i) + if field.PkgPath != "" && !field.Anonymous { + continue // Private field + } + + info := fieldInfo{Num: i} + + tag := field.Tag.Get("yaml") + if tag == "" && strings.Index(string(field.Tag), ":") < 0 { + tag = string(field.Tag) + } + if tag == "-" { + continue + } + + inline := false + fields := strings.Split(tag, ",") + if len(fields) > 1 { + for _, flag := range fields[1:] { + switch flag { + case "omitempty": + info.OmitEmpty = true + case "flow": + info.Flow = true + case "inline": + inline = true + default: + return nil, errors.New(fmt.Sprintf("unsupported flag %q in tag %q of type %s", flag, tag, st)) + } + } + tag = fields[0] + } + + if inline { + switch field.Type.Kind() { + case reflect.Map: + if inlineMap >= 0 { + return nil, errors.New("multiple ,inline maps in struct " + st.String()) + } + if field.Type.Key() != reflect.TypeOf("") { + return nil, errors.New("option ,inline needs a map with string keys in struct " + st.String()) + } + inlineMap = info.Num + case reflect.Struct, reflect.Ptr: + ftype := field.Type + for ftype.Kind() == reflect.Ptr { + ftype = ftype.Elem() + } + if ftype.Kind() != reflect.Struct { + return nil, errors.New("option ,inline may only be used on a struct or map field") + } + if reflect.PtrTo(ftype).Implements(unmarshalerType) { + inlineUnmarshalers = append(inlineUnmarshalers, []int{i}) + } else { + sinfo, err := getStructInfo(ftype) + if err != nil { + return nil, err + } + for _, index := range sinfo.InlineUnmarshalers { + inlineUnmarshalers = append(inlineUnmarshalers, append([]int{i}, index...)) + } + for _, finfo := range sinfo.FieldsList { + if _, found := fieldsMap[finfo.Key]; found { + msg := "duplicated key '" + finfo.Key + "' in struct " + st.String() + return nil, errors.New(msg) + } + if finfo.Inline == nil { + finfo.Inline = []int{i, finfo.Num} + } else { + finfo.Inline = append([]int{i}, finfo.Inline...) + } + finfo.Id = len(fieldsList) + fieldsMap[finfo.Key] = finfo + fieldsList = append(fieldsList, finfo) + } + } + default: + return nil, errors.New("option ,inline may only be used on a struct or map field") + } + continue + } + + if tag != "" { + info.Key = tag + } else { + info.Key = strings.ToLower(field.Name) + } + + if _, found = fieldsMap[info.Key]; found { + msg := "duplicated key '" + info.Key + "' in struct " + st.String() + return nil, errors.New(msg) + } + + info.Id = len(fieldsList) + fieldsList = append(fieldsList, info) + fieldsMap[info.Key] = info + } + + sinfo = &structInfo{ + FieldsMap: fieldsMap, + FieldsList: fieldsList, + InlineMap: inlineMap, + InlineUnmarshalers: inlineUnmarshalers, + } + + fieldMapMutex.Lock() + structMap[st] = sinfo + fieldMapMutex.Unlock() + return sinfo, nil +} + +// IsZeroer is used to check whether an object is zero to +// determine whether it should be omitted when marshaling +// with the omitempty flag. One notable implementation +// is time.Time. +type IsZeroer interface { + IsZero() bool +} + +func isZero(v reflect.Value) bool { + kind := v.Kind() + if z, ok := v.Interface().(IsZeroer); ok { + if (kind == reflect.Ptr || kind == reflect.Interface) && v.IsNil() { + return true + } + return z.IsZero() + } + switch kind { + case reflect.String: + return len(v.String()) == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + case reflect.Slice: + return v.Len() == 0 + case reflect.Map: + return v.Len() == 0 + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Struct: + vt := v.Type() + for i := v.NumField() - 1; i >= 0; i-- { + if vt.Field(i).PkgPath != "" { + continue // Private field + } + if !isZero(v.Field(i)) { + return false + } + } + return true + } + return false +} diff --git a/vendor/gopkg.in/yaml.v3/yamlh.go b/vendor/gopkg.in/yaml.v3/yamlh.go new file mode 100644 index 0000000000..65fb0df3b1 --- /dev/null +++ b/vendor/gopkg.in/yaml.v3/yamlh.go @@ -0,0 +1,803 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// Copyright (c) 2006-2010 Kirill Simonov +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package yaml + +import ( + "fmt" + "io" +) + +// The version directive data. +type yaml_version_directive_t struct { + major int8 // The major version number. + minor int8 // The minor version number. +} + +// The tag directive data. +type yaml_tag_directive_t struct { + handle []byte // The tag handle. + prefix []byte // The tag prefix. +} + +type yaml_encoding_t int + +// The stream encoding. +const ( + // Let the parser choose the encoding. + yaml_ANY_ENCODING yaml_encoding_t = iota + + yaml_UTF8_ENCODING // The default UTF-8 encoding. + yaml_UTF16LE_ENCODING // The UTF-16-LE encoding with BOM. + yaml_UTF16BE_ENCODING // The UTF-16-BE encoding with BOM. +) + +type yaml_break_t int + +// Line break types. +const ( + // Let the parser choose the break type. + yaml_ANY_BREAK yaml_break_t = iota + + yaml_CR_BREAK // Use CR for line breaks (Mac style). + yaml_LN_BREAK // Use LN for line breaks (Unix style). + yaml_CRLN_BREAK // Use CR LN for line breaks (DOS style). +) + +type yaml_error_type_t int + +// Many bad things could happen with the parser and emitter. +const ( + // No error is produced. + yaml_NO_ERROR yaml_error_type_t = iota + + yaml_MEMORY_ERROR // Cannot allocate or reallocate a block of memory. + yaml_READER_ERROR // Cannot read or decode the input stream. + yaml_SCANNER_ERROR // Cannot scan the input stream. + yaml_PARSER_ERROR // Cannot parse the input stream. + yaml_COMPOSER_ERROR // Cannot compose a YAML document. + yaml_WRITER_ERROR // Cannot write to the output stream. + yaml_EMITTER_ERROR // Cannot emit a YAML stream. +) + +// The pointer position. +type yaml_mark_t struct { + index int // The position index. + line int // The position line. + column int // The position column. +} + +// Node Styles + +type yaml_style_t int8 + +type yaml_scalar_style_t yaml_style_t + +// Scalar styles. +const ( + // Let the emitter choose the style. + yaml_ANY_SCALAR_STYLE yaml_scalar_style_t = 0 + + yaml_PLAIN_SCALAR_STYLE yaml_scalar_style_t = 1 << iota // The plain scalar style. + yaml_SINGLE_QUOTED_SCALAR_STYLE // The single-quoted scalar style. + yaml_DOUBLE_QUOTED_SCALAR_STYLE // The double-quoted scalar style. + yaml_LITERAL_SCALAR_STYLE // The literal scalar style. + yaml_FOLDED_SCALAR_STYLE // The folded scalar style. +) + +type yaml_sequence_style_t yaml_style_t + +// Sequence styles. +const ( + // Let the emitter choose the style. + yaml_ANY_SEQUENCE_STYLE yaml_sequence_style_t = iota + + yaml_BLOCK_SEQUENCE_STYLE // The block sequence style. + yaml_FLOW_SEQUENCE_STYLE // The flow sequence style. +) + +type yaml_mapping_style_t yaml_style_t + +// Mapping styles. +const ( + // Let the emitter choose the style. + yaml_ANY_MAPPING_STYLE yaml_mapping_style_t = iota + + yaml_BLOCK_MAPPING_STYLE // The block mapping style. + yaml_FLOW_MAPPING_STYLE // The flow mapping style. +) + +// Tokens + +type yaml_token_type_t int + +// Token types. +const ( + // An empty token. + yaml_NO_TOKEN yaml_token_type_t = iota + + yaml_STREAM_START_TOKEN // A STREAM-START token. + yaml_STREAM_END_TOKEN // A STREAM-END token. + + yaml_VERSION_DIRECTIVE_TOKEN // A VERSION-DIRECTIVE token. + yaml_TAG_DIRECTIVE_TOKEN // A TAG-DIRECTIVE token. + yaml_DOCUMENT_START_TOKEN // A DOCUMENT-START token. + yaml_DOCUMENT_END_TOKEN // A DOCUMENT-END token. + + yaml_BLOCK_SEQUENCE_START_TOKEN // A BLOCK-SEQUENCE-START token. + yaml_BLOCK_MAPPING_START_TOKEN // A BLOCK-SEQUENCE-END token. + yaml_BLOCK_END_TOKEN // A BLOCK-END token. + + yaml_FLOW_SEQUENCE_START_TOKEN // A FLOW-SEQUENCE-START token. + yaml_FLOW_SEQUENCE_END_TOKEN // A FLOW-SEQUENCE-END token. + yaml_FLOW_MAPPING_START_TOKEN // A FLOW-MAPPING-START token. + yaml_FLOW_MAPPING_END_TOKEN // A FLOW-MAPPING-END token. + + yaml_BLOCK_ENTRY_TOKEN // A BLOCK-ENTRY token. + yaml_FLOW_ENTRY_TOKEN // A FLOW-ENTRY token. + yaml_KEY_TOKEN // A KEY token. + yaml_VALUE_TOKEN // A VALUE token. + + yaml_ALIAS_TOKEN // An ALIAS token. + yaml_ANCHOR_TOKEN // An ANCHOR token. + yaml_TAG_TOKEN // A TAG token. + yaml_SCALAR_TOKEN // A SCALAR token. +) + +func (tt yaml_token_type_t) String() string { + switch tt { + case yaml_NO_TOKEN: + return "yaml_NO_TOKEN" + case yaml_STREAM_START_TOKEN: + return "yaml_STREAM_START_TOKEN" + case yaml_STREAM_END_TOKEN: + return "yaml_STREAM_END_TOKEN" + case yaml_VERSION_DIRECTIVE_TOKEN: + return "yaml_VERSION_DIRECTIVE_TOKEN" + case yaml_TAG_DIRECTIVE_TOKEN: + return "yaml_TAG_DIRECTIVE_TOKEN" + case yaml_DOCUMENT_START_TOKEN: + return "yaml_DOCUMENT_START_TOKEN" + case yaml_DOCUMENT_END_TOKEN: + return "yaml_DOCUMENT_END_TOKEN" + case yaml_BLOCK_SEQUENCE_START_TOKEN: + return "yaml_BLOCK_SEQUENCE_START_TOKEN" + case yaml_BLOCK_MAPPING_START_TOKEN: + return "yaml_BLOCK_MAPPING_START_TOKEN" + case yaml_BLOCK_END_TOKEN: + return "yaml_BLOCK_END_TOKEN" + case yaml_FLOW_SEQUENCE_START_TOKEN: + return "yaml_FLOW_SEQUENCE_START_TOKEN" + case yaml_FLOW_SEQUENCE_END_TOKEN: + return "yaml_FLOW_SEQUENCE_END_TOKEN" + case yaml_FLOW_MAPPING_START_TOKEN: + return "yaml_FLOW_MAPPING_START_TOKEN" + case yaml_FLOW_MAPPING_END_TOKEN: + return "yaml_FLOW_MAPPING_END_TOKEN" + case yaml_BLOCK_ENTRY_TOKEN: + return "yaml_BLOCK_ENTRY_TOKEN" + case yaml_FLOW_ENTRY_TOKEN: + return "yaml_FLOW_ENTRY_TOKEN" + case yaml_KEY_TOKEN: + return "yaml_KEY_TOKEN" + case yaml_VALUE_TOKEN: + return "yaml_VALUE_TOKEN" + case yaml_ALIAS_TOKEN: + return "yaml_ALIAS_TOKEN" + case yaml_ANCHOR_TOKEN: + return "yaml_ANCHOR_TOKEN" + case yaml_TAG_TOKEN: + return "yaml_TAG_TOKEN" + case yaml_SCALAR_TOKEN: + return "yaml_SCALAR_TOKEN" + } + return "" +} + +// The token structure. +type yaml_token_t struct { + // The token type. + typ yaml_token_type_t + + // The start/end of the token. + start_mark, end_mark yaml_mark_t + + // The stream encoding (for yaml_STREAM_START_TOKEN). + encoding yaml_encoding_t + + // The alias/anchor/scalar value or tag/tag directive handle + // (for yaml_ALIAS_TOKEN, yaml_ANCHOR_TOKEN, yaml_SCALAR_TOKEN, yaml_TAG_TOKEN, yaml_TAG_DIRECTIVE_TOKEN). + value []byte + + // The tag suffix (for yaml_TAG_TOKEN). + suffix []byte + + // The tag directive prefix (for yaml_TAG_DIRECTIVE_TOKEN). + prefix []byte + + // The scalar style (for yaml_SCALAR_TOKEN). + style yaml_scalar_style_t + + // The version directive major/minor (for yaml_VERSION_DIRECTIVE_TOKEN). + major, minor int8 +} + +// Events + +type yaml_event_type_t int8 + +// Event types. +const ( + // An empty event. + yaml_NO_EVENT yaml_event_type_t = iota + + yaml_STREAM_START_EVENT // A STREAM-START event. + yaml_STREAM_END_EVENT // A STREAM-END event. + yaml_DOCUMENT_START_EVENT // A DOCUMENT-START event. + yaml_DOCUMENT_END_EVENT // A DOCUMENT-END event. + yaml_ALIAS_EVENT // An ALIAS event. + yaml_SCALAR_EVENT // A SCALAR event. + yaml_SEQUENCE_START_EVENT // A SEQUENCE-START event. + yaml_SEQUENCE_END_EVENT // A SEQUENCE-END event. + yaml_MAPPING_START_EVENT // A MAPPING-START event. + yaml_MAPPING_END_EVENT // A MAPPING-END event. + yaml_TAIL_COMMENT_EVENT +) + +var eventStrings = []string{ + yaml_NO_EVENT: "none", + yaml_STREAM_START_EVENT: "stream start", + yaml_STREAM_END_EVENT: "stream end", + yaml_DOCUMENT_START_EVENT: "document start", + yaml_DOCUMENT_END_EVENT: "document end", + yaml_ALIAS_EVENT: "alias", + yaml_SCALAR_EVENT: "scalar", + yaml_SEQUENCE_START_EVENT: "sequence start", + yaml_SEQUENCE_END_EVENT: "sequence end", + yaml_MAPPING_START_EVENT: "mapping start", + yaml_MAPPING_END_EVENT: "mapping end", + yaml_TAIL_COMMENT_EVENT: "tail comment", +} + +func (e yaml_event_type_t) String() string { + if e < 0 || int(e) >= len(eventStrings) { + return fmt.Sprintf("unknown event %d", e) + } + return eventStrings[e] +} + +// The event structure. +type yaml_event_t struct { + + // The event type. + typ yaml_event_type_t + + // The start and end of the event. + start_mark, end_mark yaml_mark_t + + // The document encoding (for yaml_STREAM_START_EVENT). + encoding yaml_encoding_t + + // The version directive (for yaml_DOCUMENT_START_EVENT). + version_directive *yaml_version_directive_t + + // The list of tag directives (for yaml_DOCUMENT_START_EVENT). + tag_directives []yaml_tag_directive_t + + // The comments + head_comment []byte + line_comment []byte + foot_comment []byte + tail_comment []byte + + // The anchor (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_ALIAS_EVENT). + anchor []byte + + // The tag (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT). + tag []byte + + // The scalar value (for yaml_SCALAR_EVENT). + value []byte + + // Is the document start/end indicator implicit, or the tag optional? + // (for yaml_DOCUMENT_START_EVENT, yaml_DOCUMENT_END_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_SCALAR_EVENT). + implicit bool + + // Is the tag optional for any non-plain style? (for yaml_SCALAR_EVENT). + quoted_implicit bool + + // The style (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT). + style yaml_style_t +} + +func (e *yaml_event_t) scalar_style() yaml_scalar_style_t { return yaml_scalar_style_t(e.style) } +func (e *yaml_event_t) sequence_style() yaml_sequence_style_t { return yaml_sequence_style_t(e.style) } +func (e *yaml_event_t) mapping_style() yaml_mapping_style_t { return yaml_mapping_style_t(e.style) } + +// Nodes + +const ( + yaml_NULL_TAG = "tag:yaml.org,2002:null" // The tag !!null with the only possible value: null. + yaml_BOOL_TAG = "tag:yaml.org,2002:bool" // The tag !!bool with the values: true and false. + yaml_STR_TAG = "tag:yaml.org,2002:str" // The tag !!str for string values. + yaml_INT_TAG = "tag:yaml.org,2002:int" // The tag !!int for integer values. + yaml_FLOAT_TAG = "tag:yaml.org,2002:float" // The tag !!float for float values. + yaml_TIMESTAMP_TAG = "tag:yaml.org,2002:timestamp" // The tag !!timestamp for date and time values. + + yaml_SEQ_TAG = "tag:yaml.org,2002:seq" // The tag !!seq is used to denote sequences. + yaml_MAP_TAG = "tag:yaml.org,2002:map" // The tag !!map is used to denote mapping. + + // Not in original libyaml. + yaml_BINARY_TAG = "tag:yaml.org,2002:binary" + yaml_MERGE_TAG = "tag:yaml.org,2002:merge" + + yaml_DEFAULT_SCALAR_TAG = yaml_STR_TAG // The default scalar tag is !!str. + yaml_DEFAULT_SEQUENCE_TAG = yaml_SEQ_TAG // The default sequence tag is !!seq. + yaml_DEFAULT_MAPPING_TAG = yaml_MAP_TAG // The default mapping tag is !!map. +) + +type yaml_node_type_t int + +// Node types. +const ( + // An empty node. + yaml_NO_NODE yaml_node_type_t = iota + + yaml_SCALAR_NODE // A scalar node. + yaml_SEQUENCE_NODE // A sequence node. + yaml_MAPPING_NODE // A mapping node. +) + +// An element of a sequence node. +type yaml_node_item_t int + +// An element of a mapping node. +type yaml_node_pair_t struct { + key int // The key of the element. + value int // The value of the element. +} + +// The node structure. +type yaml_node_t struct { + typ yaml_node_type_t // The node type. + tag []byte // The node tag. + + // The node data. + + // The scalar parameters (for yaml_SCALAR_NODE). + scalar struct { + value []byte // The scalar value. + length int // The length of the scalar value. + style yaml_scalar_style_t // The scalar style. + } + + // The sequence parameters (for YAML_SEQUENCE_NODE). + sequence struct { + items_data []yaml_node_item_t // The stack of sequence items. + style yaml_sequence_style_t // The sequence style. + } + + // The mapping parameters (for yaml_MAPPING_NODE). + mapping struct { + pairs_data []yaml_node_pair_t // The stack of mapping pairs (key, value). + pairs_start *yaml_node_pair_t // The beginning of the stack. + pairs_end *yaml_node_pair_t // The end of the stack. + pairs_top *yaml_node_pair_t // The top of the stack. + style yaml_mapping_style_t // The mapping style. + } + + start_mark yaml_mark_t // The beginning of the node. + end_mark yaml_mark_t // The end of the node. + +} + +// The document structure. +type yaml_document_t struct { + + // The document nodes. + nodes []yaml_node_t + + // The version directive. + version_directive *yaml_version_directive_t + + // The list of tag directives. + tag_directives_data []yaml_tag_directive_t + tag_directives_start int // The beginning of the tag directives list. + tag_directives_end int // The end of the tag directives list. + + start_implicit int // Is the document start indicator implicit? + end_implicit int // Is the document end indicator implicit? + + // The start/end of the document. + start_mark, end_mark yaml_mark_t +} + +// The prototype of a read handler. +// +// The read handler is called when the parser needs to read more bytes from the +// source. The handler should write not more than size bytes to the buffer. +// The number of written bytes should be set to the size_read variable. +// +// [in,out] data A pointer to an application data specified by +// yaml_parser_set_input(). +// [out] buffer The buffer to write the data from the source. +// [in] size The size of the buffer. +// [out] size_read The actual number of bytes read from the source. +// +// On success, the handler should return 1. If the handler failed, +// the returned value should be 0. On EOF, the handler should set the +// size_read to 0 and return 1. +type yaml_read_handler_t func(parser *yaml_parser_t, buffer []byte) (n int, err error) + +// This structure holds information about a potential simple key. +type yaml_simple_key_t struct { + possible bool // Is a simple key possible? + required bool // Is a simple key required? + token_number int // The number of the token. + mark yaml_mark_t // The position mark. +} + +// The states of the parser. +type yaml_parser_state_t int + +const ( + yaml_PARSE_STREAM_START_STATE yaml_parser_state_t = iota + + yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE // Expect the beginning of an implicit document. + yaml_PARSE_DOCUMENT_START_STATE // Expect DOCUMENT-START. + yaml_PARSE_DOCUMENT_CONTENT_STATE // Expect the content of a document. + yaml_PARSE_DOCUMENT_END_STATE // Expect DOCUMENT-END. + yaml_PARSE_BLOCK_NODE_STATE // Expect a block node. + yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE // Expect a block node or indentless sequence. + yaml_PARSE_FLOW_NODE_STATE // Expect a flow node. + yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a block sequence. + yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE // Expect an entry of a block sequence. + yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE // Expect an entry of an indentless sequence. + yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping. + yaml_PARSE_BLOCK_MAPPING_KEY_STATE // Expect a block mapping key. + yaml_PARSE_BLOCK_MAPPING_VALUE_STATE // Expect a block mapping value. + yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a flow sequence. + yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE // Expect an entry of a flow sequence. + yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE // Expect a key of an ordered mapping. + yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE // Expect a value of an ordered mapping. + yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE // Expect the and of an ordered mapping entry. + yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping. + yaml_PARSE_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping. + yaml_PARSE_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping. + yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE // Expect an empty value of a flow mapping. + yaml_PARSE_END_STATE // Expect nothing. +) + +func (ps yaml_parser_state_t) String() string { + switch ps { + case yaml_PARSE_STREAM_START_STATE: + return "yaml_PARSE_STREAM_START_STATE" + case yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE: + return "yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE" + case yaml_PARSE_DOCUMENT_START_STATE: + return "yaml_PARSE_DOCUMENT_START_STATE" + case yaml_PARSE_DOCUMENT_CONTENT_STATE: + return "yaml_PARSE_DOCUMENT_CONTENT_STATE" + case yaml_PARSE_DOCUMENT_END_STATE: + return "yaml_PARSE_DOCUMENT_END_STATE" + case yaml_PARSE_BLOCK_NODE_STATE: + return "yaml_PARSE_BLOCK_NODE_STATE" + case yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE: + return "yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE" + case yaml_PARSE_FLOW_NODE_STATE: + return "yaml_PARSE_FLOW_NODE_STATE" + case yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE: + return "yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE" + case yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE: + return "yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE" + case yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE: + return "yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE" + case yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE: + return "yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE" + case yaml_PARSE_BLOCK_MAPPING_KEY_STATE: + return "yaml_PARSE_BLOCK_MAPPING_KEY_STATE" + case yaml_PARSE_BLOCK_MAPPING_VALUE_STATE: + return "yaml_PARSE_BLOCK_MAPPING_VALUE_STATE" + case yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE: + return "yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE" + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE: + return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE" + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE: + return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE" + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE: + return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE" + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE: + return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE" + case yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE: + return "yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE" + case yaml_PARSE_FLOW_MAPPING_KEY_STATE: + return "yaml_PARSE_FLOW_MAPPING_KEY_STATE" + case yaml_PARSE_FLOW_MAPPING_VALUE_STATE: + return "yaml_PARSE_FLOW_MAPPING_VALUE_STATE" + case yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE: + return "yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE" + case yaml_PARSE_END_STATE: + return "yaml_PARSE_END_STATE" + } + return "" +} + +// This structure holds aliases data. +type yaml_alias_data_t struct { + anchor []byte // The anchor. + index int // The node id. + mark yaml_mark_t // The anchor mark. +} + +// The parser structure. +// +// All members are internal. Manage the structure using the +// yaml_parser_ family of functions. +type yaml_parser_t struct { + + // Error handling + + error yaml_error_type_t // Error type. + + problem string // Error description. + + // The byte about which the problem occurred. + problem_offset int + problem_value int + problem_mark yaml_mark_t + + // The error context. + context string + context_mark yaml_mark_t + + // Reader stuff + + read_handler yaml_read_handler_t // Read handler. + + input_reader io.Reader // File input data. + input []byte // String input data. + input_pos int + + eof bool // EOF flag + + buffer []byte // The working buffer. + buffer_pos int // The current position of the buffer. + + unread int // The number of unread characters in the buffer. + + newlines int // The number of line breaks since last non-break/non-blank character + + raw_buffer []byte // The raw buffer. + raw_buffer_pos int // The current position of the buffer. + + encoding yaml_encoding_t // The input encoding. + + offset int // The offset of the current position (in bytes). + mark yaml_mark_t // The mark of the current position. + + // Comments + + head_comment []byte // The current head comments + line_comment []byte // The current line comments + foot_comment []byte // The current foot comments + tail_comment []byte // Foot comment that happens at the end of a block. + + comments []yaml_comment_t // The folded comments for all parsed tokens + comments_head int + + // Scanner stuff + + stream_start_produced bool // Have we started to scan the input stream? + stream_end_produced bool // Have we reached the end of the input stream? + + flow_level int // The number of unclosed '[' and '{' indicators. + + tokens []yaml_token_t // The tokens queue. + tokens_head int // The head of the tokens queue. + tokens_parsed int // The number of tokens fetched from the queue. + token_available bool // Does the tokens queue contain a token ready for dequeueing. + + indent int // The current indentation level. + indents []int // The indentation levels stack. + + simple_key_allowed bool // May a simple key occur at the current position? + simple_keys []yaml_simple_key_t // The stack of simple keys. + + // Parser stuff + + state yaml_parser_state_t // The current parser state. + states []yaml_parser_state_t // The parser states stack. + marks []yaml_mark_t // The stack of marks. + tag_directives []yaml_tag_directive_t // The list of TAG directives. + + // Dumper stuff + + aliases []yaml_alias_data_t // The alias data. + + document *yaml_document_t // The currently parsed document. +} + +type yaml_comment_t struct { + + scan_mark yaml_mark_t // Position where scanning for comments started + token_mark yaml_mark_t // Position after which tokens will be associated with this comment + start_mark yaml_mark_t // Position of '#' comment mark + end_mark yaml_mark_t // Position where comment terminated + + head []byte + line []byte + foot []byte +} + +// Emitter Definitions + +// The prototype of a write handler. +// +// The write handler is called when the emitter needs to flush the accumulated +// characters to the output. The handler should write @a size bytes of the +// @a buffer to the output. +// +// @param[in,out] data A pointer to an application data specified by +// yaml_emitter_set_output(). +// @param[in] buffer The buffer with bytes to be written. +// @param[in] size The size of the buffer. +// +// @returns On success, the handler should return @c 1. If the handler failed, +// the returned value should be @c 0. +// +type yaml_write_handler_t func(emitter *yaml_emitter_t, buffer []byte) error + +type yaml_emitter_state_t int + +// The emitter states. +const ( + // Expect STREAM-START. + yaml_EMIT_STREAM_START_STATE yaml_emitter_state_t = iota + + yaml_EMIT_FIRST_DOCUMENT_START_STATE // Expect the first DOCUMENT-START or STREAM-END. + yaml_EMIT_DOCUMENT_START_STATE // Expect DOCUMENT-START or STREAM-END. + yaml_EMIT_DOCUMENT_CONTENT_STATE // Expect the content of a document. + yaml_EMIT_DOCUMENT_END_STATE // Expect DOCUMENT-END. + yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a flow sequence. + yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE // Expect the next item of a flow sequence, with the comma already written out + yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE // Expect an item of a flow sequence. + yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping. + yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE // Expect the next key of a flow mapping, with the comma already written out + yaml_EMIT_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping. + yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a flow mapping. + yaml_EMIT_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping. + yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a block sequence. + yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE // Expect an item of a block sequence. + yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping. + yaml_EMIT_BLOCK_MAPPING_KEY_STATE // Expect the key of a block mapping. + yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a block mapping. + yaml_EMIT_BLOCK_MAPPING_VALUE_STATE // Expect a value of a block mapping. + yaml_EMIT_END_STATE // Expect nothing. +) + +// The emitter structure. +// +// All members are internal. Manage the structure using the @c yaml_emitter_ +// family of functions. +type yaml_emitter_t struct { + + // Error handling + + error yaml_error_type_t // Error type. + problem string // Error description. + + // Writer stuff + + write_handler yaml_write_handler_t // Write handler. + + output_buffer *[]byte // String output data. + output_writer io.Writer // File output data. + + buffer []byte // The working buffer. + buffer_pos int // The current position of the buffer. + + raw_buffer []byte // The raw buffer. + raw_buffer_pos int // The current position of the buffer. + + encoding yaml_encoding_t // The stream encoding. + + // Emitter stuff + + canonical bool // If the output is in the canonical style? + best_indent int // The number of indentation spaces. + best_width int // The preferred width of the output lines. + unicode bool // Allow unescaped non-ASCII characters? + line_break yaml_break_t // The preferred line break. + + state yaml_emitter_state_t // The current emitter state. + states []yaml_emitter_state_t // The stack of states. + + events []yaml_event_t // The event queue. + events_head int // The head of the event queue. + + indents []int // The stack of indentation levels. + + tag_directives []yaml_tag_directive_t // The list of tag directives. + + indent int // The current indentation level. + + flow_level int // The current flow level. + + root_context bool // Is it the document root context? + sequence_context bool // Is it a sequence context? + mapping_context bool // Is it a mapping context? + simple_key_context bool // Is it a simple mapping key context? + + line int // The current line. + column int // The current column. + whitespace bool // If the last character was a whitespace? + indention bool // If the last character was an indentation character (' ', '-', '?', ':')? + open_ended bool // If an explicit document end is required? + + space_above bool // Is there's an empty line above? + foot_indent int // The indent used to write the foot comment above, or -1 if none. + + // Anchor analysis. + anchor_data struct { + anchor []byte // The anchor value. + alias bool // Is it an alias? + } + + // Tag analysis. + tag_data struct { + handle []byte // The tag handle. + suffix []byte // The tag suffix. + } + + // Scalar analysis. + scalar_data struct { + value []byte // The scalar value. + multiline bool // Does the scalar contain line breaks? + flow_plain_allowed bool // Can the scalar be expessed in the flow plain style? + block_plain_allowed bool // Can the scalar be expressed in the block plain style? + single_quoted_allowed bool // Can the scalar be expressed in the single quoted style? + block_allowed bool // Can the scalar be expressed in the literal or folded styles? + style yaml_scalar_style_t // The output style. + } + + // Comments + head_comment []byte + line_comment []byte + foot_comment []byte + tail_comment []byte + + // Dumper stuff + + opened bool // If the stream was already opened? + closed bool // If the stream was already closed? + + // The information associated with the document nodes. + anchors *struct { + references int // The number of references. + anchor int // The anchor id. + serialized bool // If the node has been emitted? + } + + last_anchor_id int // The last assigned anchor id. + + document *yaml_document_t // The currently emitted document. +} diff --git a/vendor/gopkg.in/yaml.v3/yamlprivateh.go b/vendor/gopkg.in/yaml.v3/yamlprivateh.go new file mode 100644 index 0000000000..e88f9c54ae --- /dev/null +++ b/vendor/gopkg.in/yaml.v3/yamlprivateh.go @@ -0,0 +1,198 @@ +// +// Copyright (c) 2011-2019 Canonical Ltd +// Copyright (c) 2006-2010 Kirill Simonov +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package yaml + +const ( + // The size of the input raw buffer. + input_raw_buffer_size = 512 + + // The size of the input buffer. + // It should be possible to decode the whole raw buffer. + input_buffer_size = input_raw_buffer_size * 3 + + // The size of the output buffer. + output_buffer_size = 128 + + // The size of the output raw buffer. + // It should be possible to encode the whole output buffer. + output_raw_buffer_size = (output_buffer_size*2 + 2) + + // The size of other stacks and queues. + initial_stack_size = 16 + initial_queue_size = 16 + initial_string_size = 16 +) + +// Check if the character at the specified position is an alphabetical +// character, a digit, '_', or '-'. +func is_alpha(b []byte, i int) bool { + return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'Z' || b[i] >= 'a' && b[i] <= 'z' || b[i] == '_' || b[i] == '-' +} + +// Check if the character at the specified position is a digit. +func is_digit(b []byte, i int) bool { + return b[i] >= '0' && b[i] <= '9' +} + +// Get the value of a digit. +func as_digit(b []byte, i int) int { + return int(b[i]) - '0' +} + +// Check if the character at the specified position is a hex-digit. +func is_hex(b []byte, i int) bool { + return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'F' || b[i] >= 'a' && b[i] <= 'f' +} + +// Get the value of a hex-digit. +func as_hex(b []byte, i int) int { + bi := b[i] + if bi >= 'A' && bi <= 'F' { + return int(bi) - 'A' + 10 + } + if bi >= 'a' && bi <= 'f' { + return int(bi) - 'a' + 10 + } + return int(bi) - '0' +} + +// Check if the character is ASCII. +func is_ascii(b []byte, i int) bool { + return b[i] <= 0x7F +} + +// Check if the character at the start of the buffer can be printed unescaped. +func is_printable(b []byte, i int) bool { + return ((b[i] == 0x0A) || // . == #x0A + (b[i] >= 0x20 && b[i] <= 0x7E) || // #x20 <= . <= #x7E + (b[i] == 0xC2 && b[i+1] >= 0xA0) || // #0xA0 <= . <= #xD7FF + (b[i] > 0xC2 && b[i] < 0xED) || + (b[i] == 0xED && b[i+1] < 0xA0) || + (b[i] == 0xEE) || + (b[i] == 0xEF && // #xE000 <= . <= #xFFFD + !(b[i+1] == 0xBB && b[i+2] == 0xBF) && // && . != #xFEFF + !(b[i+1] == 0xBF && (b[i+2] == 0xBE || b[i+2] == 0xBF)))) +} + +// Check if the character at the specified position is NUL. +func is_z(b []byte, i int) bool { + return b[i] == 0x00 +} + +// Check if the beginning of the buffer is a BOM. +func is_bom(b []byte, i int) bool { + return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF +} + +// Check if the character at the specified position is space. +func is_space(b []byte, i int) bool { + return b[i] == ' ' +} + +// Check if the character at the specified position is tab. +func is_tab(b []byte, i int) bool { + return b[i] == '\t' +} + +// Check if the character at the specified position is blank (space or tab). +func is_blank(b []byte, i int) bool { + //return is_space(b, i) || is_tab(b, i) + return b[i] == ' ' || b[i] == '\t' +} + +// Check if the character at the specified position is a line break. +func is_break(b []byte, i int) bool { + return (b[i] == '\r' || // CR (#xD) + b[i] == '\n' || // LF (#xA) + b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9) // PS (#x2029) +} + +func is_crlf(b []byte, i int) bool { + return b[i] == '\r' && b[i+1] == '\n' +} + +// Check if the character is a line break or NUL. +func is_breakz(b []byte, i int) bool { + //return is_break(b, i) || is_z(b, i) + return ( + // is_break: + b[i] == '\r' || // CR (#xD) + b[i] == '\n' || // LF (#xA) + b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) + // is_z: + b[i] == 0) +} + +// Check if the character is a line break, space, or NUL. +func is_spacez(b []byte, i int) bool { + //return is_space(b, i) || is_breakz(b, i) + return ( + // is_space: + b[i] == ' ' || + // is_breakz: + b[i] == '\r' || // CR (#xD) + b[i] == '\n' || // LF (#xA) + b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) + b[i] == 0) +} + +// Check if the character is a line break, space, tab, or NUL. +func is_blankz(b []byte, i int) bool { + //return is_blank(b, i) || is_breakz(b, i) + return ( + // is_blank: + b[i] == ' ' || b[i] == '\t' || + // is_breakz: + b[i] == '\r' || // CR (#xD) + b[i] == '\n' || // LF (#xA) + b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) + b[i] == 0) +} + +// Determine the width of the character. +func width(b byte) int { + // Don't replace these by a switch without first + // confirming that it is being inlined. + if b&0x80 == 0x00 { + return 1 + } + if b&0xE0 == 0xC0 { + return 2 + } + if b&0xF0 == 0xE0 { + return 3 + } + if b&0xF8 == 0xF0 { + return 4 + } + return 0 + +} diff --git a/vendor/modules.txt b/vendor/modules.txt index d8b0255d06..4e2771cce8 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -11,21 +11,21 @@ github.com/beorn7/perks/quantile # github.com/blang/semver v3.5.1+incompatible github.com/blang/semver # github.com/coreos/etcd v3.3.15+incompatible -github.com/coreos/etcd/auth/authpb github.com/coreos/etcd/clientv3 +github.com/coreos/etcd/pkg/transport +github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes +github.com/coreos/etcd/mvcc/mvccpb +github.com/coreos/etcd/auth/authpb github.com/coreos/etcd/clientv3/balancer -github.com/coreos/etcd/clientv3/balancer/connectivity github.com/coreos/etcd/clientv3/balancer/picker github.com/coreos/etcd/clientv3/balancer/resolver/endpoint github.com/coreos/etcd/clientv3/credentials -github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes github.com/coreos/etcd/etcdserver/etcdserverpb -github.com/coreos/etcd/mvcc/mvccpb github.com/coreos/etcd/pkg/logutil -github.com/coreos/etcd/pkg/systemd -github.com/coreos/etcd/pkg/tlsutil -github.com/coreos/etcd/pkg/transport github.com/coreos/etcd/pkg/types +github.com/coreos/etcd/pkg/tlsutil +github.com/coreos/etcd/clientv3/balancer/connectivity +github.com/coreos/etcd/pkg/systemd github.com/coreos/etcd/raft github.com/coreos/etcd/raft/raftpb # github.com/coreos/go-semver v0.3.0 @@ -42,6 +42,8 @@ github.com/emicklei/go-restful github.com/emicklei/go-restful/log # github.com/evanphx/json-patch v4.5.0+incompatible github.com/evanphx/json-patch +# github.com/fatih/color v1.7.0 +github.com/fatih/color # github.com/fsnotify/fsnotify v1.4.7 github.com/fsnotify/fsnotify # github.com/ghodss/yaml v1.0.0 @@ -70,27 +72,29 @@ github.com/go-openapi/strfmt github.com/go-openapi/swag # github.com/go-openapi/validate v0.19.2 github.com/go-openapi/validate +# github.com/gobuffalo/flect v0.1.5 +github.com/gobuffalo/flect # github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d -github.com/gogo/protobuf/gogoproto github.com/gogo/protobuf/proto -github.com/gogo/protobuf/protoc-gen-gogo/descriptor github.com/gogo/protobuf/sortkeys +github.com/gogo/protobuf/gogoproto +github.com/gogo/protobuf/protoc-gen-gogo/descriptor # github.com/golang-migrate/migrate/v4 v4.6.2 -github.com/golang-migrate/migrate/v4/source github.com/golang-migrate/migrate/v4/source/file +github.com/golang-migrate/migrate/v4/source # github.com/golang/groupcache v0.0.0-20181024230925-c65c006176ff github.com/golang/groupcache/lru # github.com/golang/mock v1.3.1 -github.com/golang/mock/gomock github.com/golang/mock/mockgen +github.com/golang/mock/gomock github.com/golang/mock/mockgen/model # github.com/golang/protobuf v1.3.2 github.com/golang/protobuf/proto -github.com/golang/protobuf/protoc-gen-go/descriptor -github.com/golang/protobuf/ptypes github.com/golang/protobuf/ptypes/any +github.com/golang/protobuf/ptypes github.com/golang/protobuf/ptypes/duration github.com/golang/protobuf/ptypes/timestamp +github.com/golang/protobuf/protoc-gen-go/descriptor # github.com/google/go-cmp v0.3.0 github.com/google/go-cmp/cmp github.com/google/go-cmp/cmp/internal/diff @@ -121,9 +125,13 @@ github.com/konsorten/go-windows-terminal-sequences # github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de github.com/liggitt/tabwriter # github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63 -github.com/mailru/easyjson/buffer github.com/mailru/easyjson/jlexer github.com/mailru/easyjson/jwriter +github.com/mailru/easyjson/buffer +# github.com/mattn/go-colorable v0.1.2 +github.com/mattn/go-colorable +# github.com/mattn/go-isatty v0.0.8 +github.com/mattn/go-isatty # github.com/mattn/go-sqlite3 v1.10.0 github.com/mattn/go-sqlite3 # github.com/matttproud/golang_protobuf_extensions v1.0.1 @@ -133,6 +141,10 @@ github.com/maxbrunsfeld/counterfeiter/v6 github.com/maxbrunsfeld/counterfeiter/v6/arguments github.com/maxbrunsfeld/counterfeiter/v6/command github.com/maxbrunsfeld/counterfeiter/v6/generator +# github.com/mikefarah/yaml/v2 v2.4.0 +github.com/mikefarah/yaml/v2 +# github.com/mikefarah/yq/v2 v2.4.1 +github.com/mikefarah/yq/v2 # github.com/mitchellh/hashstructure v1.0.0 github.com/mitchellh/hashstructure # github.com/mitchellh/mapstructure v1.1.2 @@ -146,21 +158,21 @@ github.com/munnerz/goautoneg # github.com/openshift/api v3.9.1-0.20190924102528-32369d4db2ad+incompatible => github.com/openshift/api v3.9.1-0.20190924102528-32369d4db2ad+incompatible github.com/openshift/api/config/v1 # github.com/openshift/client-go v0.0.0-20190923180330-3b6373338c9b => github.com/openshift/client-go v0.0.0-20190923180330-3b6373338c9b -github.com/openshift/client-go/config/clientset/versioned -github.com/openshift/client-go/config/clientset/versioned/fake -github.com/openshift/client-go/config/clientset/versioned/scheme github.com/openshift/client-go/config/clientset/versioned/typed/config/v1 -github.com/openshift/client-go/config/clientset/versioned/typed/config/v1/fake +github.com/openshift/client-go/config/clientset/versioned github.com/openshift/client-go/config/informers/externalversions -github.com/openshift/client-go/config/informers/externalversions/config github.com/openshift/client-go/config/informers/externalversions/config/v1 -github.com/openshift/client-go/config/informers/externalversions/internalinterfaces github.com/openshift/client-go/config/listers/config/v1 +github.com/openshift/client-go/config/clientset/versioned/scheme +github.com/openshift/client-go/config/clientset/versioned/fake +github.com/openshift/client-go/config/informers/externalversions/config +github.com/openshift/client-go/config/informers/externalversions/internalinterfaces +github.com/openshift/client-go/config/clientset/versioned/typed/config/v1/fake # github.com/operator-framework/operator-registry v1.5.1 +github.com/operator-framework/operator-registry/pkg/registry +github.com/operator-framework/operator-registry/pkg/client github.com/operator-framework/operator-registry/pkg/api github.com/operator-framework/operator-registry/pkg/api/grpc_health_v1 -github.com/operator-framework/operator-registry/pkg/client -github.com/operator-framework/operator-registry/pkg/registry github.com/operator-framework/operator-registry/pkg/server github.com/operator-framework/operator-registry/pkg/sqlite github.com/operator-framework/operator-registry/pkg/sqlite/migrations @@ -171,20 +183,20 @@ github.com/pkg/errors # github.com/pmezard/go-difflib v1.0.0 github.com/pmezard/go-difflib/difflib # github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829 +github.com/prometheus/client_golang/prometheus/promhttp github.com/prometheus/client_golang/prometheus github.com/prometheus/client_golang/prometheus/internal -github.com/prometheus/client_golang/prometheus/promhttp # github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f github.com/prometheus/client_model/go # github.com/prometheus/common v0.2.0 github.com/prometheus/common/expfmt -github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg github.com/prometheus/common/model +github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg # github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1 github.com/prometheus/procfs -github.com/prometheus/procfs/internal/util github.com/prometheus/procfs/nfs github.com/prometheus/procfs/xfs +github.com/prometheus/procfs/internal/util # github.com/sirupsen/logrus v1.4.2 github.com/sirupsen/logrus # github.com/spf13/cobra v0.0.5 @@ -192,116 +204,113 @@ github.com/spf13/cobra # github.com/spf13/pflag v1.0.3 github.com/spf13/pflag # github.com/stretchr/testify v1.4.0 -github.com/stretchr/testify/assert github.com/stretchr/testify/require +github.com/stretchr/testify/assert # go.uber.org/atomic v1.4.0 go.uber.org/atomic # go.uber.org/multierr v1.1.0 go.uber.org/multierr # go.uber.org/zap v1.10.0 go.uber.org/zap -go.uber.org/zap/buffer +go.uber.org/zap/zapcore go.uber.org/zap/internal/bufferpool +go.uber.org/zap/buffer go.uber.org/zap/internal/color go.uber.org/zap/internal/exit -go.uber.org/zap/zapcore # golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 +golang.org/x/crypto/ssh/terminal golang.org/x/crypto/cryptobyte +golang.org/x/crypto/nacl/secretbox golang.org/x/crypto/cryptobyte/asn1 golang.org/x/crypto/internal/subtle -golang.org/x/crypto/nacl/secretbox golang.org/x/crypto/poly1305 golang.org/x/crypto/salsa20/salsa -golang.org/x/crypto/ssh/terminal # golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc +golang.org/x/net/http2 +golang.org/x/net/trace golang.org/x/net/context -golang.org/x/net/context/ctxhttp golang.org/x/net/http/httpguts -golang.org/x/net/http2 golang.org/x/net/http2/hpack golang.org/x/net/idna golang.org/x/net/internal/timeseries -golang.org/x/net/trace +golang.org/x/net/context/ctxhttp golang.org/x/net/websocket # golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 golang.org/x/oauth2 golang.org/x/oauth2/internal -# golang.org/x/sync v0.0.0-20190423024810-112230192c58 -golang.org/x/sync/errgroup # golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb -golang.org/x/sys/cpu golang.org/x/sys/unix golang.org/x/sys/windows +golang.org/x/sys/cpu # golang.org/x/text v0.3.2 golang.org/x/text/secure/bidirule -golang.org/x/text/transform golang.org/x/text/unicode/bidi golang.org/x/text/unicode/norm golang.org/x/text/width +golang.org/x/text/transform # golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 golang.org/x/time/rate -# golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db -golang.org/x/tools/go/ast/astutil +# golang.org/x/tools v0.0.0-20191030203535-5e247c9ad0a0 +golang.org/x/tools/go/packages golang.org/x/tools/go/gcexportdata -golang.org/x/tools/go/internal/gcimporter golang.org/x/tools/go/internal/packagesdriver -golang.org/x/tools/go/packages +golang.org/x/tools/internal/gopathwalk +golang.org/x/tools/internal/semver +golang.org/x/tools/internal/span golang.org/x/tools/go/types/typeutil golang.org/x/tools/imports +golang.org/x/tools/go/internal/gcimporter golang.org/x/tools/internal/fastwalk -golang.org/x/tools/internal/gopathwalk +golang.org/x/tools/go/ast/astutil golang.org/x/tools/internal/imports golang.org/x/tools/internal/module -golang.org/x/tools/internal/semver # gonum.org/v1/gonum v0.0.0-20190710053202-4340aa3071a0 -gonum.org/v1/gonum/blas -gonum.org/v1/gonum/blas/blas64 -gonum.org/v1/gonum/blas/cblas128 -gonum.org/v1/gonum/blas/gonum -gonum.org/v1/gonum/floats gonum.org/v1/gonum/graph -gonum.org/v1/gonum/graph/internal/linear +gonum.org/v1/gonum/graph/simple +gonum.org/v1/gonum/graph/topo gonum.org/v1/gonum/graph/internal/ordered -gonum.org/v1/gonum/graph/internal/set gonum.org/v1/gonum/graph/internal/uid gonum.org/v1/gonum/graph/iterator -gonum.org/v1/gonum/graph/simple -gonum.org/v1/gonum/graph/topo +gonum.org/v1/gonum/mat +gonum.org/v1/gonum/graph/internal/linear +gonum.org/v1/gonum/graph/internal/set gonum.org/v1/gonum/graph/traverse +gonum.org/v1/gonum/blas +gonum.org/v1/gonum/blas/blas64 +gonum.org/v1/gonum/blas/cblas128 +gonum.org/v1/gonum/floats +gonum.org/v1/gonum/internal/asm/f64 +gonum.org/v1/gonum/lapack +gonum.org/v1/gonum/lapack/lapack64 +gonum.org/v1/gonum/blas/gonum +gonum.org/v1/gonum/lapack/gonum gonum.org/v1/gonum/internal/asm/c128 gonum.org/v1/gonum/internal/asm/c64 gonum.org/v1/gonum/internal/asm/f32 -gonum.org/v1/gonum/internal/asm/f64 gonum.org/v1/gonum/internal/cmplx64 gonum.org/v1/gonum/internal/math32 -gonum.org/v1/gonum/lapack -gonum.org/v1/gonum/lapack/gonum -gonum.org/v1/gonum/lapack/lapack64 -gonum.org/v1/gonum/mat # google.golang.org/appengine v1.5.0 +google.golang.org/appengine/urlfetch google.golang.org/appengine/internal +google.golang.org/appengine/internal/urlfetch google.golang.org/appengine/internal/base google.golang.org/appengine/internal/datastore google.golang.org/appengine/internal/log google.golang.org/appengine/internal/remote_api -google.golang.org/appengine/internal/urlfetch -google.golang.org/appengine/urlfetch # google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 -google.golang.org/genproto/googleapis/api/annotations google.golang.org/genproto/googleapis/rpc/status +google.golang.org/genproto/googleapis/api/annotations # google.golang.org/grpc v1.23.0 +google.golang.org/grpc/connectivity google.golang.org/grpc +google.golang.org/grpc/metadata +google.golang.org/grpc/grpclog google.golang.org/grpc/balancer -google.golang.org/grpc/balancer/base google.golang.org/grpc/balancer/roundrobin -google.golang.org/grpc/binarylog/grpc_binarylog_v1 google.golang.org/grpc/codes -google.golang.org/grpc/connectivity google.golang.org/grpc/credentials -google.golang.org/grpc/credentials/internal google.golang.org/grpc/encoding google.golang.org/grpc/encoding/proto -google.golang.org/grpc/grpclog google.golang.org/grpc/internal google.golang.org/grpc/internal/backoff google.golang.org/grpc/internal/balancerload @@ -310,10 +319,8 @@ google.golang.org/grpc/internal/channelz google.golang.org/grpc/internal/envconfig google.golang.org/grpc/internal/grpcrand google.golang.org/grpc/internal/grpcsync -google.golang.org/grpc/internal/syscall google.golang.org/grpc/internal/transport google.golang.org/grpc/keepalive -google.golang.org/grpc/metadata google.golang.org/grpc/naming google.golang.org/grpc/peer google.golang.org/grpc/resolver @@ -323,25 +330,32 @@ google.golang.org/grpc/serviceconfig google.golang.org/grpc/stats google.golang.org/grpc/status google.golang.org/grpc/tap +google.golang.org/grpc/balancer/base +google.golang.org/grpc/credentials/internal +google.golang.org/grpc/binarylog/grpc_binarylog_v1 +google.golang.org/grpc/internal/syscall +# gopkg.in/imdario/mergo.v0 v0.3.7 +gopkg.in/imdario/mergo.v0 # gopkg.in/inf.v0 v0.9.1 gopkg.in/inf.v0 # gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/natefinch/lumberjack.v2 +# gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 +gopkg.in/op/go-logging.v1 # gopkg.in/yaml.v2 v2.2.2 gopkg.in/yaml.v2 +# gopkg.in/yaml.v3 v3.0.0-20190905181640-827449938966 +gopkg.in/yaml.v3 # k8s.io/api v0.0.0 => k8s.io/api v0.0.0-20190918155943-95b840bb6a1f -k8s.io/api/admission/v1 -k8s.io/api/admission/v1beta1 +k8s.io/api/core/v1 +k8s.io/api/rbac/v1 +k8s.io/api/apps/v1 +k8s.io/api/extensions/v1beta1 k8s.io/api/admissionregistration/v1 k8s.io/api/admissionregistration/v1beta1 -k8s.io/api/apps/v1 k8s.io/api/apps/v1beta1 k8s.io/api/apps/v1beta2 k8s.io/api/auditregistration/v1alpha1 -k8s.io/api/authentication/v1 -k8s.io/api/authentication/v1beta1 -k8s.io/api/authorization/v1 -k8s.io/api/authorization/v1beta1 k8s.io/api/autoscaling/v1 k8s.io/api/autoscaling/v2beta1 k8s.io/api/autoscaling/v2beta2 @@ -351,16 +365,13 @@ k8s.io/api/batch/v2alpha1 k8s.io/api/certificates/v1beta1 k8s.io/api/coordination/v1 k8s.io/api/coordination/v1beta1 -k8s.io/api/core/v1 k8s.io/api/discovery/v1alpha1 k8s.io/api/events/v1beta1 -k8s.io/api/extensions/v1beta1 k8s.io/api/networking/v1 k8s.io/api/networking/v1beta1 k8s.io/api/node/v1alpha1 k8s.io/api/node/v1beta1 k8s.io/api/policy/v1beta1 -k8s.io/api/rbac/v1 k8s.io/api/rbac/v1alpha1 k8s.io/api/rbac/v1beta1 k8s.io/api/scheduling/v1 @@ -370,242 +381,240 @@ k8s.io/api/settings/v1alpha1 k8s.io/api/storage/v1 k8s.io/api/storage/v1alpha1 k8s.io/api/storage/v1beta1 +k8s.io/api/authentication/v1 +k8s.io/api/authentication/v1beta1 +k8s.io/api/authorization/v1 +k8s.io/api/authorization/v1beta1 +k8s.io/api/admission/v1 +k8s.io/api/admission/v1beta1 # k8s.io/apiextensions-apiserver v0.0.0 => k8s.io/apiextensions-apiserver v0.0.0-20190918161926-8f644eb6e783 k8s.io/apiextensions-apiserver/pkg/apis/apiextensions -k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1 k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1 k8s.io/apiextensions-apiserver/pkg/apiserver/validation +k8s.io/apiextensions-apiserver/pkg/client/informers/externalversions +k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset +k8s.io/apiextensions-apiserver/pkg/client/listers/apiextensions/v1beta1 k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/fake -k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme +k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1 +k8s.io/apiextensions-apiserver/pkg/client/informers/externalversions/apiextensions +k8s.io/apiextensions-apiserver/pkg/client/informers/externalversions/internalinterfaces k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1 -k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1/fake k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1 +k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1/fake k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1/fake -k8s.io/apiextensions-apiserver/pkg/client/informers/externalversions -k8s.io/apiextensions-apiserver/pkg/client/informers/externalversions/apiextensions k8s.io/apiextensions-apiserver/pkg/client/informers/externalversions/apiextensions/v1 k8s.io/apiextensions-apiserver/pkg/client/informers/externalversions/apiextensions/v1beta1 -k8s.io/apiextensions-apiserver/pkg/client/informers/externalversions/internalinterfaces k8s.io/apiextensions-apiserver/pkg/client/listers/apiextensions/v1 -k8s.io/apiextensions-apiserver/pkg/client/listers/apiextensions/v1beta1 # k8s.io/apimachinery v0.0.0 => k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655 -k8s.io/apimachinery/pkg/api/equality +k8s.io/apimachinery/pkg/util/clock k8s.io/apimachinery/pkg/api/errors -k8s.io/apimachinery/pkg/api/meta -k8s.io/apimachinery/pkg/api/resource -k8s.io/apimachinery/pkg/api/validation -k8s.io/apimachinery/pkg/api/validation/path -k8s.io/apimachinery/pkg/apis/meta/internalversion k8s.io/apimachinery/pkg/apis/meta/v1 -k8s.io/apimachinery/pkg/apis/meta/v1/unstructured -k8s.io/apimachinery/pkg/apis/meta/v1/validation -k8s.io/apimachinery/pkg/apis/meta/v1beta1 -k8s.io/apimachinery/pkg/apis/meta/v1beta1/validation -k8s.io/apimachinery/pkg/conversion -k8s.io/apimachinery/pkg/conversion/queryparams -k8s.io/apimachinery/pkg/fields -k8s.io/apimachinery/pkg/labels +k8s.io/apimachinery/pkg/types +k8s.io/apimachinery/pkg/util/wait k8s.io/apimachinery/pkg/runtime k8s.io/apimachinery/pkg/runtime/schema +k8s.io/apimachinery/pkg/util/runtime +k8s.io/apimachinery/pkg/conversion k8s.io/apimachinery/pkg/runtime/serializer -k8s.io/apimachinery/pkg/runtime/serializer/json -k8s.io/apimachinery/pkg/runtime/serializer/protobuf -k8s.io/apimachinery/pkg/runtime/serializer/recognizer -k8s.io/apimachinery/pkg/runtime/serializer/streaming -k8s.io/apimachinery/pkg/runtime/serializer/versioning -k8s.io/apimachinery/pkg/selection -k8s.io/apimachinery/pkg/types -k8s.io/apimachinery/pkg/util/cache -k8s.io/apimachinery/pkg/util/clock +k8s.io/apimachinery/pkg/watch +k8s.io/apimachinery/pkg/labels +k8s.io/apimachinery/pkg/api/equality k8s.io/apimachinery/pkg/util/diff -k8s.io/apimachinery/pkg/util/duration k8s.io/apimachinery/pkg/util/errors -k8s.io/apimachinery/pkg/util/framer +k8s.io/apimachinery/pkg/util/validation/field k8s.io/apimachinery/pkg/util/intstr +k8s.io/apimachinery/pkg/api/resource +k8s.io/apimachinery/pkg/apis/meta/v1/unstructured +k8s.io/apimachinery/pkg/runtime/serializer/json +k8s.io/apimachinery/pkg/util/yaml +k8s.io/apimachinery/pkg/api/meta +k8s.io/apimachinery/pkg/util/strategicpatch +k8s.io/apimachinery/pkg/selection +k8s.io/apimachinery/pkg/apis/meta/internalversion +k8s.io/apimachinery/pkg/apis/meta/v1beta1 +k8s.io/apimachinery/pkg/fields +k8s.io/apimachinery/pkg/util/duration +k8s.io/apimachinery/pkg/version +k8s.io/apimachinery/pkg/util/validation +k8s.io/apimachinery/pkg/conversion/queryparams k8s.io/apimachinery/pkg/util/json -k8s.io/apimachinery/pkg/util/mergepatch k8s.io/apimachinery/pkg/util/naming +k8s.io/apimachinery/pkg/util/sets +k8s.io/apimachinery/third_party/forked/golang/reflect k8s.io/apimachinery/pkg/util/net +k8s.io/apimachinery/pkg/runtime/serializer/streaming +k8s.io/apimachinery/pkg/runtime/serializer/protobuf +k8s.io/apimachinery/pkg/runtime/serializer/recognizer +k8s.io/apimachinery/pkg/runtime/serializer/versioning +k8s.io/apimachinery/pkg/util/cache +k8s.io/apimachinery/pkg/api/validation +k8s.io/apimachinery/pkg/util/framer k8s.io/apimachinery/pkg/util/rand -k8s.io/apimachinery/pkg/util/runtime -k8s.io/apimachinery/pkg/util/sets -k8s.io/apimachinery/pkg/util/strategicpatch -k8s.io/apimachinery/pkg/util/uuid -k8s.io/apimachinery/pkg/util/validation -k8s.io/apimachinery/pkg/util/validation/field -k8s.io/apimachinery/pkg/util/wait -k8s.io/apimachinery/pkg/util/waitgroup -k8s.io/apimachinery/pkg/util/yaml -k8s.io/apimachinery/pkg/version -k8s.io/apimachinery/pkg/watch +k8s.io/apimachinery/pkg/util/mergepatch k8s.io/apimachinery/third_party/forked/golang/json -k8s.io/apimachinery/third_party/forked/golang/reflect +k8s.io/apimachinery/pkg/util/waitgroup +k8s.io/apimachinery/pkg/api/validation/path +k8s.io/apimachinery/pkg/apis/meta/v1/validation +k8s.io/apimachinery/pkg/util/uuid +k8s.io/apimachinery/pkg/apis/meta/v1beta1/validation # k8s.io/apiserver v0.0.0 => k8s.io/apiserver v0.0.0-20190918160949-bfa5e2e684ad +k8s.io/apiserver/pkg/authentication/serviceaccount +k8s.io/apiserver/pkg/authentication/user +k8s.io/apiserver/pkg/authorization/authorizer +k8s.io/apiserver/pkg/storage/names +k8s.io/apiserver/pkg/endpoints/openapi +k8s.io/apiserver/pkg/server +k8s.io/apiserver/pkg/registry/rest +k8s.io/apiserver/pkg/server/options +k8s.io/apiserver/pkg/endpoints/request k8s.io/apiserver/pkg/admission -k8s.io/apiserver/pkg/admission/configuration -k8s.io/apiserver/pkg/admission/initializer -k8s.io/apiserver/pkg/admission/metrics k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle -k8s.io/apiserver/pkg/admission/plugin/webhook -k8s.io/apiserver/pkg/admission/plugin/webhook/config -k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission -k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1alpha1 -k8s.io/apiserver/pkg/admission/plugin/webhook/errors -k8s.io/apiserver/pkg/admission/plugin/webhook/generic k8s.io/apiserver/pkg/admission/plugin/webhook/mutating -k8s.io/apiserver/pkg/admission/plugin/webhook/namespace -k8s.io/apiserver/pkg/admission/plugin/webhook/object -k8s.io/apiserver/pkg/admission/plugin/webhook/request -k8s.io/apiserver/pkg/admission/plugin/webhook/rules k8s.io/apiserver/pkg/admission/plugin/webhook/validating -k8s.io/apiserver/pkg/apis/apiserver k8s.io/apiserver/pkg/apis/apiserver/install -k8s.io/apiserver/pkg/apis/apiserver/v1alpha1 -k8s.io/apiserver/pkg/apis/audit -k8s.io/apiserver/pkg/apis/audit/install -k8s.io/apiserver/pkg/apis/audit/v1 -k8s.io/apiserver/pkg/apis/audit/v1alpha1 -k8s.io/apiserver/pkg/apis/audit/v1beta1 -k8s.io/apiserver/pkg/apis/audit/validation -k8s.io/apiserver/pkg/apis/config -k8s.io/apiserver/pkg/apis/config/v1 k8s.io/apiserver/pkg/audit -k8s.io/apiserver/pkg/audit/event k8s.io/apiserver/pkg/audit/policy -k8s.io/apiserver/pkg/audit/util k8s.io/apiserver/pkg/authentication/authenticator k8s.io/apiserver/pkg/authentication/authenticatorfactory -k8s.io/apiserver/pkg/authentication/group -k8s.io/apiserver/pkg/authentication/request/anonymous -k8s.io/apiserver/pkg/authentication/request/bearertoken -k8s.io/apiserver/pkg/authentication/request/headerrequest k8s.io/apiserver/pkg/authentication/request/union -k8s.io/apiserver/pkg/authentication/request/websocket -k8s.io/apiserver/pkg/authentication/request/x509 -k8s.io/apiserver/pkg/authentication/serviceaccount -k8s.io/apiserver/pkg/authentication/token/cache -k8s.io/apiserver/pkg/authentication/token/tokenfile -k8s.io/apiserver/pkg/authentication/user -k8s.io/apiserver/pkg/authorization/authorizer k8s.io/apiserver/pkg/authorization/authorizerfactory -k8s.io/apiserver/pkg/authorization/path k8s.io/apiserver/pkg/authorization/union k8s.io/apiserver/pkg/endpoints k8s.io/apiserver/pkg/endpoints/discovery k8s.io/apiserver/pkg/endpoints/filters -k8s.io/apiserver/pkg/endpoints/handlers -k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager -k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal -k8s.io/apiserver/pkg/endpoints/handlers/negotiation k8s.io/apiserver/pkg/endpoints/handlers/responsewriters -k8s.io/apiserver/pkg/endpoints/metrics -k8s.io/apiserver/pkg/endpoints/openapi -k8s.io/apiserver/pkg/endpoints/request -k8s.io/apiserver/pkg/features k8s.io/apiserver/pkg/registry/generic -k8s.io/apiserver/pkg/registry/generic/registry -k8s.io/apiserver/pkg/registry/rest -k8s.io/apiserver/pkg/server k8s.io/apiserver/pkg/server/egressselector k8s.io/apiserver/pkg/server/filters k8s.io/apiserver/pkg/server/healthz -k8s.io/apiserver/pkg/server/httplog k8s.io/apiserver/pkg/server/mux -k8s.io/apiserver/pkg/server/options -k8s.io/apiserver/pkg/server/options/encryptionconfig -k8s.io/apiserver/pkg/server/resourceconfig k8s.io/apiserver/pkg/server/routes k8s.io/apiserver/pkg/server/storage -k8s.io/apiserver/pkg/storage -k8s.io/apiserver/pkg/storage/cacher -k8s.io/apiserver/pkg/storage/errors -k8s.io/apiserver/pkg/storage/etcd3 -k8s.io/apiserver/pkg/storage/etcd3/metrics -k8s.io/apiserver/pkg/storage/names +k8s.io/apiserver/pkg/util/openapi +k8s.io/apiserver/pkg/features +k8s.io/apiserver/pkg/util/feature +k8s.io/apiserver/pkg/admission/initializer +k8s.io/apiserver/pkg/admission/metrics +k8s.io/apiserver/pkg/apis/apiserver +k8s.io/apiserver/pkg/apis/apiserver/v1alpha1 +k8s.io/apiserver/pkg/apis/audit +k8s.io/apiserver/pkg/apis/audit/v1 +k8s.io/apiserver/pkg/apis/audit/v1alpha1 +k8s.io/apiserver/pkg/apis/audit/v1beta1 +k8s.io/apiserver/pkg/authorization/path +k8s.io/apiserver/pkg/registry/generic/registry +k8s.io/apiserver/pkg/server/options/encryptionconfig +k8s.io/apiserver/pkg/server/resourceconfig k8s.io/apiserver/pkg/storage/storagebackend k8s.io/apiserver/pkg/storage/storagebackend/factory -k8s.io/apiserver/pkg/storage/value -k8s.io/apiserver/pkg/storage/value/encrypt/aes -k8s.io/apiserver/pkg/storage/value/encrypt/envelope -k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1 -k8s.io/apiserver/pkg/storage/value/encrypt/identity -k8s.io/apiserver/pkg/storage/value/encrypt/secretbox -k8s.io/apiserver/pkg/util/dryrun -k8s.io/apiserver/pkg/util/feature -k8s.io/apiserver/pkg/util/flushwriter -k8s.io/apiserver/pkg/util/openapi k8s.io/apiserver/pkg/util/webhook -k8s.io/apiserver/pkg/util/wsstream k8s.io/apiserver/plugin/pkg/audit/buffered k8s.io/apiserver/plugin/pkg/audit/dynamic k8s.io/apiserver/plugin/pkg/audit/dynamic/enforced k8s.io/apiserver/plugin/pkg/audit/log k8s.io/apiserver/plugin/pkg/audit/truncate k8s.io/apiserver/plugin/pkg/audit/webhook +k8s.io/apiserver/pkg/admission/configuration +k8s.io/apiserver/pkg/admission/plugin/webhook +k8s.io/apiserver/pkg/admission/plugin/webhook/errors +k8s.io/apiserver/pkg/admission/plugin/webhook/generic +k8s.io/apiserver/pkg/admission/plugin/webhook/request +k8s.io/apiserver/pkg/apis/audit/validation +k8s.io/apiserver/pkg/authentication/group +k8s.io/apiserver/pkg/authentication/request/anonymous +k8s.io/apiserver/pkg/authentication/request/bearertoken +k8s.io/apiserver/pkg/authentication/request/headerrequest +k8s.io/apiserver/pkg/authentication/request/websocket +k8s.io/apiserver/pkg/authentication/request/x509 +k8s.io/apiserver/pkg/authentication/token/cache +k8s.io/apiserver/pkg/authentication/token/tokenfile k8s.io/apiserver/plugin/pkg/authenticator/token/webhook k8s.io/apiserver/plugin/pkg/authorizer/webhook +k8s.io/apiserver/pkg/endpoints/handlers +k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager +k8s.io/apiserver/pkg/endpoints/handlers/negotiation +k8s.io/apiserver/pkg/endpoints/metrics +k8s.io/apiserver/pkg/server/httplog +k8s.io/apiserver/pkg/storage +k8s.io/apiserver/pkg/util/flushwriter +k8s.io/apiserver/pkg/util/wsstream +k8s.io/apiserver/pkg/storage/etcd3/metrics +k8s.io/apiserver/pkg/storage/value +k8s.io/apiserver/pkg/storage/cacher +k8s.io/apiserver/pkg/storage/errors +k8s.io/apiserver/pkg/storage/etcd3 +k8s.io/apiserver/pkg/util/dryrun +k8s.io/apiserver/pkg/apis/config +k8s.io/apiserver/pkg/apis/config/v1 +k8s.io/apiserver/pkg/storage/value/encrypt/aes +k8s.io/apiserver/pkg/storage/value/encrypt/envelope +k8s.io/apiserver/pkg/storage/value/encrypt/identity +k8s.io/apiserver/pkg/storage/value/encrypt/secretbox +k8s.io/apiserver/pkg/apis/audit/install +k8s.io/apiserver/pkg/audit/util +k8s.io/apiserver/pkg/audit/event +k8s.io/apiserver/pkg/admission/plugin/webhook/config +k8s.io/apiserver/pkg/admission/plugin/webhook/namespace +k8s.io/apiserver/pkg/admission/plugin/webhook/object +k8s.io/apiserver/pkg/admission/plugin/webhook/rules +k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal +k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1 +k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission +k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1alpha1 # k8s.io/client-go v8.0.0+incompatible => k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90 +k8s.io/client-go/tools/clientcmd +k8s.io/client-go/util/retry +k8s.io/client-go/tools/reference +k8s.io/client-go/tools/record +k8s.io/client-go/plugin/pkg/client/auth/oidc +k8s.io/client-go/rest k8s.io/client-go/discovery +k8s.io/client-go/util/flowcontrol k8s.io/client-go/discovery/fake +k8s.io/client-go/testing +k8s.io/client-go/tools/cache +k8s.io/client-go/listers/rbac/v1 k8s.io/client-go/dynamic k8s.io/client-go/informers +k8s.io/client-go/util/workqueue +k8s.io/client-go/kubernetes/scheme +k8s.io/client-go/listers/core/v1 +k8s.io/client-go/kubernetes/fake +k8s.io/client-go/kubernetes/typed/core/v1 +k8s.io/client-go/kubernetes +k8s.io/client-go/listers/apps/v1 +k8s.io/client-go/tools/auth +k8s.io/client-go/tools/clientcmd/api +k8s.io/client-go/tools/clientcmd/api/latest +k8s.io/client-go/util/homedir +k8s.io/client-go/tools/record/util +k8s.io/client-go/pkg/version +k8s.io/client-go/plugin/pkg/client/auth/exec +k8s.io/client-go/rest/watch +k8s.io/client-go/tools/metrics +k8s.io/client-go/transport +k8s.io/client-go/util/cert +k8s.io/client-go/tools/pager k8s.io/client-go/informers/admissionregistration -k8s.io/client-go/informers/admissionregistration/v1 -k8s.io/client-go/informers/admissionregistration/v1beta1 k8s.io/client-go/informers/apps -k8s.io/client-go/informers/apps/v1 -k8s.io/client-go/informers/apps/v1beta1 -k8s.io/client-go/informers/apps/v1beta2 k8s.io/client-go/informers/auditregistration -k8s.io/client-go/informers/auditregistration/v1alpha1 k8s.io/client-go/informers/autoscaling -k8s.io/client-go/informers/autoscaling/v1 -k8s.io/client-go/informers/autoscaling/v2beta1 -k8s.io/client-go/informers/autoscaling/v2beta2 k8s.io/client-go/informers/batch -k8s.io/client-go/informers/batch/v1 -k8s.io/client-go/informers/batch/v1beta1 -k8s.io/client-go/informers/batch/v2alpha1 k8s.io/client-go/informers/certificates -k8s.io/client-go/informers/certificates/v1beta1 k8s.io/client-go/informers/coordination -k8s.io/client-go/informers/coordination/v1 -k8s.io/client-go/informers/coordination/v1beta1 k8s.io/client-go/informers/core -k8s.io/client-go/informers/core/v1 k8s.io/client-go/informers/discovery -k8s.io/client-go/informers/discovery/v1alpha1 k8s.io/client-go/informers/events -k8s.io/client-go/informers/events/v1beta1 k8s.io/client-go/informers/extensions -k8s.io/client-go/informers/extensions/v1beta1 k8s.io/client-go/informers/internalinterfaces k8s.io/client-go/informers/networking -k8s.io/client-go/informers/networking/v1 -k8s.io/client-go/informers/networking/v1beta1 k8s.io/client-go/informers/node -k8s.io/client-go/informers/node/v1alpha1 -k8s.io/client-go/informers/node/v1beta1 k8s.io/client-go/informers/policy -k8s.io/client-go/informers/policy/v1beta1 k8s.io/client-go/informers/rbac -k8s.io/client-go/informers/rbac/v1 -k8s.io/client-go/informers/rbac/v1alpha1 -k8s.io/client-go/informers/rbac/v1beta1 k8s.io/client-go/informers/scheduling -k8s.io/client-go/informers/scheduling/v1 -k8s.io/client-go/informers/scheduling/v1alpha1 -k8s.io/client-go/informers/scheduling/v1beta1 k8s.io/client-go/informers/settings -k8s.io/client-go/informers/settings/v1alpha1 k8s.io/client-go/informers/storage -k8s.io/client-go/informers/storage/v1 -k8s.io/client-go/informers/storage/v1alpha1 -k8s.io/client-go/informers/storage/v1beta1 -k8s.io/client-go/kubernetes -k8s.io/client-go/kubernetes/fake -k8s.io/client-go/kubernetes/scheme k8s.io/client-go/kubernetes/typed/admissionregistration/v1 k8s.io/client-go/kubernetes/typed/admissionregistration/v1/fake k8s.io/client-go/kubernetes/typed/admissionregistration/v1beta1 @@ -644,7 +653,6 @@ k8s.io/client-go/kubernetes/typed/coordination/v1 k8s.io/client-go/kubernetes/typed/coordination/v1/fake k8s.io/client-go/kubernetes/typed/coordination/v1beta1 k8s.io/client-go/kubernetes/typed/coordination/v1beta1/fake -k8s.io/client-go/kubernetes/typed/core/v1 k8s.io/client-go/kubernetes/typed/core/v1/fake k8s.io/client-go/kubernetes/typed/discovery/v1alpha1 k8s.io/client-go/kubernetes/typed/discovery/v1alpha1/fake @@ -682,9 +690,48 @@ k8s.io/client-go/kubernetes/typed/storage/v1alpha1 k8s.io/client-go/kubernetes/typed/storage/v1alpha1/fake k8s.io/client-go/kubernetes/typed/storage/v1beta1 k8s.io/client-go/kubernetes/typed/storage/v1beta1/fake +k8s.io/client-go/util/keyutil +k8s.io/client-go/tools/clientcmd/api/v1 +k8s.io/client-go/pkg/apis/clientauthentication +k8s.io/client-go/pkg/apis/clientauthentication/v1alpha1 +k8s.io/client-go/pkg/apis/clientauthentication/v1beta1 +k8s.io/client-go/util/connrotation +k8s.io/client-go/informers/admissionregistration/v1 +k8s.io/client-go/informers/admissionregistration/v1beta1 +k8s.io/client-go/informers/apps/v1 +k8s.io/client-go/informers/apps/v1beta1 +k8s.io/client-go/informers/apps/v1beta2 +k8s.io/client-go/informers/auditregistration/v1alpha1 +k8s.io/client-go/informers/autoscaling/v1 +k8s.io/client-go/informers/autoscaling/v2beta1 +k8s.io/client-go/informers/autoscaling/v2beta2 +k8s.io/client-go/informers/batch/v1 +k8s.io/client-go/informers/batch/v1beta1 +k8s.io/client-go/informers/batch/v2alpha1 +k8s.io/client-go/informers/certificates/v1beta1 +k8s.io/client-go/informers/coordination/v1 +k8s.io/client-go/informers/coordination/v1beta1 +k8s.io/client-go/informers/core/v1 +k8s.io/client-go/informers/discovery/v1alpha1 +k8s.io/client-go/informers/events/v1beta1 +k8s.io/client-go/informers/extensions/v1beta1 +k8s.io/client-go/informers/networking/v1 +k8s.io/client-go/informers/networking/v1beta1 +k8s.io/client-go/informers/node/v1alpha1 +k8s.io/client-go/informers/node/v1beta1 +k8s.io/client-go/informers/policy/v1beta1 +k8s.io/client-go/informers/rbac/v1 +k8s.io/client-go/informers/rbac/v1alpha1 +k8s.io/client-go/informers/rbac/v1beta1 +k8s.io/client-go/informers/scheduling/v1 +k8s.io/client-go/informers/scheduling/v1alpha1 +k8s.io/client-go/informers/scheduling/v1beta1 +k8s.io/client-go/informers/settings/v1alpha1 +k8s.io/client-go/informers/storage/v1 +k8s.io/client-go/informers/storage/v1alpha1 +k8s.io/client-go/informers/storage/v1beta1 k8s.io/client-go/listers/admissionregistration/v1 k8s.io/client-go/listers/admissionregistration/v1beta1 -k8s.io/client-go/listers/apps/v1 k8s.io/client-go/listers/apps/v1beta1 k8s.io/client-go/listers/apps/v1beta2 k8s.io/client-go/listers/auditregistration/v1alpha1 @@ -697,7 +744,6 @@ k8s.io/client-go/listers/batch/v2alpha1 k8s.io/client-go/listers/certificates/v1beta1 k8s.io/client-go/listers/coordination/v1 k8s.io/client-go/listers/coordination/v1beta1 -k8s.io/client-go/listers/core/v1 k8s.io/client-go/listers/discovery/v1alpha1 k8s.io/client-go/listers/events/v1beta1 k8s.io/client-go/listers/extensions/v1beta1 @@ -706,7 +752,6 @@ k8s.io/client-go/listers/networking/v1beta1 k8s.io/client-go/listers/node/v1alpha1 k8s.io/client-go/listers/node/v1beta1 k8s.io/client-go/listers/policy/v1beta1 -k8s.io/client-go/listers/rbac/v1 k8s.io/client-go/listers/rbac/v1alpha1 k8s.io/client-go/listers/rbac/v1beta1 k8s.io/client-go/listers/scheduling/v1 @@ -716,70 +761,42 @@ k8s.io/client-go/listers/settings/v1alpha1 k8s.io/client-go/listers/storage/v1 k8s.io/client-go/listers/storage/v1alpha1 k8s.io/client-go/listers/storage/v1beta1 -k8s.io/client-go/pkg/apis/clientauthentication -k8s.io/client-go/pkg/apis/clientauthentication/v1alpha1 -k8s.io/client-go/pkg/apis/clientauthentication/v1beta1 -k8s.io/client-go/pkg/version -k8s.io/client-go/plugin/pkg/client/auth/exec -k8s.io/client-go/plugin/pkg/client/auth/oidc -k8s.io/client-go/rest -k8s.io/client-go/rest/watch -k8s.io/client-go/testing -k8s.io/client-go/tools/auth -k8s.io/client-go/tools/cache -k8s.io/client-go/tools/clientcmd -k8s.io/client-go/tools/clientcmd/api -k8s.io/client-go/tools/clientcmd/api/latest -k8s.io/client-go/tools/clientcmd/api/v1 -k8s.io/client-go/tools/metrics -k8s.io/client-go/tools/pager -k8s.io/client-go/tools/record -k8s.io/client-go/tools/record/util -k8s.io/client-go/tools/reference -k8s.io/client-go/transport -k8s.io/client-go/util/cert -k8s.io/client-go/util/connrotation -k8s.io/client-go/util/flowcontrol -k8s.io/client-go/util/homedir -k8s.io/client-go/util/keyutil -k8s.io/client-go/util/retry -k8s.io/client-go/util/workqueue # k8s.io/code-generator v0.0.0 => k8s.io/code-generator v0.0.0-20190912054826-cd179ad6a269 k8s.io/code-generator/cmd/client-gen +k8s.io/code-generator/cmd/conversion-gen/ +k8s.io/code-generator/cmd/deepcopy-gen +k8s.io/code-generator/cmd/defaulter-gen +k8s.io/code-generator/cmd/go-to-protobuf +k8s.io/code-generator/cmd/import-boss +k8s.io/code-generator/cmd/informer-gen +k8s.io/code-generator/cmd/lister-gen +k8s.io/code-generator/cmd/openapi-gen +k8s.io/code-generator/cmd/set-gen k8s.io/code-generator/cmd/client-gen/args k8s.io/code-generator/cmd/client-gen/generators -k8s.io/code-generator/cmd/client-gen/generators/fake -k8s.io/code-generator/cmd/client-gen/generators/scheme -k8s.io/code-generator/cmd/client-gen/generators/util -k8s.io/code-generator/cmd/client-gen/path -k8s.io/code-generator/cmd/client-gen/types -k8s.io/code-generator/cmd/conversion-gen/ +k8s.io/code-generator/pkg/util k8s.io/code-generator/cmd/conversion-gen/args k8s.io/code-generator/cmd/conversion-gen/generators -k8s.io/code-generator/cmd/deepcopy-gen k8s.io/code-generator/cmd/deepcopy-gen/args -k8s.io/code-generator/cmd/defaulter-gen k8s.io/code-generator/cmd/defaulter-gen/args -k8s.io/code-generator/cmd/go-to-protobuf k8s.io/code-generator/cmd/go-to-protobuf/protobuf -k8s.io/code-generator/cmd/import-boss -k8s.io/code-generator/cmd/informer-gen k8s.io/code-generator/cmd/informer-gen/args k8s.io/code-generator/cmd/informer-gen/generators -k8s.io/code-generator/cmd/lister-gen k8s.io/code-generator/cmd/lister-gen/args k8s.io/code-generator/cmd/lister-gen/generators -k8s.io/code-generator/cmd/openapi-gen -k8s.io/code-generator/cmd/set-gen +k8s.io/code-generator/cmd/client-gen/types +k8s.io/code-generator/cmd/client-gen/generators/fake +k8s.io/code-generator/cmd/client-gen/generators/scheme +k8s.io/code-generator/cmd/client-gen/generators/util +k8s.io/code-generator/cmd/client-gen/path k8s.io/code-generator/pkg/namer -k8s.io/code-generator/pkg/util k8s.io/code-generator/third_party/forked/golang/reflect # k8s.io/component-base v0.0.0 => k8s.io/component-base v0.0.0-20190918160511-547f6c5d7090 -k8s.io/component-base/cli/flag -k8s.io/component-base/featuregate k8s.io/component-base/logs +k8s.io/component-base/cli/flag k8s.io/component-base/metrics k8s.io/component-base/metrics/legacyregistry +k8s.io/component-base/featuregate k8s.io/component-base/version # k8s.io/gengo v0.0.0-20190822140433-26a664648505 k8s.io/gengo/args @@ -787,62 +804,77 @@ k8s.io/gengo/examples/deepcopy-gen/generators k8s.io/gengo/examples/defaulter-gen/generators k8s.io/gengo/examples/import-boss/generators k8s.io/gengo/examples/set-gen/generators -k8s.io/gengo/examples/set-gen/sets k8s.io/gengo/generator k8s.io/gengo/namer -k8s.io/gengo/parser k8s.io/gengo/types +k8s.io/gengo/parser +k8s.io/gengo/examples/set-gen/sets # k8s.io/klog v0.4.0 k8s.io/klog # k8s.io/kube-aggregator v0.0.0 => k8s.io/kube-aggregator v0.0.0-20190918161219-8c8f079fddc3 -k8s.io/kube-aggregator/pkg/apis/apiregistration k8s.io/kube-aggregator/pkg/apis/apiregistration/v1 -k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1 +k8s.io/kube-aggregator/pkg/client/informers/externalversions k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset +k8s.io/kube-aggregator/pkg/client/listers/apiregistration/v1 k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/fake -k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme +k8s.io/kube-aggregator/pkg/apis/apiregistration +k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1 +k8s.io/kube-aggregator/pkg/client/informers/externalversions/apiregistration +k8s.io/kube-aggregator/pkg/client/informers/externalversions/internalinterfaces k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/typed/apiregistration/v1 -k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/typed/apiregistration/v1/fake k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/typed/apiregistration/v1beta1 +k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/typed/apiregistration/v1/fake k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/typed/apiregistration/v1beta1/fake -k8s.io/kube-aggregator/pkg/client/informers/externalversions -k8s.io/kube-aggregator/pkg/client/informers/externalversions/apiregistration k8s.io/kube-aggregator/pkg/client/informers/externalversions/apiregistration/v1 k8s.io/kube-aggregator/pkg/client/informers/externalversions/apiregistration/v1beta1 -k8s.io/kube-aggregator/pkg/client/informers/externalversions/internalinterfaces -k8s.io/kube-aggregator/pkg/client/listers/apiregistration/v1 +k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme k8s.io/kube-aggregator/pkg/client/listers/apiregistration/v1beta1 # k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf k8s.io/kube-openapi/cmd/openapi-gen -k8s.io/kube-openapi/cmd/openapi-gen/args -k8s.io/kube-openapi/pkg/builder k8s.io/kube-openapi/pkg/common +k8s.io/kube-openapi/cmd/openapi-gen/args k8s.io/kube-openapi/pkg/generators -k8s.io/kube-openapi/pkg/generators/rules -k8s.io/kube-openapi/pkg/handler -k8s.io/kube-openapi/pkg/schemaconv -k8s.io/kube-openapi/pkg/util k8s.io/kube-openapi/pkg/util/proto +k8s.io/kube-openapi/pkg/util +k8s.io/kube-openapi/pkg/builder +k8s.io/kube-openapi/pkg/handler +k8s.io/kube-openapi/pkg/generators/rules k8s.io/kube-openapi/pkg/util/sets +k8s.io/kube-openapi/pkg/schemaconv # k8s.io/kubernetes v1.16.0 -k8s.io/kubernetes/pkg/apis/rbac -k8s.io/kubernetes/pkg/apis/rbac/v1 +k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac +k8s.io/kubernetes/pkg/util/labels k8s.io/kubernetes/pkg/printers k8s.io/kubernetes/pkg/printers/storage +k8s.io/kubernetes/pkg/apis/rbac/v1 k8s.io/kubernetes/pkg/registry/rbac/validation -k8s.io/kubernetes/pkg/util/labels -k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac +k8s.io/kubernetes/pkg/apis/rbac # k8s.io/utils v0.0.0-20190801114015-581e00157fb1 -k8s.io/utils/buffer k8s.io/utils/integer -k8s.io/utils/path -k8s.io/utils/pointer +k8s.io/utils/buffer k8s.io/utils/trace +k8s.io/utils/pointer +k8s.io/utils/path +# sigs.k8s.io/controller-tools v0.2.4 +sigs.k8s.io/controller-tools/cmd/controller-gen +sigs.k8s.io/controller-tools/pkg/crd +sigs.k8s.io/controller-tools/pkg/deepcopy +sigs.k8s.io/controller-tools/pkg/genall +sigs.k8s.io/controller-tools/pkg/genall/help +sigs.k8s.io/controller-tools/pkg/genall/help/pretty +sigs.k8s.io/controller-tools/pkg/markers +sigs.k8s.io/controller-tools/pkg/rbac +sigs.k8s.io/controller-tools/pkg/schemapatcher +sigs.k8s.io/controller-tools/pkg/version +sigs.k8s.io/controller-tools/pkg/webhook +sigs.k8s.io/controller-tools/pkg/crd/markers +sigs.k8s.io/controller-tools/pkg/loader +sigs.k8s.io/controller-tools/pkg/schemapatcher/internal/yaml # sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca sigs.k8s.io/structured-merge-diff/fieldpath sigs.k8s.io/structured-merge-diff/merge -sigs.k8s.io/structured-merge-diff/schema sigs.k8s.io/structured-merge-diff/typed sigs.k8s.io/structured-merge-diff/value +sigs.k8s.io/structured-merge-diff/schema # sigs.k8s.io/yaml v1.1.0 sigs.k8s.io/yaml diff --git a/vendor/sigs.k8s.io/controller-tools/LICENSE b/vendor/sigs.k8s.io/controller-tools/LICENSE new file mode 100644 index 0000000000..8dada3edaf --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. diff --git a/vendor/sigs.k8s.io/controller-tools/cmd/controller-gen/main.go b/vendor/sigs.k8s.io/controller-tools/cmd/controller-gen/main.go new file mode 100644 index 0000000000..692b26c8da --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/cmd/controller-gen/main.go @@ -0,0 +1,263 @@ +/* +Copyright 2018 The Kubernetes 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 main + +import ( + "encoding/json" + "fmt" + "io" + "os" + "strings" + + "github.com/spf13/cobra" + + "sigs.k8s.io/controller-tools/pkg/crd" + "sigs.k8s.io/controller-tools/pkg/deepcopy" + "sigs.k8s.io/controller-tools/pkg/genall" + "sigs.k8s.io/controller-tools/pkg/genall/help" + prettyhelp "sigs.k8s.io/controller-tools/pkg/genall/help/pretty" + "sigs.k8s.io/controller-tools/pkg/markers" + "sigs.k8s.io/controller-tools/pkg/rbac" + "sigs.k8s.io/controller-tools/pkg/schemapatcher" + "sigs.k8s.io/controller-tools/pkg/version" + "sigs.k8s.io/controller-tools/pkg/webhook" +) + +//go:generate go run ../helpgen/main.go paths=../../pkg/... generate:headerFile=../../boilerplate.go.txt,year=2019 + +// Options are specified to controller-gen by turning generators and output rules into +// markers, and then parsing them using the standard registry logic (without the "+"). +// Each marker and output rule should thus be usable as a marker target. + +var ( + // allGenerators maintains the list of all known generators, giving + // them names for use on the command line. + // each turns into a command line option, + // and has options for output forms. + allGenerators = map[string]genall.Generator{ + "crd": crd.Generator{}, + "rbac": rbac.Generator{}, + "object": deepcopy.Generator{}, + "webhook": webhook.Generator{}, + "schemapatch": schemapatcher.Generator{}, + } + + // allOutputRules defines the list of all known output rules, giving + // them names for use on the command line. + // Each output rule turns into two command line options: + // - output::
(per-generator output) + // - output: (default output) + allOutputRules = map[string]genall.OutputRule{ + "dir": genall.OutputToDirectory(""), + "none": genall.OutputToNothing, + "stdout": genall.OutputToStdout, + "artifacts": genall.OutputArtifacts{}, + } + + // optionsRegistry contains all the marker definitions used to process command line options + optionsRegistry = &markers.Registry{} +) + +func init() { + for genName, gen := range allGenerators { + // make the generator options marker itself + defn := markers.Must(markers.MakeDefinition(genName, markers.DescribesPackage, gen)) + if err := optionsRegistry.Register(defn); err != nil { + panic(err) + } + if helpGiver, hasHelp := gen.(genall.HasHelp); hasHelp { + if help := helpGiver.Help(); help != nil { + optionsRegistry.AddHelp(defn, help) + } + } + + // make per-generation output rule markers + for ruleName, rule := range allOutputRules { + ruleMarker := markers.Must(markers.MakeDefinition(fmt.Sprintf("output:%s:%s", genName, ruleName), markers.DescribesPackage, rule)) + if err := optionsRegistry.Register(ruleMarker); err != nil { + panic(err) + } + if helpGiver, hasHelp := rule.(genall.HasHelp); hasHelp { + if help := helpGiver.Help(); help != nil { + optionsRegistry.AddHelp(ruleMarker, help) + } + } + } + } + + // make "default output" output rule markers + for ruleName, rule := range allOutputRules { + ruleMarker := markers.Must(markers.MakeDefinition("output:"+ruleName, markers.DescribesPackage, rule)) + if err := optionsRegistry.Register(ruleMarker); err != nil { + panic(err) + } + if helpGiver, hasHelp := rule.(genall.HasHelp); hasHelp { + if help := helpGiver.Help(); help != nil { + optionsRegistry.AddHelp(ruleMarker, help) + } + } + } + + // add in the common options markers + if err := genall.RegisterOptionsMarkers(optionsRegistry); err != nil { + panic(err) + } +} + +// noUsageError suppresses usage printing when it occurs +// (since cobra doesn't provide a good way to avoid printing +// out usage in only certain situations). +type noUsageError struct{ error } + +func main() { + helpLevel := 0 + whichLevel := 0 + showVersion := false + + cmd := &cobra.Command{ + Use: "controller-gen", + Short: "Generate Kubernetes API extension resources and code.", + Long: "Generate Kubernetes API extension resources and code.", + Example: ` # Generate RBAC manifests and crds for all types under apis/, + # outputting crds to /tmp/crds and everything else to stdout + controller-gen rbac:roleName= crd paths=./apis/... output:crd:dir=/tmp/crds output:stdout + + # Generate deepcopy/runtime.Object implementations for a particular file + controller-gen object paths=./apis/v1beta1/some_types.go + + # Generate OpenAPI v3 schemas for API packages and merge them into existing CRD manifests + controller-gen schemapatch:manifests=./manifests output:dir=./manifests paths=./pkg/apis/... + + # Run all the generators for a given project + controller-gen paths=./apis/... + + # Explain the markers for generating CRDs, and their arguments + controller-gen crd -ww +`, + RunE: func(c *cobra.Command, rawOpts []string) error { + // print version if asked for it + if showVersion { + version.Print() + return nil + } + + // print the help if we asked for it (since we've got a different help flag :-/), then bail + if helpLevel > 0 { + return c.Usage() + } + + // print the marker docs if we asked for them, then bail + if whichLevel > 0 { + return printMarkerDocs(c, rawOpts, whichLevel) + } + + // otherwise, set up the runtime for actually running the generators + rt, err := genall.FromOptions(optionsRegistry, rawOpts) + if err != nil { + return err + } + if len(rt.Generators) == 0 { + return fmt.Errorf("no generators specified") + } + + if hadErrs := rt.Run(); hadErrs { + // don't obscure the actual error with a bunch of usage + return noUsageError{fmt.Errorf("not all generators ran successfully")} + } + return nil + }, + SilenceUsage: true, // silence the usage, then print it out ourselves if it wasn't suppressed + } + cmd.Flags().CountVarP(&whichLevel, "which-markers", "w", "print out all markers available with the requested generators\n(up to -www for the most detailed output, or -wwww for json output)") + cmd.Flags().CountVarP(&helpLevel, "detailed-help", "h", "print out more detailed help\n(up to -hhh for the most detailed output, or -hhhh for json output)") + cmd.Flags().BoolVar(&showVersion, "version", false, "show version") + cmd.Flags().Bool("help", false, "print out usage and a summary of options") + oldUsage := cmd.UsageFunc() + cmd.SetUsageFunc(func(c *cobra.Command) error { + if err := oldUsage(c); err != nil { + return err + } + if helpLevel == 0 { + helpLevel = summaryHelp + } + fmt.Fprintf(c.OutOrStderr(), "\n\nOptions\n\n") + return helpForLevels(c.OutOrStdout(), c.OutOrStderr(), helpLevel, optionsRegistry, help.SortByOption) + }) + + if err := cmd.Execute(); err != nil { + if _, noUsage := err.(noUsageError); !noUsage { + // print the usage unless we suppressed it + if err := cmd.Usage(); err != nil { + panic(err) + } + } + fmt.Fprintf(cmd.OutOrStderr(), "run `%[1]s %[2]s -w` to see all available markers, or `%[1]s %[2]s -h` for usage\n", cmd.CalledAs(), strings.Join(os.Args[1:], " ")) + os.Exit(1) + } +} + +// printMarkerDocs prints out marker help for the given generators specified in +// the rawOptions, at the given level. +func printMarkerDocs(c *cobra.Command, rawOptions []string, whichLevel int) error { + // just grab a registry so we don't lag while trying to load roots + // (like we'd do if we just constructed the full runtime). + reg, err := genall.RegistryFromOptions(optionsRegistry, rawOptions) + if err != nil { + return err + } + + return helpForLevels(c.OutOrStdout(), c.OutOrStderr(), whichLevel, reg, help.SortByCategory) +} + +func helpForLevels(mainOut io.Writer, errOut io.Writer, whichLevel int, reg *markers.Registry, sorter help.SortGroup) error { + helpInfo := help.ByCategory(reg, sorter) + switch whichLevel { + case jsonHelp: + if err := json.NewEncoder(mainOut).Encode(helpInfo); err != nil { + return err + } + case detailedHelp, fullHelp: + fullDetail := whichLevel == fullHelp + for _, cat := range helpInfo { + if cat.Category == "" { + continue + } + contents := prettyhelp.MarkersDetails(fullDetail, cat.Category, cat.Markers) + if err := contents.WriteTo(errOut); err != nil { + return err + } + } + case summaryHelp: + for _, cat := range helpInfo { + if cat.Category == "" { + continue + } + contents := prettyhelp.MarkersSummary(cat.Category, cat.Markers) + if err := contents.WriteTo(errOut); err != nil { + return err + } + } + } + return nil +} + +const ( + _ = iota + summaryHelp + detailedHelp + fullHelp + jsonHelp +) diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/crd/conv.go b/vendor/sigs.k8s.io/controller-tools/pkg/crd/conv.go new file mode 100644 index 0000000000..753b7d9698 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/crd/conv.go @@ -0,0 +1,122 @@ +package crd + +import ( + "fmt" + + apiextinternal "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" + apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" + "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +var ( + conversionScheme = runtime.NewScheme() +) + +func init() { + if err := apiextinternal.AddToScheme(conversionScheme); err != nil { + panic("must be able to add internal apiextensions to the CRD conversion Scheme") + } + if err := apiext.AddToScheme(conversionScheme); err != nil { + panic("must be able to add apiextensions/v1 to the CRD conversion Scheme") + } + if err := apiextv1beta1.AddToScheme(conversionScheme); err != nil { + panic("must be able to add apiextensions/v1beta1 to the CRD conversion Scheme") + } +} + +// AsVersion converts a CRD from the canonical internal form (currently v1) to some external form. +func AsVersion(original apiext.CustomResourceDefinition, gv schema.GroupVersion) (runtime.Object, error) { + // We can use the internal versions an existing conversions from kubernetes, since they're not in k/k itself. + // This punts the problem of conversion down the road for a future maintainer (or future instance of @directxman12) + // when we have to support older versions that get removed, or when API machinery decides to yell at us for this + // questionable decision. + intVer, err := conversionScheme.ConvertToVersion(&original, apiextinternal.SchemeGroupVersion) + if err != nil { + return nil, fmt.Errorf("unable to convert to internal CRD version: %v", err) + } + + return conversionScheme.ConvertToVersion(intVer, gv) +} + +// mergeIdenticalSubresources checks to see if subresources are identical across +// all versions, and if so, merges them into a top-level version. +// +// This assumes you're not using trivial versions. +func mergeIdenticalSubresources(crd *apiextv1beta1.CustomResourceDefinition) { + subres := crd.Spec.Versions[0].Subresources + for _, ver := range crd.Spec.Versions { + if ver.Subresources == nil || !equality.Semantic.DeepEqual(subres, ver.Subresources) { + // either all nil, or not identical + return + } + } + + // things are identical if we've gotten this far, so move the subresources up + // and discard the identical per-version ones + crd.Spec.Subresources = subres + for i := range crd.Spec.Versions { + crd.Spec.Versions[i].Subresources = nil + } +} + +// mergeIdenticalSchemata checks to see if schemata are identical across +// all versions, and if so, merges them into a top-level version. +// +// This assumes you're not using trivial versions. +func mergeIdenticalSchemata(crd *apiextv1beta1.CustomResourceDefinition) { + schema := crd.Spec.Versions[0].Schema + for _, ver := range crd.Spec.Versions { + if ver.Schema == nil || !equality.Semantic.DeepEqual(schema, ver.Schema) { + // either all nil, or not identical + return + } + } + + // things are identical if we've gotten this far, so move the schemata up + // to a single schema and discard the identical per-version ones + crd.Spec.Validation = schema + for i := range crd.Spec.Versions { + crd.Spec.Versions[i].Schema = nil + } +} + +// mergeIdenticalPrinterColumns checks to see if schemata are identical across +// all versions, and if so, merges them into a top-level version. +// +// This assumes you're not using trivial versions. +func mergeIdenticalPrinterColumns(crd *apiextv1beta1.CustomResourceDefinition) { + cols := crd.Spec.Versions[0].AdditionalPrinterColumns + for _, ver := range crd.Spec.Versions { + if len(ver.AdditionalPrinterColumns) == 0 || !equality.Semantic.DeepEqual(cols, ver.AdditionalPrinterColumns) { + // either all nil, or not identical + return + } + } + + // things are identical if we've gotten this far, so move the printer columns up + // and discard the identical per-version ones + crd.Spec.AdditionalPrinterColumns = cols + for i := range crd.Spec.Versions { + crd.Spec.Versions[i].AdditionalPrinterColumns = nil + } +} + +// MergeIdenticalVersionInfo makes sure that components of the Versions field that are identical +// across all versions get merged into the top-level fields in v1beta1. +// +// This is required by the Kubernetes API server validation. +// +// The reason is that a v1beta1 -> v1 -> v1beta1 conversion cycle would need to +// round-trip identically, v1 doesn't have top-level subresources, and without +// this restriction it would be ambiguous how a v1-with-identical-subresources +// converts into a v1beta1). +func MergeIdenticalVersionInfo(crd *apiextv1beta1.CustomResourceDefinition) { + if len(crd.Spec.Versions) > 0 { + mergeIdenticalSubresources(crd) + mergeIdenticalSchemata(crd) + mergeIdenticalPrinterColumns(crd) + } +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/crd/desc_visitor.go b/vendor/sigs.k8s.io/controller-tools/pkg/crd/desc_visitor.go new file mode 100644 index 0000000000..cba36c46cb --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/crd/desc_visitor.go @@ -0,0 +1,78 @@ +/* +Copyright 2019 The Kubernetes 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 crd + +import ( + "strings" + "unicode" + + apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" +) + +// TruncateDescription truncates the description of fields in given schema if it +// exceeds maxLen. +// It tries to chop off the description at the closest sentence boundary. +func TruncateDescription(schema *apiext.JSONSchemaProps, maxLen int) { + EditSchema(schema, descVisitor{maxLen: maxLen}) +} + +// descVisitor recursively visits all fields in the schema and truncates the +// description of the fields to specified maxLen. +type descVisitor struct { + // maxLen is the maximum allowed length for decription of a field + maxLen int +} + +func (v descVisitor) Visit(schema *apiext.JSONSchemaProps) SchemaVisitor { + if schema == nil { + return v + } + if v.maxLen < 0 { + return nil /* no further work to be done for this schema */ + } + if v.maxLen == 0 { + schema.Description = "" + return v + } + if len(schema.Description) > v.maxLen { + schema.Description = truncateString(schema.Description, v.maxLen) + return v + } + return v +} + +// truncateString truncates given desc string if it exceeds maxLen. It may +// return string with length less than maxLen even in cases where original desc +// exceeds maxLen because it tries to chop off the desc at the closest sentence +// boundary to avoid incomplete sentences. +func truncateString(desc string, maxLen int) string { + desc = desc[0:maxLen] + + // Trying to chop off at closest sentence boundary. + if n := strings.LastIndexFunc(desc, isSentenceTerminal); n > 0 { + return desc[0 : n+1] + } + // TODO(droot): Improve the logic to chop off at closest word boundary + // or add elipses (...) to indicate that it's chopped incase no closest + // sentence found within maxLen. + return desc +} + +// helper function to determine if given rune is a sentence terminal or not. +func isSentenceTerminal(r rune) bool { + return unicode.Is(unicode.STerm, r) +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/crd/doc.go b/vendor/sigs.k8s.io/controller-tools/pkg/crd/doc.go new file mode 100644 index 0000000000..fd48e44aec --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/crd/doc.go @@ -0,0 +1,63 @@ +/* +Copyright 2019 The Kubernetes 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 crd contains utilities for generating CustomResourceDefinitions and +// their corresponding OpenAPI validation schemata. +// +// Markers +// +// Markers live under the markers subpackage. Two types of markers exist: +// those that modify schema generation (for validation), and those that modify +// the rest of the CRD. See the subpackage for more information and all +// supported markers. +// +// Collecting Types and Generating CRDs +// +// The Parser is the entrypoint for collecting the information required to +// generate CRDs. Like loader and collector, its methods are idemptotent, not +// doing extra work if called multiple times. +// +// Parser's method start with Need. Calling NeedXYZ indicates that XYZ should +// be made present in the eqivalent field in the Parser, where it can then be +// loaded from. Each Need method will in turn call Need on anything it needs. +// +// In general, root packages should first be loaded into the Parser with +// NeedPackage. Then, CRDs can be generated with NeedCRDFor. +// +// Errors are generally attached directly to the relevant Package with +// AddError. +// +// Known Packages +// +// There are a few types from Kubernetes that have special meaning, but don't +// have validation markers attached. Those specific types have overrides +// listed in KnownPackages that can be added as overrides to any parser. +// +// Flattening +// +// Once schemata are generated, they can be used directly by external tooling +// (like JSONSchema validators), but must first be "flattened" to not contain +// references before use in a CRD (Kubernetes doesn't allow references in the +// CRD's validation schema). +// +// The Flattener built in to the Parser takes care of flattening out references +// when requesting the CRDs, but can be invoked manually. It will not modify +// the input schemata. +// +// Flattened schemata may further be passed to FlattenEmbedded to remove the +// use of AllOf (which is used to describe embedded struct fields when +// references are in use). This done automatically when fetching CRDs. +package crd diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/crd/flatten.go b/vendor/sigs.k8s.io/controller-tools/pkg/crd/flatten.go new file mode 100644 index 0000000000..00f4dc4c7d --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/crd/flatten.go @@ -0,0 +1,441 @@ +/* +Copyright 2019 The Kubernetes 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 crd + +import ( + "fmt" + "reflect" + "sort" + "strings" + "sync" + + apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + + "sigs.k8s.io/controller-tools/pkg/loader" +) + +// ErrorRecorder knows how to record errors. It wraps the part of +// pkg/loader.Package that we need to record errors in places were it might not +// make sense to have a loader.Package +type ErrorRecorder interface { + // AddError records that the given error occurred. + // See the documentation on loader.Package.AddError for more information. + AddError(error) +} + +// isOrNil checks if val is nil if val is of a nillable type, otherwise, +// it compares val to valInt (which should probably be the zero value). +func isOrNil(val reflect.Value, valInt interface{}, zeroInt interface{}) bool { + switch valKind := val.Kind(); valKind { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + return val.IsNil() + default: + return valInt == zeroInt + } +} + +// flattenAllOfInto copies properties from src to dst, then copies the properties +// of each item in src's allOf to dst's properties as well. +func flattenAllOfInto(dst *apiext.JSONSchemaProps, src apiext.JSONSchemaProps, errRec ErrorRecorder) { + if len(src.AllOf) > 0 { + for _, embedded := range src.AllOf { + flattenAllOfInto(dst, embedded, errRec) + } + } + + dstVal := reflect.Indirect(reflect.ValueOf(dst)) + srcVal := reflect.ValueOf(src) + typ := dstVal.Type() + + srcRemainder := apiext.JSONSchemaProps{} + srcRemVal := reflect.Indirect(reflect.ValueOf(&srcRemainder)) + dstRemainder := apiext.JSONSchemaProps{} + dstRemVal := reflect.Indirect(reflect.ValueOf(&dstRemainder)) + hoisted := false + + for i := 0; i < srcVal.NumField(); i++ { + fieldName := typ.Field(i).Name + switch fieldName { + case "AllOf": + // don't merge because we deal with it above + continue + case "Title", "Description", "Example", "ExternalDocs": + // don't merge because we pre-merge to properly preserve field docs + continue + } + srcField := srcVal.Field(i) + fldTyp := srcField.Type() + zeroVal := reflect.Zero(fldTyp) + zeroInt := zeroVal.Interface() + srcInt := srcField.Interface() + + if isOrNil(srcField, srcInt, zeroInt) { + // nothing to copy from src, continue + continue + } + + dstField := dstVal.Field(i) + dstInt := dstField.Interface() + if isOrNil(dstField, dstInt, zeroInt) { + // dst is empty, continue + dstField.Set(srcField) + continue + } + + if fldTyp.Comparable() && srcInt == dstInt { + // same value, continue + continue + } + + // resolve conflict + switch fieldName { + case "Properties": + // merge if possible, use all of otherwise + srcMap := srcInt.(map[string]apiext.JSONSchemaProps) + dstMap := dstInt.(map[string]apiext.JSONSchemaProps) + + for k, v := range srcMap { + dstProp, exists := dstMap[k] + if !exists { + dstMap[k] = v + continue + } + flattenAllOfInto(&dstProp, v, errRec) + dstMap[k] = dstProp + } + case "Required": + // merge + dstField.Set(reflect.AppendSlice(dstField, srcField)) + case "Type": + if srcInt != dstInt { + // TODO(directxman12): figure out how to attach this back to a useful point in the Go source or in the schema + errRec.AddError(fmt.Errorf("conflicting types in allOf branches in schema: %s vs %s", dstInt, srcInt)) + } + // keep the destination value, for now + // TODO(directxman12): Default -- use field? + // TODO(directxman12): + // - Dependencies: if field x is present, then either schema validates or all props are present + // - AdditionalItems: like AdditionalProperties + // - Definitions: common named validation sets that can be references (merge, bail if duplicate) + case "AdditionalProperties": + // as of the time of writing, `allows: false` is not allowed, so we don't have to handle it + srcProps := srcInt.(*apiext.JSONSchemaPropsOrBool) + if srcProps.Schema == nil { + // nothing to merge + continue + } + dstProps := dstInt.(*apiext.JSONSchemaPropsOrBool) + if dstProps.Schema == nil { + dstProps.Schema = &apiext.JSONSchemaProps{} + } + flattenAllOfInto(dstProps.Schema, *srcProps.Schema, errRec) + // NB(directxman12): no need to explicitly handle nullable -- false is considered to be the zero value + // TODO(directxman12): src isn't necessarily the field value -- it's just the most recent allOf entry + default: + // hoist into allOf... + hoisted = true + + srcRemVal.Field(i).Set(srcField) + dstRemVal.Field(i).Set(dstField) + // ...and clear the original + dstField.Set(zeroVal) + } + } + + if hoisted { + dst.AllOf = append(dst.AllOf, dstRemainder, srcRemainder) + } + + // dedup required + if len(dst.Required) > 0 { + reqUniq := make(map[string]struct{}) + for _, req := range dst.Required { + reqUniq[req] = struct{}{} + } + dst.Required = make([]string, 0, len(reqUniq)) + for req := range reqUniq { + dst.Required = append(dst.Required, req) + } + // be deterministic + sort.Strings(dst.Required) + } +} + +// allOfVisitor recursively visits allOf fields in the schema, +// merging nested allOf properties into the root schema. +type allOfVisitor struct { + // errRec is used to record errors while flattening (like two conflicting + // field values used in an allOf) + errRec ErrorRecorder +} + +func (v *allOfVisitor) Visit(schema *apiext.JSONSchemaProps) SchemaVisitor { + if schema == nil { + return v + } + + // clear this now so that we can safely preserve edits made my flattenAllOfInto + origAllOf := schema.AllOf + schema.AllOf = nil + + for _, embedded := range origAllOf { + flattenAllOfInto(schema, embedded, v.errRec) + } + return v +} + +// NB(directxman12): FlattenEmbedded is separate from Flattener because +// some tooling wants to flatten out embedded fields, but only actually +// flatten a few specific types first. + +// FlattenEmbedded flattens embedded fields (represented via AllOf) which have +// already had their references resolved into simple properties in the containing +// schema. +func FlattenEmbedded(schema *apiext.JSONSchemaProps, errRec ErrorRecorder) *apiext.JSONSchemaProps { + outSchema := schema.DeepCopy() + EditSchema(outSchema, &allOfVisitor{errRec: errRec}) + return outSchema +} + +// Flattener knows how to take a root type, and flatten all references in it +// into a single, flat type. Flattened types are cached, so it's relatively +// cheap to make repeated calls with the same type. +type Flattener struct { + // Parser is used to lookup package and type details, and parse in new packages. + Parser *Parser + + LookupReference func(ref string, contextPkg *loader.Package) (TypeIdent, error) + + // flattenedTypes hold the flattened version of each seen type for later reuse. + flattenedTypes map[TypeIdent]apiext.JSONSchemaProps + initOnce sync.Once +} + +func (f *Flattener) init() { + f.initOnce.Do(func() { + f.flattenedTypes = make(map[TypeIdent]apiext.JSONSchemaProps) + if f.LookupReference == nil { + f.LookupReference = identFromRef + } + }) +} + +// cacheType saves the flattened version of the given type for later reuse +func (f *Flattener) cacheType(typ TypeIdent, schema apiext.JSONSchemaProps) { + f.init() + f.flattenedTypes[typ] = schema +} + +// loadUnflattenedSchema fetches a fresh, unflattened schema from the parser. +func (f *Flattener) loadUnflattenedSchema(typ TypeIdent) (*apiext.JSONSchemaProps, error) { + f.Parser.NeedSchemaFor(typ) + + baseSchema, found := f.Parser.Schemata[typ] + if !found { + return nil, fmt.Errorf("unable to locate schema for type %s", typ) + } + return &baseSchema, nil +} + +// FlattenType flattens the given pre-loaded type, removing any references from it. +// It deep-copies the schema first, so it won't affect the parser's version of the schema. +func (f *Flattener) FlattenType(typ TypeIdent) *apiext.JSONSchemaProps { + f.init() + if cachedSchema, isCached := f.flattenedTypes[typ]; isCached { + return &cachedSchema + } + baseSchema, err := f.loadUnflattenedSchema(typ) + if err != nil { + typ.Package.AddError(err) + return nil + } + resSchema := f.FlattenSchema(*baseSchema, typ.Package) + f.cacheType(typ, *resSchema) + return resSchema +} + +// FlattenSchema flattens the given schema, removing any references. +// It deep-copies the schema first, so the input schema won't be affected. +func (f *Flattener) FlattenSchema(baseSchema apiext.JSONSchemaProps, currentPackage *loader.Package) *apiext.JSONSchemaProps { + resSchema := baseSchema.DeepCopy() + EditSchema(resSchema, &flattenVisitor{ + Flattener: f, + currentPackage: currentPackage, + }) + + return resSchema +} + +// RefParts splits a reference produced by the schema generator into its component +// type name and package name (if it's a cross-package reference). Note that +// referenced packages *must* be looked up relative to the current package. +func RefParts(ref string) (typ string, pkgName string, err error) { + if !strings.HasPrefix(ref, defPrefix) { + return "", "", fmt.Errorf("non-standard reference link %q", ref) + } + ref = ref[len(defPrefix):] + // decode the json pointer encodings + ref = strings.Replace(ref, "~1", "/", -1) + ref = strings.Replace(ref, "~0", "~", -1) + nameParts := strings.SplitN(ref, "~", 2) + + if len(nameParts) == 1 { + // local reference + return nameParts[0], "", nil + } + // cross-package reference + return nameParts[1], nameParts[0], nil +} + +// identFromRef converts the given schema ref from the given package back +// into the TypeIdent that it represents. +func identFromRef(ref string, contextPkg *loader.Package) (TypeIdent, error) { + typ, pkgName, err := RefParts(ref) + if err != nil { + return TypeIdent{}, err + } + + if pkgName == "" { + // a local reference + return TypeIdent{ + Name: typ, + Package: contextPkg, + }, nil + } + + // an external reference + return TypeIdent{ + Name: typ, + Package: contextPkg.Imports()[pkgName], + }, nil +} + +// preserveFields copies documentation fields from src into dst, preserving +// field-level documentation when flattening, and preserving field-level validation +// as allOf entries. +func preserveFields(dst *apiext.JSONSchemaProps, src apiext.JSONSchemaProps) { + srcDesc := src.Description + srcTitle := src.Title + srcExDoc := src.ExternalDocs + srcEx := src.Example + + src.Description, src.Title, src.ExternalDocs, src.Example = "", "", nil, nil + + src.Ref = nil + *dst = apiext.JSONSchemaProps{ + AllOf: []apiext.JSONSchemaProps{*dst, src}, + + // keep these, in case the source field doesn't specify anything useful + Description: dst.Description, + Title: dst.Title, + ExternalDocs: dst.ExternalDocs, + Example: dst.Example, + } + + if srcDesc != "" { + dst.Description = srcDesc + } + if srcTitle != "" { + dst.Title = srcTitle + } + if srcExDoc != nil { + dst.ExternalDocs = srcExDoc + } + if srcEx != nil { + dst.Example = srcEx + } +} + +// flattenVisitor visits each node in the schema, recursively flattening references. +type flattenVisitor struct { + *Flattener + + currentPackage *loader.Package + currentType *TypeIdent + currentSchema *apiext.JSONSchemaProps + originalField apiext.JSONSchemaProps +} + +func (f *flattenVisitor) Visit(baseSchema *apiext.JSONSchemaProps) SchemaVisitor { + if baseSchema == nil { + // end-of-node marker, cache the results + if f.currentType != nil { + f.cacheType(*f.currentType, *f.currentSchema) + // preserve field information *after* caching so that we don't + // accidentally cache field-level information onto the schema for + // the type in general. + preserveFields(f.currentSchema, f.originalField) + } + return f + } + + // if we get a type that's just a ref, resolve it + if baseSchema.Ref != nil && len(*baseSchema.Ref) > 0 { + // resolve this ref + refIdent, err := f.LookupReference(*baseSchema.Ref, f.currentPackage) + if err != nil { + f.currentPackage.AddError(err) + return nil + } + + // load and potentially flatten the schema + + // check the cache first... + if refSchemaCached, isCached := f.flattenedTypes[refIdent]; isCached { + // shallow copy is fine, it's just to avoid overwriting the doc fields + preserveFields(&refSchemaCached, *baseSchema) + *baseSchema = refSchemaCached + return nil // don't recurse, we're done + } + + // ...otherwise, we need to flatten + refSchema, err := f.loadUnflattenedSchema(refIdent) + if err != nil { + f.currentPackage.AddError(err) + return nil + } + refSchema = refSchema.DeepCopy() + + // keep field around to preserve field-level validation, docs, etc + origField := *baseSchema + *baseSchema = *refSchema + + // avoid loops (which shouldn't exist, but just in case) + // by marking a nil cached pointer before we start recursing + f.cacheType(refIdent, apiext.JSONSchemaProps{}) + + return &flattenVisitor{ + Flattener: f.Flattener, + + currentPackage: refIdent.Package, + currentType: &refIdent, + currentSchema: baseSchema, + originalField: origField, + } + } + + // otherwise, continue recursing... + if f.currentType != nil { + // ...but don't accidentally end this node early (for caching purposes) + return &flattenVisitor{ + Flattener: f.Flattener, + currentPackage: f.currentPackage, + } + } + + return f +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/crd/gen.go b/vendor/sigs.k8s.io/controller-tools/pkg/crd/gen.go new file mode 100644 index 0000000000..4385a5fe60 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/crd/gen.go @@ -0,0 +1,264 @@ +/* +Copyright 2018 The Kubernetes 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 crd + +import ( + "fmt" + "go/types" + + apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + apiextlegacy "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" + "k8s.io/apimachinery/pkg/runtime/schema" + + crdmarkers "sigs.k8s.io/controller-tools/pkg/crd/markers" + "sigs.k8s.io/controller-tools/pkg/genall" + "sigs.k8s.io/controller-tools/pkg/loader" + "sigs.k8s.io/controller-tools/pkg/markers" + "sigs.k8s.io/controller-tools/pkg/version" +) + +// +controllertools:marker:generateHelp + +// Generator generates CustomResourceDefinition objects. +type Generator struct { + // TrivialVersions indicates that we should produce a single-version CRD. + // + // Single "trivial-version" CRDs are compatible with older (pre 1.13) + // Kubernetes API servers. The storage version's schema will be used as + // the CRD's schema. + // + // Only works with the v1beta1 CRD version. + TrivialVersions bool `marker:",optional"` + + // PreserveUnknownFields indicates whether or not we should turn off pruning. + // + // Left unspecified, it'll default to true when only a v1beta1 CRD is + // generated (to preserve compatibility with older versions of this tool), + // or false otherwise. + // + // It's required to be false for v1 CRDs. + PreserveUnknownFields *bool `marker:",optional"` + + // MaxDescLen specifies the maximum description length for fields in CRD's OpenAPI schema. + // + // 0 indicates drop the description for all fields completely. + // n indicates limit the description to at most n characters and truncate the description to + // closest sentence boundary if it exceeds n characters. + MaxDescLen *int `marker:",optional"` + + // CRDVersions specifies the target API versions of the CRD type itself to + // generate. Defaults to v1beta1. + // + // The first version listed will be assumed to be the "default" version and + // will not get a version suffix in the output filename. + // + // You'll need to use "v1" to get support for features like defaulting, + // along with an API server that supports it (Kubernetes 1.16+). + CRDVersions []string `marker:"crdVersions,optional"` +} + +func (Generator) RegisterMarkers(into *markers.Registry) error { + return crdmarkers.Register(into) +} +func (g Generator) Generate(ctx *genall.GenerationContext) error { + parser := &Parser{ + Collector: ctx.Collector, + Checker: ctx.Checker, + } + + AddKnownTypes(parser) + for _, root := range ctx.Roots { + parser.NeedPackage(root) + } + + metav1Pkg := FindMetav1(ctx.Roots) + if metav1Pkg == nil { + // no objects in the roots, since nothing imported metav1 + return nil + } + + // TODO: allow selecting a specific object + kubeKinds := FindKubeKinds(parser, metav1Pkg) + if len(kubeKinds) == 0 { + // no objects in the roots + return nil + } + + crdVersions := g.CRDVersions + + if len(crdVersions) == 0 { + crdVersions = []string{"v1beta1"} + } + + for _, groupKind := range kubeKinds { + parser.NeedCRDFor(groupKind, g.MaxDescLen) + crdRaw := parser.CustomResourceDefinitions[groupKind] + addAttribution(&crdRaw) + + versionedCRDs := make([]interface{}, len(crdVersions)) + for i, ver := range crdVersions { + conv, err := AsVersion(crdRaw, schema.GroupVersion{Group: apiext.SchemeGroupVersion.Group, Version: ver}) + if err != nil { + return err + } + versionedCRDs[i] = conv + } + + if g.TrivialVersions { + for i, crd := range versionedCRDs { + if crdVersions[i] == "v1beta1" { + toTrivialVersions(crd.(*apiextlegacy.CustomResourceDefinition)) + } + } + } + + // *If* we're only generating v1beta1 CRDs, default to `preserveUnknownFields: (unset)` + // for compatibility purposes. In any other case, default to false, since that's + // the sensible default and is required for v1. + v1beta1Only := len(crdVersions) == 1 && crdVersions[0] == "v1beta1" + switch { + case (g.PreserveUnknownFields == nil || *g.PreserveUnknownFields) && v1beta1Only: + crd := versionedCRDs[0].(*apiextlegacy.CustomResourceDefinition) + crd.Spec.PreserveUnknownFields = nil + case g.PreserveUnknownFields == nil, g.PreserveUnknownFields != nil && !*g.PreserveUnknownFields: + // it'll be false here (coming from v1) -- leave it as such + default: + return fmt.Errorf("you may only set PreserveUnknownFields to true with v1beta1 CRDs") + } + + for i, crd := range versionedCRDs { + var fileName string + if i == 0 { + fileName = fmt.Sprintf("%s_%s.yaml", crdRaw.Spec.Group, crdRaw.Spec.Names.Plural) + } else { + fileName = fmt.Sprintf("%s_%s.%s.yaml", crdRaw.Spec.Group, crdRaw.Spec.Names.Plural, crdVersions[i]) + } + if err := ctx.WriteYAML(fileName, crd); err != nil { + return err + } + } + } + + return nil +} + +// toTrivialVersions strips out all schemata except for the storage schema, +// and moves that up into the root object. This makes the CRD compatible +// with pre 1.13 clusters. +func toTrivialVersions(crd *apiextlegacy.CustomResourceDefinition) { + var canonicalSchema *apiextlegacy.CustomResourceValidation + var canonicalSubresources *apiextlegacy.CustomResourceSubresources + var canonicalColumns []apiextlegacy.CustomResourceColumnDefinition + for i, ver := range crd.Spec.Versions { + if ver.Storage == true { + canonicalSchema = ver.Schema + canonicalSubresources = ver.Subresources + canonicalColumns = ver.AdditionalPrinterColumns + } + crd.Spec.Versions[i].Schema = nil + crd.Spec.Versions[i].Subresources = nil + crd.Spec.Versions[i].AdditionalPrinterColumns = nil + } + if canonicalSchema == nil { + return + } + + crd.Spec.Validation = canonicalSchema + crd.Spec.Subresources = canonicalSubresources + crd.Spec.AdditionalPrinterColumns = canonicalColumns +} + +// addAttribution adds attribution info to indicate controller-gen tool was used +// to generate this CRD definition along with the version info. +func addAttribution(crd *apiext.CustomResourceDefinition) { + if crd.ObjectMeta.Annotations == nil { + crd.ObjectMeta.Annotations = map[string]string{} + } + crd.ObjectMeta.Annotations["controller-gen.kubebuilder.io/version"] = version.Version() +} + +// FindMetav1 locates the actual package representing metav1 amongst +// the imports of the roots. +func FindMetav1(roots []*loader.Package) *loader.Package { + for _, root := range roots { + pkg := root.Imports()["k8s.io/apimachinery/pkg/apis/meta/v1"] + if pkg != nil { + return pkg + } + } + return nil +} + +// FindKubeKinds locates all types that contain TypeMeta and ObjectMeta +// (and thus may be a Kubernetes object), and returns the corresponding +// group-kinds. +func FindKubeKinds(parser *Parser, metav1Pkg *loader.Package) []schema.GroupKind { + // TODO(directxman12): technically, we should be finding metav1 per-package + var kubeKinds []schema.GroupKind + for typeIdent, info := range parser.Types { + hasObjectMeta := false + hasTypeMeta := false + + pkg := typeIdent.Package + pkg.NeedTypesInfo() + typesInfo := pkg.TypesInfo + + for _, field := range info.Fields { + if field.Name != "" { + // type and object meta are embedded, + // so they can't be this + continue + } + + fieldType := typesInfo.TypeOf(field.RawField.Type) + namedField, isNamed := fieldType.(*types.Named) + if !isNamed { + // ObjectMeta and TypeMeta are named types + continue + } + if namedField.Obj().Pkg() == nil { + // Embedded non-builtin universe type (specifically, it's probably `error`), + // so it can't be ObjectMeta or TypeMeta + continue + } + fieldPkgPath := loader.NonVendorPath(namedField.Obj().Pkg().Path()) + fieldPkg := pkg.Imports()[fieldPkgPath] + if fieldPkg != metav1Pkg { + continue + } + + switch namedField.Obj().Name() { + case "ObjectMeta": + hasObjectMeta = true + case "TypeMeta": + hasTypeMeta = true + } + } + + if !hasObjectMeta || !hasTypeMeta { + continue + } + + groupKind := schema.GroupKind{ + Group: parser.GroupVersions[pkg].Group, + Kind: typeIdent.Name, + } + kubeKinds = append(kubeKinds, groupKind) + } + + return kubeKinds +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/crd/known_types.go b/vendor/sigs.k8s.io/controller-tools/pkg/crd/known_types.go new file mode 100644 index 0000000000..fafd81b368 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/crd/known_types.go @@ -0,0 +1,115 @@ +/* +Copyright 2019 The Kubernetes 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 crd + +import ( + apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + + "sigs.k8s.io/controller-tools/pkg/loader" +) + +// KnownPackages overrides types in some comment packages that have custom validation +// but don't have validation markers on them (since they're from core Kubernetes). +var KnownPackages = map[string]PackageOverride{ + + "k8s.io/apimachinery/pkg/apis/meta/v1": func(p *Parser, pkg *loader.Package) { + // ObjectMeta is managed by the Kubernetes API server, so no need to + // generate validation for it. + p.Schemata[TypeIdent{Name: "ObjectMeta", Package: pkg}] = apiext.JSONSchemaProps{ + Type: "object", + } + p.Schemata[TypeIdent{Name: "Time", Package: pkg}] = apiext.JSONSchemaProps{ + Type: "string", + Format: "date-time", + } + p.Schemata[TypeIdent{Name: "MicroTime", Package: pkg}] = apiext.JSONSchemaProps{ + Type: "string", + Format: "date-time", + } + p.Schemata[TypeIdent{Name: "Duration", Package: pkg}] = apiext.JSONSchemaProps{ + // TODO(directxman12): regexp validation for this (or get kube to support it as a format value) + Type: "string", + } + p.Schemata[TypeIdent{Name: "Fields", Package: pkg}] = apiext.JSONSchemaProps{ + // this is a recursive structure that can't be flattened or, for that matter, properly generated. + // so just treat it as an arbitrary map + Type: "object", + AdditionalProperties: &apiext.JSONSchemaPropsOrBool{Allows: true}, + } + p.AddPackage(pkg) // get the rest of the types + }, + + "k8s.io/apimachinery/pkg/api/resource": func(p *Parser, pkg *loader.Package) { + p.Schemata[TypeIdent{Name: "Quantity", Package: pkg}] = apiext.JSONSchemaProps{ + // TODO(directxman12): regexp validation for this (or get kube to support it as a format value) + Type: "string", + } + // No point in calling AddPackage, this is the sole inhabitant + }, + + "k8s.io/apimachinery/pkg/runtime": func(p *Parser, pkg *loader.Package) { + p.Schemata[TypeIdent{Name: "RawExtension", Package: pkg}] = apiext.JSONSchemaProps{ + // TODO(directxman12): regexp validation for this (or get kube to support it as a format value) + Type: "object", + } + p.AddPackage(pkg) // get the rest of the types + }, + + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured": func(p *Parser, pkg *loader.Package) { + p.Schemata[TypeIdent{Name: "Unstructured", Package: pkg}] = apiext.JSONSchemaProps{ + Type: "object", + } + p.AddPackage(pkg) // get the rest of the types + }, + + "k8s.io/apimachinery/pkg/util/intstr": func(p *Parser, pkg *loader.Package) { + p.Schemata[TypeIdent{Name: "IntOrString", Package: pkg}] = apiext.JSONSchemaProps{ + XIntOrString: true, + AnyOf: []apiext.JSONSchemaProps{ + {Type: "integer"}, + {Type: "string"}, + }, + } + // No point in calling AddPackage, this is the sole inhabitant + }, + + "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1": func(p *Parser, pkg *loader.Package) { + p.Schemata[TypeIdent{Name: "JSON", Package: pkg}] = apiext.JSONSchemaProps{ + XPreserveUnknownFields: boolPtr(true), + } + p.AddPackage(pkg) // get the rest of the types + }, + "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1": func(p *Parser, pkg *loader.Package) { + p.Schemata[TypeIdent{Name: "JSON", Package: pkg}] = apiext.JSONSchemaProps{ + XPreserveUnknownFields: boolPtr(true), + } + p.AddPackage(pkg) // get the rest of the types + }, +} + +func boolPtr(b bool) *bool { + return &b +} + +// AddKnownTypes registers the packages overrides in KnownPackages with the given parser. +func AddKnownTypes(parser *Parser) { + // ensure everything is there before adding to PackageOverrides + // TODO(directxman12): this is a bit of a hack, maybe just use constructors? + parser.init() + for pkgName, override := range KnownPackages { + parser.PackageOverrides[pkgName] = override + } +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/crd/markers/crd.go b/vendor/sigs.k8s.io/controller-tools/pkg/crd/markers/crd.go new file mode 100644 index 0000000000..1a82db76e4 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/crd/markers/crd.go @@ -0,0 +1,293 @@ +/* +Copyright 2019 The Kubernetes 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 markers + +import ( + "fmt" + + apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + + "sigs.k8s.io/controller-tools/pkg/markers" +) + +// CRDMarkers lists all markers that directly modify the CRD (not validation +// schemas). +var CRDMarkers = []*definitionWithHelp{ + // TODO(directxman12): more detailed help + must(markers.MakeDefinition("kubebuilder:subresource:status", markers.DescribesType, SubresourceStatus{})). + WithHelp(SubresourceStatus{}.Help()), + + must(markers.MakeDefinition("kubebuilder:subresource:scale", markers.DescribesType, SubresourceScale{})). + WithHelp(SubresourceScale{}.Help()), + + must(markers.MakeDefinition("kubebuilder:printcolumn", markers.DescribesType, PrintColumn{})). + WithHelp(PrintColumn{}.Help()), + + must(markers.MakeDefinition("kubebuilder:resource", markers.DescribesType, Resource{})). + WithHelp(Resource{}.Help()), + + must(markers.MakeDefinition("kubebuilder:storageversion", markers.DescribesType, StorageVersion{})). + WithHelp(StorageVersion{}.Help()), + + must(markers.MakeDefinition("kubebuilder:skipversion", markers.DescribesType, SkipVersion{})). + WithHelp(SkipVersion{}.Help()), +} + +// TODO: categories and singular used to be annotations types +// TODO: doc + +func init() { + AllDefinitions = append(AllDefinitions, CRDMarkers...) +} + +// +controllertools:marker:generateHelp:category=CRD + +// SubresourceStatus enables the "/status" subresource on a CRD. +type SubresourceStatus struct{} + +func (s SubresourceStatus) ApplyToCRD(crd *apiext.CustomResourceDefinitionSpec, version string) error { + var subresources *apiext.CustomResourceSubresources + for i := range crd.Versions { + ver := &crd.Versions[i] + if ver.Name != version { + continue + } + if ver.Subresources == nil { + ver.Subresources = &apiext.CustomResourceSubresources{} + } + subresources = ver.Subresources + break + } + if subresources == nil { + return fmt.Errorf("status subresource applied to version %q not in CRD", version) + } + subresources.Status = &apiext.CustomResourceSubresourceStatus{} + return nil +} + +// +controllertools:marker:generateHelp:category=CRD + +// SubresourceScale enables the "/scale" subresource on a CRD. +type SubresourceScale struct { + // marker names are leftover legacy cruft + + // SpecPath specifies the jsonpath to the replicas field for the scale's spec. + SpecPath string `marker:"specpath"` + + // StatusPath specifies the jsonpath to the replicas field for the scale's status. + StatusPath string `marker:"statuspath"` + + // SelectorPath specifies the jsonpath to the pod label selector field for the scale's status. + // + // The selector field must be the *string* form (serialized form) of a selector. + // Setting a pod label selector is necessary for your type to work with the HorizontalPodAutoscaler. + SelectorPath *string `marker:"selectorpath"` +} + +func (s SubresourceScale) ApplyToCRD(crd *apiext.CustomResourceDefinitionSpec, version string) error { + var subresources *apiext.CustomResourceSubresources + for i := range crd.Versions { + ver := &crd.Versions[i] + if ver.Name != version { + continue + } + if ver.Subresources == nil { + ver.Subresources = &apiext.CustomResourceSubresources{} + } + subresources = ver.Subresources + break + } + if subresources == nil { + return fmt.Errorf("scale subresource applied to version %q not in CRD", version) + } + subresources.Scale = &apiext.CustomResourceSubresourceScale{ + SpecReplicasPath: s.SpecPath, + StatusReplicasPath: s.StatusPath, + LabelSelectorPath: s.SelectorPath, + } + return nil +} + +// +controllertools:marker:generateHelp:category=CRD + +// StorageVersion marks this version as the "storage version" for the CRD for conversion. +// +// When conversion is enabled for a CRD (i.e. it's not a trivial-versions/single-version CRD), +// one version is set as the "storage version" to be stored in etcd. Attempting to store any +// other version will result in conversion to the storage version via a conversion webhook. +type StorageVersion struct{} + +func (s StorageVersion) ApplyToCRD(crd *apiext.CustomResourceDefinitionSpec, version string) error { + if version == "" { + // single-version, do nothing + return nil + } + // multi-version + for i := range crd.Versions { + ver := &crd.Versions[i] + if ver.Name != version { + continue + } + ver.Storage = true + break + } + return nil +} + +// +controllertools:marker:generateHelp:category=CRD + +// SkipVersion removes the particular version of the CRD from the CRDs spec. +// +// This is useful if you need to skip generating and listing version entries +// for 'internal' resource versions, which typically exist if using the +// Kubernetes upstream conversion-gen tool. +type SkipVersion struct{} + +func (s SkipVersion) ApplyToCRD(crd *apiext.CustomResourceDefinitionSpec, version string) error { + if version == "" { + // single-version, this is an invalid state + return fmt.Errorf("cannot skip a version if there is only a single version") + } + var versions []apiext.CustomResourceDefinitionVersion + // multi-version + for i := range crd.Versions { + ver := crd.Versions[i] + if ver.Name == version { + // skip the skipped version + continue + } + versions = append(versions, ver) + } + crd.Versions = versions + return nil +} + +// +controllertools:marker:generateHelp:category=CRD + +// PrintColumn adds a column to "kubectl get" output for this CRD. +type PrintColumn struct { + // Name specifies the name of the column. + Name string + + // Type indicates the type of the column. + // + // It may be any OpenAPI data type listed at + // https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types. + Type string + + // JSONPath specifies the jsonpath expression used to extract the value of the column. + JSONPath string `marker:"JSONPath"` // legacy cruft + + // Description specifies the help/description for this column. + Description string `marker:",optional"` + + // Format specifies the format of the column. + // + // It may be any OpenAPI data format corresponding to the type, listed at + // https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types. + Format string `marker:",optional"` + + // Priority indicates how important it is that this column be displayed. + // + // Lower priority (*higher* numbered) columns will be hidden if the terminal + // width is too small. + Priority int32 `marker:",optional"` +} + +func (s PrintColumn) ApplyToCRD(crd *apiext.CustomResourceDefinitionSpec, version string) error { + var columns *[]apiext.CustomResourceColumnDefinition + for i := range crd.Versions { + ver := &crd.Versions[i] + if ver.Name != version { + continue + } + if ver.Subresources == nil { + ver.Subresources = &apiext.CustomResourceSubresources{} + } + columns = &ver.AdditionalPrinterColumns + break + } + if columns == nil { + return fmt.Errorf("printer columns applied to version %q not in CRD", version) + } + + *columns = append(*columns, apiext.CustomResourceColumnDefinition{ + Name: s.Name, + Type: s.Type, + JSONPath: s.JSONPath, + Description: s.Description, + Format: s.Format, + Priority: s.Priority, + }) + + return nil +} + +// +controllertools:marker:generateHelp:category=CRD + +// Resource configures naming and scope for a CRD. +type Resource struct { + // Path specifies the plural "resource" for this CRD. + // + // It generally corresponds to a plural, lower-cased version of the Kind. + // See https://book.kubebuilder.io/cronjob-tutorial/gvks.html. + Path string `marker:",optional"` + + // ShortName specifies aliases for this CRD. + // + // Short names are often used when people have work with your resource + // over and over again. For instance, "rs" for "replicaset" or + // "crd" for customresourcedefinition. + ShortName []string `marker:",optional"` + + // Categories specifies which group aliases this resource is part of. + // + // Group aliases are used to work with groups of resources at once. + // The most common one is "all" which covers about a third of the base + // resources in Kubernetes, and is generally used for "user-facing" resources. + Categories []string `marker:",optional"` + + // Singular overrides the singular form of your resource. + // + // The singular form is otherwise defaulted off the plural (path). + Singular string `marker:",optional"` + + // Scope overrides the scope of the CRD (Cluster vs Namespaced). + // + // Scope defaults to "Namespaced". Cluster-scoped ("Cluster") resources + // don't exist in namespaces. + Scope string `marker:",optional"` +} + +func (s Resource) ApplyToCRD(crd *apiext.CustomResourceDefinitionSpec, version string) error { + if s.Path != "" { + crd.Names.Plural = s.Path + } + crd.Names.ShortNames = s.ShortName + crd.Names.Categories = s.Categories + + switch s.Scope { + case "": + crd.Scope = apiext.NamespaceScoped + default: + crd.Scope = apiext.ResourceScope(s.Scope) + } + + return nil +} + +// NB(directxman12): singular was historically distinct, so we keep it here for backwards compat diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/crd/markers/doc.go b/vendor/sigs.k8s.io/controller-tools/pkg/crd/markers/doc.go new file mode 100644 index 0000000000..48736401cf --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/crd/markers/doc.go @@ -0,0 +1,46 @@ +/* +Copyright 2019 The Kubernetes 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 markers defines markers for generating schema valiation +// and CRD structure. +// +// All markers related to CRD generation live in AllDefinitions. +// +// Validation Markers +// +// Validation markers have values that implement ApplyToSchema +// (crd.SchemaMarker). Any marker implementing this will automatically +// be run after the rest of a given schema node has been generated. +// Markers that need to be run before any other markers can also +// implement ApplyFirst, but this is discouraged and may change +// in the future. +// +// All validation markers start with "+kubebuilder:validation", and +// have the same name as their type name. +// +// CRD Markers +// +// Markers that modify anything in the CRD itself *except* for the schema +// implement ApplyToCRD (crd.CRDMarker). They are expected to detect whether +// they should apply themselves to a specific version in the CRD (as passed to +// them), or to the root-level CRD for legacy cases. They are applied *after* +// the rest of the CRD is computed. +// +// Misc +// +// This package also defines the "+groupName" and "+versionName" package-level +// markers, for defining package<->group-version mappings. +package markers diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/crd/markers/package.go b/vendor/sigs.k8s.io/controller-tools/pkg/crd/markers/package.go new file mode 100644 index 0000000000..cebe8fa4b1 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/crd/markers/package.go @@ -0,0 +1,40 @@ +/* +Copyright 2019 The Kubernetes 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 markers + +import ( + "sigs.k8s.io/controller-tools/pkg/markers" +) + +func init() { + AllDefinitions = append(AllDefinitions, + must(markers.MakeDefinition("groupName", markers.DescribesPackage, "")). + WithHelp(markers.SimpleHelp("CRD", "specifies the API group name for this package.")), + + must(markers.MakeDefinition("versionName", markers.DescribesPackage, "")). + WithHelp(markers.SimpleHelp("CRD", "overrides the API group version for this package (defaults to the package name).")), + + must(markers.MakeDefinition("kubebuilder:validation:Optional", markers.DescribesPackage, struct{}{})). + WithHelp(markers.SimpleHelp("CRD validation", "specifies that all fields in this package are optional by default.")), + + must(markers.MakeDefinition("kubebuilder:validation:Required", markers.DescribesPackage, struct{}{})). + WithHelp(markers.SimpleHelp("CRD validation", "specifies that all fields in this package are required by default.")), + + must(markers.MakeDefinition("kubebuilder:skip", markers.DescribesPackage, struct{}{})). + WithHelp(markers.SimpleHelp("CRD", "don't consider this package as an API version.")), + ) +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/crd/markers/register.go b/vendor/sigs.k8s.io/controller-tools/pkg/crd/markers/register.go new file mode 100644 index 0000000000..0e7c426942 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/crd/markers/register.go @@ -0,0 +1,83 @@ +/* +Copyright 2019 The Kubernetes 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 markers + +import ( + "reflect" + + "sigs.k8s.io/controller-tools/pkg/markers" +) + +type definitionWithHelp struct { + *markers.Definition + Help *markers.DefinitionHelp +} + +func (d *definitionWithHelp) WithHelp(help *markers.DefinitionHelp) *definitionWithHelp { + d.Help = help + return d +} + +func (d *definitionWithHelp) Register(reg *markers.Registry) error { + if err := reg.Register(d.Definition); err != nil { + return err + } + if d.Help != nil { + reg.AddHelp(d.Definition, d.Help) + } + return nil +} + +func must(def *markers.Definition, err error) *definitionWithHelp { + return &definitionWithHelp{ + Definition: markers.Must(def, err), + } +} + +// AllDefinitions contains all marker definitions for this package. +var AllDefinitions []*definitionWithHelp + +type hasHelp interface { + Help() *markers.DefinitionHelp +} + +// mustMakeAllWithPrefix converts each object into a marker definition using +// the object's type's with the prefix to form the marker name. +func mustMakeAllWithPrefix(prefix string, target markers.TargetType, objs ...interface{}) []*definitionWithHelp { + defs := make([]*definitionWithHelp, len(objs)) + for i, obj := range objs { + name := prefix + ":" + reflect.TypeOf(obj).Name() + def, err := markers.MakeDefinition(name, target, obj) + if err != nil { + panic(err) + } + defs[i] = &definitionWithHelp{Definition: def, Help: obj.(hasHelp).Help()} + } + + return defs +} + +// Register registers all definitions for CRD generation to the given registry. +func Register(reg *markers.Registry) error { + for _, def := range AllDefinitions { + if err := def.Register(reg); err != nil { + return err + } + } + + return nil +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/crd/markers/validation.go b/vendor/sigs.k8s.io/controller-tools/pkg/crd/markers/validation.go new file mode 100644 index 0000000000..d765e6d921 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/crd/markers/validation.go @@ -0,0 +1,350 @@ +/* +Copyright 2019 The Kubernetes 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 markers + +import ( + "fmt" + + "encoding/json" + + apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + + "sigs.k8s.io/controller-tools/pkg/markers" +) + +// ValidationMarkers lists all available markers that affect CRD schema generation, +// except for the few that don't make sense as type-level markers (see FieldOnlyMarkers). +// All markers start with `+kubebuilder:validation:`, and continue with their type name. +// A copy is produced of all markers that describes types as well, for making types +// reusable and writing complex validations on slice items. +var ValidationMarkers = mustMakeAllWithPrefix("kubebuilder:validation", markers.DescribesField, + + // integer markers + + Maximum(0), + Minimum(0), + ExclusiveMaximum(false), + ExclusiveMinimum(false), + MultipleOf(0), + + // string markers + + MaxLength(0), + MinLength(0), + Pattern(""), + + // slice markers + + MaxItems(0), + MinItems(0), + UniqueItems(false), + + // general markers + + Enum(nil), + Format(""), + Type(""), + XPreserveUnknownFields{}, + XEmbeddedResource{}, +) + +// FieldOnlyMarkers list field-specific validation markers (i.e. those markers that don't make +// sense on a type, and thus aren't in ValidationMarkers). +var FieldOnlyMarkers = []*definitionWithHelp{ + must(markers.MakeDefinition("kubebuilder:validation:Required", markers.DescribesField, struct{}{})). + WithHelp(markers.SimpleHelp("CRD validation", "specifies that this field is required, if fields are optional by default.")), + must(markers.MakeDefinition("kubebuilder:validation:Optional", markers.DescribesField, struct{}{})). + WithHelp(markers.SimpleHelp("CRD validation", "specifies that this field is optional, if fields are required by default.")), + must(markers.MakeDefinition("optional", markers.DescribesField, struct{}{})). + WithHelp(markers.SimpleHelp("CRD validation", "specifies that this field is optional, if fields are required by default.")), + + must(markers.MakeDefinition("nullable", markers.DescribesField, Nullable{})). + WithHelp(Nullable{}.Help()), + + must(markers.MakeAnyTypeDefinition("kubebuilder:default", markers.DescribesField, Default{})). + WithHelp(Default{}.Help()), + + must(markers.MakeDefinition("kubebuilder:pruning:PreserveUnknownFields", markers.DescribesField, XPreserveUnknownFields{})). + WithHelp(XPreserveUnknownFields{}.Help()), + must(markers.MakeDefinition("kubebuilder:validation:EmbeddedResource", markers.DescribesField, XEmbeddedResource{})). + WithHelp(XEmbeddedResource{}.Help()), +} + +func init() { + AllDefinitions = append(AllDefinitions, ValidationMarkers...) + + for _, def := range ValidationMarkers { + newDef := *def.Definition + // copy both parts so we don't change the definition + typDef := definitionWithHelp{ + Definition: &newDef, + Help: def.Help, + } + typDef.Target = markers.DescribesType + AllDefinitions = append(AllDefinitions, &typDef) + } + + AllDefinitions = append(AllDefinitions, FieldOnlyMarkers...) +} + +// +controllertools:marker:generateHelp:category="CRD validation" +// Maximum specifies the maximum numeric value that this field can have. +type Maximum int + +// +controllertools:marker:generateHelp:category="CRD validation" +// Minimum specifies the minimum numeric value that this field can have. +type Minimum int + +// +controllertools:marker:generateHelp:category="CRD validation" +// ExclusiveMinimum indicates that the minimum is "up to" but not including that value. +type ExclusiveMinimum bool + +// +controllertools:marker:generateHelp:category="CRD validation" +// ExclusiveMaximum indicates that the maximum is "up to" but not including that value. +type ExclusiveMaximum bool + +// +controllertools:marker:generateHelp:category="CRD validation" +// MultipleOf specifies that this field must have a numeric value that's a multiple of this one. +type MultipleOf int + +// +controllertools:marker:generateHelp:category="CRD validation" +// MaxLength specifies the maximum length for this string. +type MaxLength int + +// +controllertools:marker:generateHelp:category="CRD validation" +// MinLength specifies the minimum length for this string. +type MinLength int + +// +controllertools:marker:generateHelp:category="CRD validation" +// Pattern specifies that this string must match the given regular expression. +type Pattern string + +// +controllertools:marker:generateHelp:category="CRD validation" +// MaxItems specifies the maximum length for this list. +type MaxItems int + +// +controllertools:marker:generateHelp:category="CRD validation" +// MinItems specifies the minimun length for this list. +type MinItems int + +// +controllertools:marker:generateHelp:category="CRD validation" +// UniqueItems specifies that all items in this list must be unique. +type UniqueItems bool + +// +controllertools:marker:generateHelp:category="CRD validation" +// Enum specifies that this (scalar) field is restricted to the *exact* values specified here. +type Enum []interface{} + +// +controllertools:marker:generateHelp:category="CRD validation" +// Format specifies additional "complex" formatting for this field. +// +// For example, a date-time field would be marked as "type: string" and +// "format: date-time". +type Format string + +// +controllertools:marker:generateHelp:category="CRD validation" +// Type overrides the type for this field (which defaults to the equivalent of the Go type). +// +// This generally must be paired with custom serialization. For example, the +// metav1.Time field would be marked as "type: string" and "format: date-time". +type Type string + +// +controllertools:marker:generateHelp:category="CRD validation" +// Nullable marks this field as allowing the "null" value. +// +// This is often not necessary, but may be helpful with custom serialization. +type Nullable struct{} + +// +controllertools:marker:generateHelp:category="CRD validation" +// Default sets the default value for this field. +// +// A default value will be accepted as any value valid for the +// field. Formatting for common types include: boolean: `true`, string: +// `Cluster`, numerical: `1.24`, array: `{1,2}`, object: `{policy: +// "delete"}`). Defaults should be defined in pruned form, and only best-effort +// validation will be performed. Full validation of a default requires +// submission of the containing CRD to an apiserver. +type Default struct { + Value interface{} +} + +// +controllertools:marker:generateHelp:category="CRD processing" +// PreserveUnknownFields stops the apiserver from pruning fields which are not specified. +// +// By default the apiserver drops unknown fields from the request payload +// during the decoding step. This marker stops the API server from doing so. +// It affects fields recursively, but switches back to normal pruning behaviour +// if nested properties or additionalProperties are specified in the schema. +// This can either be true or undefined. False +// is forbidden. +type XPreserveUnknownFields struct{} + +// +controllertools:marker:generateHelp:category="CRD validation" +// EmbeddedResource marks a fields as an embedded resource with apiVersion, kind and metadata fields. +// +// An embedded resource is a value that has apiVersion, kind and metadata fields. +// They are validated implicitly according to the semantics of the currently +// running apiserver. It is not necessary to add any additional schema for these +// field, yet it is possible. This can be combined with PreserveUnknownFields. +type XEmbeddedResource struct{} + +func (m Maximum) ApplyToSchema(schema *apiext.JSONSchemaProps) error { + if schema.Type != "integer" { + return fmt.Errorf("must apply maximum to an integer") + } + val := float64(m) + schema.Maximum = &val + return nil +} +func (m Minimum) ApplyToSchema(schema *apiext.JSONSchemaProps) error { + if schema.Type != "integer" { + return fmt.Errorf("must apply minimum to an integer") + } + val := float64(m) + schema.Minimum = &val + return nil +} +func (m ExclusiveMaximum) ApplyToSchema(schema *apiext.JSONSchemaProps) error { + if schema.Type != "integer" { + return fmt.Errorf("must apply exclusivemaximum to an integer") + } + schema.ExclusiveMaximum = bool(m) + return nil +} +func (m ExclusiveMinimum) ApplyToSchema(schema *apiext.JSONSchemaProps) error { + if schema.Type != "integer" { + return fmt.Errorf("must apply exclusiveminimum to an integer") + } + schema.ExclusiveMinimum = bool(m) + return nil +} +func (m MultipleOf) ApplyToSchema(schema *apiext.JSONSchemaProps) error { + if schema.Type != "integer" { + return fmt.Errorf("must apply multipleof to an integer") + } + val := float64(m) + schema.MultipleOf = &val + return nil +} + +func (m MaxLength) ApplyToSchema(schema *apiext.JSONSchemaProps) error { + if schema.Type != "string" { + return fmt.Errorf("must apply maxlength to a string") + } + val := int64(m) + schema.MaxLength = &val + return nil +} +func (m MinLength) ApplyToSchema(schema *apiext.JSONSchemaProps) error { + if schema.Type != "string" { + return fmt.Errorf("must apply minlength to a string") + } + val := int64(m) + schema.MinLength = &val + return nil +} +func (m Pattern) ApplyToSchema(schema *apiext.JSONSchemaProps) error { + if schema.Type != "string" { + return fmt.Errorf("must apply pattern to a string") + } + schema.Pattern = string(m) + return nil +} + +func (m MaxItems) ApplyToSchema(schema *apiext.JSONSchemaProps) error { + if schema.Type != "array" { + return fmt.Errorf("must apply maxitem to an array") + } + val := int64(m) + schema.MaxItems = &val + return nil +} +func (m MinItems) ApplyToSchema(schema *apiext.JSONSchemaProps) error { + if schema.Type != "array" { + return fmt.Errorf("must apply minitems to an array") + } + val := int64(m) + schema.MinItems = &val + return nil +} +func (m UniqueItems) ApplyToSchema(schema *apiext.JSONSchemaProps) error { + if schema.Type != "array" { + return fmt.Errorf("must apply uniqueitems to an array") + } + schema.UniqueItems = bool(m) + return nil +} + +func (m Enum) ApplyToSchema(schema *apiext.JSONSchemaProps) error { + // TODO(directxman12): this is a bit hacky -- we should + // probably support AnyType better + using the schema structure + vals := make([]apiext.JSON, len(m)) + for i, val := range m { + // TODO(directxman12): check actual type with schema type? + // if we're expecting a string, marshal the string properly... + // NB(directxman12): we use json.Marshal to ensure we handle JSON escaping properly + valMarshalled, err := json.Marshal(val) + if err != nil { + return err + } + vals[i] = apiext.JSON{Raw: valMarshalled} + } + schema.Enum = vals + return nil +} +func (m Format) ApplyToSchema(schema *apiext.JSONSchemaProps) error { + schema.Format = string(m) + return nil +} + +// NB(directxman12): we "typecheck" on target schema properties here, +// which means the "Type" marker *must* be applied first. +// TODO(directxman12): find a less hacky way to do this +// (we could preserve ordering of markers, but that feels bad in its own right). + +func (m Type) ApplyToSchema(schema *apiext.JSONSchemaProps) error { + schema.Type = string(m) + return nil +} + +func (m Type) ApplyFirst() {} + +func (m Nullable) ApplyToSchema(schema *apiext.JSONSchemaProps) error { + schema.Nullable = true + return nil +} + +// Defaults are only valid CRDs created with the v1 API +func (m Default) ApplyToSchema(schema *apiext.JSONSchemaProps) error { + marshalledDefault, err := json.Marshal(m.Value) + if err != nil { + return err + } + schema.Default = &apiext.JSON{Raw: marshalledDefault} + return nil +} + +func (m XPreserveUnknownFields) ApplyToSchema(schema *apiext.JSONSchemaProps) error { + defTrue := true + schema.XPreserveUnknownFields = &defTrue + return nil +} + +func (m XEmbeddedResource) ApplyToSchema(schema *apiext.JSONSchemaProps) error { + schema.XEmbeddedResource = true + return nil +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/crd/markers/zz_generated.markerhelp.go b/vendor/sigs.k8s.io/controller-tools/pkg/crd/markers/zz_generated.markerhelp.go new file mode 100644 index 0000000000..aeb8bec642 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/crd/markers/zz_generated.markerhelp.go @@ -0,0 +1,353 @@ +// +build !ignore_autogenerated + +/* +Copyright2019 The Kubernetes 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. +*/ + +// Code generated by helpgen. DO NOT EDIT. + +package markers + +import ( + "sigs.k8s.io/controller-tools/pkg/markers" +) + +func (Default) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "CRD validation", + DetailedHelp: markers.DetailedHelp{ + Summary: "sets the default value for this field. ", + Details: "A default value will be accepted as any value valid for the field. Formatting for common types include: boolean: `true`, string: `Cluster`, numerical: `1.24`, array: `{1,2}`, object: `{policy: \"delete\"}`). Defaults should be defined in pruned form, and only best-effort validation will be performed. Full validation of a default requires submission of the containing CRD to an apiserver.", + }, + FieldHelp: map[string]markers.DetailedHelp{ + "Value": markers.DetailedHelp{ + Summary: "", + Details: "", + }, + }, + } +} + +func (Enum) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "CRD validation", + DetailedHelp: markers.DetailedHelp{ + Summary: "specifies that this (scalar) field is restricted to the *exact* values specified here.", + Details: "", + }, + FieldHelp: map[string]markers.DetailedHelp{}, + } +} + +func (ExclusiveMaximum) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "CRD validation", + DetailedHelp: markers.DetailedHelp{ + Summary: "indicates that the maximum is \"up to\" but not including that value.", + Details: "", + }, + FieldHelp: map[string]markers.DetailedHelp{}, + } +} + +func (ExclusiveMinimum) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "CRD validation", + DetailedHelp: markers.DetailedHelp{ + Summary: "indicates that the minimum is \"up to\" but not including that value.", + Details: "", + }, + FieldHelp: map[string]markers.DetailedHelp{}, + } +} + +func (Format) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "CRD validation", + DetailedHelp: markers.DetailedHelp{ + Summary: "specifies additional \"complex\" formatting for this field. ", + Details: "For example, a date-time field would be marked as \"type: string\" and \"format: date-time\".", + }, + FieldHelp: map[string]markers.DetailedHelp{}, + } +} + +func (MaxItems) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "CRD validation", + DetailedHelp: markers.DetailedHelp{ + Summary: "specifies the maximum length for this list.", + Details: "", + }, + FieldHelp: map[string]markers.DetailedHelp{}, + } +} + +func (MaxLength) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "CRD validation", + DetailedHelp: markers.DetailedHelp{ + Summary: "specifies the maximum length for this string.", + Details: "", + }, + FieldHelp: map[string]markers.DetailedHelp{}, + } +} + +func (Maximum) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "CRD validation", + DetailedHelp: markers.DetailedHelp{ + Summary: "specifies the maximum numeric value that this field can have.", + Details: "", + }, + FieldHelp: map[string]markers.DetailedHelp{}, + } +} + +func (MinItems) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "CRD validation", + DetailedHelp: markers.DetailedHelp{ + Summary: "specifies the minimun length for this list.", + Details: "", + }, + FieldHelp: map[string]markers.DetailedHelp{}, + } +} + +func (MinLength) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "CRD validation", + DetailedHelp: markers.DetailedHelp{ + Summary: "specifies the minimum length for this string.", + Details: "", + }, + FieldHelp: map[string]markers.DetailedHelp{}, + } +} + +func (Minimum) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "CRD validation", + DetailedHelp: markers.DetailedHelp{ + Summary: "specifies the minimum numeric value that this field can have.", + Details: "", + }, + FieldHelp: map[string]markers.DetailedHelp{}, + } +} + +func (MultipleOf) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "CRD validation", + DetailedHelp: markers.DetailedHelp{ + Summary: "specifies that this field must have a numeric value that's a multiple of this one.", + Details: "", + }, + FieldHelp: map[string]markers.DetailedHelp{}, + } +} + +func (Nullable) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "CRD validation", + DetailedHelp: markers.DetailedHelp{ + Summary: "marks this field as allowing the \"null\" value. ", + Details: "This is often not necessary, but may be helpful with custom serialization.", + }, + FieldHelp: map[string]markers.DetailedHelp{}, + } +} + +func (Pattern) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "CRD validation", + DetailedHelp: markers.DetailedHelp{ + Summary: "specifies that this string must match the given regular expression.", + Details: "", + }, + FieldHelp: map[string]markers.DetailedHelp{}, + } +} + +func (PrintColumn) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "CRD", + DetailedHelp: markers.DetailedHelp{ + Summary: "adds a column to \"kubectl get\" output for this CRD.", + Details: "", + }, + FieldHelp: map[string]markers.DetailedHelp{ + "Name": markers.DetailedHelp{ + Summary: "specifies the name of the column.", + Details: "", + }, + "Type": markers.DetailedHelp{ + Summary: "indicates the type of the column. ", + Details: "It may be any OpenAPI data type listed at https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types.", + }, + "JSONPath": markers.DetailedHelp{ + Summary: "specifies the jsonpath expression used to extract the value of the column.", + Details: "", + }, + "Description": markers.DetailedHelp{ + Summary: "specifies the help/description for this column.", + Details: "", + }, + "Format": markers.DetailedHelp{ + Summary: "specifies the format of the column. ", + Details: "It may be any OpenAPI data format corresponding to the type, listed at https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types.", + }, + "Priority": markers.DetailedHelp{ + Summary: "indicates how important it is that this column be displayed. ", + Details: "Lower priority (*higher* numbered) columns will be hidden if the terminal width is too small.", + }, + }, + } +} + +func (Resource) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "CRD", + DetailedHelp: markers.DetailedHelp{ + Summary: "configures naming and scope for a CRD.", + Details: "", + }, + FieldHelp: map[string]markers.DetailedHelp{ + "Path": markers.DetailedHelp{ + Summary: "specifies the plural \"resource\" for this CRD. ", + Details: "It generally corresponds to a plural, lower-cased version of the Kind. See https://book.kubebuilder.io/cronjob-tutorial/gvks.html.", + }, + "ShortName": markers.DetailedHelp{ + Summary: "specifies aliases for this CRD. ", + Details: "Short names are often used when people have work with your resource over and over again. For instance, \"rs\" for \"replicaset\" or \"crd\" for customresourcedefinition.", + }, + "Categories": markers.DetailedHelp{ + Summary: "specifies which group aliases this resource is part of. ", + Details: "Group aliases are used to work with groups of resources at once. The most common one is \"all\" which covers about a third of the base resources in Kubernetes, and is generally used for \"user-facing\" resources.", + }, + "Singular": markers.DetailedHelp{ + Summary: "overrides the singular form of your resource. ", + Details: "The singular form is otherwise defaulted off the plural (path).", + }, + "Scope": markers.DetailedHelp{ + Summary: "overrides the scope of the CRD (cluster vs namespaced). ", + Details: "Scope defaults to \"namespaced\". Cluster-scoped (\"cluster\") resources don't exist in namespaces.", + }, + }, + } +} + +func (SkipVersion) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "CRD", + DetailedHelp: markers.DetailedHelp{ + Summary: "removes the particular version of the CRD from the CRDs spec. ", + Details: "This is useful if you need to skip generating and listing version entries for 'internal' resource versions, which typically exist if using the Kubernetes upstream conversion-gen tool.", + }, + FieldHelp: map[string]markers.DetailedHelp{}, + } +} + +func (StorageVersion) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "CRD", + DetailedHelp: markers.DetailedHelp{ + Summary: "marks this version as the \"storage version\" for the CRD for conversion. ", + Details: "When conversion is enabled for a CRD (i.e. it's not a trivial-versions/single-version CRD), one version is set as the \"storage version\" to be stored in etcd. Attempting to store any other version will result in conversion to the storage version via a conversion webhook.", + }, + FieldHelp: map[string]markers.DetailedHelp{}, + } +} + +func (SubresourceScale) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "CRD", + DetailedHelp: markers.DetailedHelp{ + Summary: "enables the \"/scale\" subresource on a CRD.", + Details: "", + }, + FieldHelp: map[string]markers.DetailedHelp{ + "SpecPath": markers.DetailedHelp{ + Summary: "specifies the jsonpath to the replicas field for the scale's spec.", + Details: "", + }, + "StatusPath": markers.DetailedHelp{ + Summary: "specifies the jsonpath to the replicas field for the scale's status.", + Details: "", + }, + "SelectorPath": markers.DetailedHelp{ + Summary: "specifies the jsonpath to the pod label selector field for the scale's status. ", + Details: "The selector field must be the *string* form (serialized form) of a selector. Setting a pod label selector is necessary for your type to work with the HorizontalPodAutoscaler.", + }, + }, + } +} + +func (SubresourceStatus) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "CRD", + DetailedHelp: markers.DetailedHelp{ + Summary: "enables the \"/status\" subresource on a CRD.", + Details: "", + }, + FieldHelp: map[string]markers.DetailedHelp{}, + } +} + +func (Type) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "CRD validation", + DetailedHelp: markers.DetailedHelp{ + Summary: "overrides the type for this field (which defaults to the equivalent of the Go type). ", + Details: "This generally must be paired with custom serialization. For example, the metav1.Time field would be marked as \"type: string\" and \"format: date-time\".", + }, + FieldHelp: map[string]markers.DetailedHelp{}, + } +} + +func (UniqueItems) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "CRD validation", + DetailedHelp: markers.DetailedHelp{ + Summary: "specifies that all items in this list must be unique.", + Details: "", + }, + FieldHelp: map[string]markers.DetailedHelp{}, + } +} + +func (XEmbeddedResource) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "CRD validation", + DetailedHelp: markers.DetailedHelp{ + Summary: "EmbeddedResource marks a fields as an embedded resource with apiVersion, kind and metadata fields. ", + Details: "An embedded resource is a value that has apiVersion, kind and metadata fields. They are validated implicitly according to the semantics of the currently running apiserver. It is not necessary to add any additional schema for these field, yet it is possible. This can be combined with PreserveUnknownFields.", + }, + FieldHelp: map[string]markers.DetailedHelp{}, + } +} + +func (XPreserveUnknownFields) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "CRD processing", + DetailedHelp: markers.DetailedHelp{ + Summary: "PreserveUnknownFields stops the apiserver from pruning fields which are not specified. ", + Details: "By default the apiserver drops unknown fields from the request payload during the decoding step. This marker stops the API server from doing so. It affects fields recursively, but switches back to normal pruning behaviour if nested properties or additionalProperties are specified in the schema. This can either be true or undefined. False is forbidden.", + }, + FieldHelp: map[string]markers.DetailedHelp{}, + } +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/crd/parser.go b/vendor/sigs.k8s.io/controller-tools/pkg/crd/parser.go new file mode 100644 index 0000000000..34398a8c72 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/crd/parser.go @@ -0,0 +1,243 @@ +/* +Copyright 2019 The Kubernetes 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 crd + +import ( + "fmt" + "go/ast" + + apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + + "sigs.k8s.io/controller-tools/pkg/loader" + "sigs.k8s.io/controller-tools/pkg/markers" +) + +// TypeIdent represents some type in a Package. +type TypeIdent struct { + Package *loader.Package + Name string +} + +func (t TypeIdent) String() string { + return fmt.Sprintf("%q.%s", t.Package.ID, t.Name) +} + +// PackageOverride overrides the loading of some package +// (potentially setting custom schemata, etc). It must +// call AddPackage if it wants to continue with the default +// loading behavior. +type PackageOverride func(p *Parser, pkg *loader.Package) + +// Parser knows how to parse out CRD information and generate +// OpenAPI schemata from some collection of types and markers. +// Most methods on Parser cache their results automatically, +// and thus may be called any number of times. +type Parser struct { + Collector *markers.Collector + + // Types contains the known TypeInfo for this parser. + Types map[TypeIdent]*markers.TypeInfo + // Schemata contains the known OpenAPI JSONSchemata for this parser. + Schemata map[TypeIdent]apiext.JSONSchemaProps + // GroupVersions contains the known group-versions of each package in this parser. + GroupVersions map[*loader.Package]schema.GroupVersion + // CustomResourceDefinitions contains the known CustomResourceDefinitions for types in this parser. + CustomResourceDefinitions map[schema.GroupKind]apiext.CustomResourceDefinition + // FlattenedSchemata contains fully flattened schemata for use in building + // CustomResourceDefinition validation. Each schema has been flattened by the flattener, + // and then embedded fields have been flattened with FlattenEmbedded. + FlattenedSchemata map[TypeIdent]apiext.JSONSchemaProps + + // PackageOverrides indicates that the loading of any package with + // the given path should be handled by the given overrider. + PackageOverrides map[string]PackageOverride + + // checker stores persistent partial type-checking/reference-traversal information. + Checker *loader.TypeChecker + // packages marks packages as loaded, to avoid re-loading them. + packages map[*loader.Package]struct{} + + flattener *Flattener +} + +func (p *Parser) init() { + if p.packages == nil { + p.packages = make(map[*loader.Package]struct{}) + } + if p.flattener == nil { + p.flattener = &Flattener{ + Parser: p, + } + } + if p.Schemata == nil { + p.Schemata = make(map[TypeIdent]apiext.JSONSchemaProps) + } + if p.Types == nil { + p.Types = make(map[TypeIdent]*markers.TypeInfo) + } + if p.PackageOverrides == nil { + p.PackageOverrides = make(map[string]PackageOverride) + } + if p.GroupVersions == nil { + p.GroupVersions = make(map[*loader.Package]schema.GroupVersion) + } + if p.CustomResourceDefinitions == nil { + p.CustomResourceDefinitions = make(map[schema.GroupKind]apiext.CustomResourceDefinition) + } + if p.FlattenedSchemata == nil { + p.FlattenedSchemata = make(map[TypeIdent]apiext.JSONSchemaProps) + } +} + +// indexTypes loads all types in the package into Types. +func (p *Parser) indexTypes(pkg *loader.Package) { + // autodetect + pkgMarkers, err := markers.PackageMarkers(p.Collector, pkg) + if err != nil { + pkg.AddError(err) + } else { + if skipPkg := pkgMarkers.Get("kubebuilder:skip"); skipPkg != nil { + return + } + if nameVal := pkgMarkers.Get("groupName"); nameVal != nil { + versionVal := pkg.Name // a reasonable guess + if versionMarker := pkgMarkers.Get("versionName"); versionMarker != nil { + versionVal = versionMarker.(string) + } + + p.GroupVersions[pkg] = schema.GroupVersion{ + Version: versionVal, + Group: nameVal.(string), + } + } + } + + if err := markers.EachType(p.Collector, pkg, func(info *markers.TypeInfo) { + ident := TypeIdent{ + Package: pkg, + Name: info.Name, + } + + p.Types[ident] = info + }); err != nil { + pkg.AddError(err) + } +} + +// LookupType fetches type info from Types. +func (p *Parser) LookupType(pkg *loader.Package, name string) *markers.TypeInfo { + return p.Types[TypeIdent{Package: pkg, Name: name}] +} + +// NeedSchemaFor indicates that a schema should be generated for the given type. +func (p *Parser) NeedSchemaFor(typ TypeIdent) { + p.init() + + p.NeedPackage(typ.Package) + if _, knownSchema := p.Schemata[typ]; knownSchema { + return + } + + info, knownInfo := p.Types[typ] + if !knownInfo { + typ.Package.AddError(fmt.Errorf("unknown type %s", typ)) + return + } + + // avoid tripping recursive schemata, like ManagedFields, by adding an empty WIP schema + p.Schemata[typ] = apiext.JSONSchemaProps{} + + schemaCtx := newSchemaContext(typ.Package, p) + ctxForInfo := schemaCtx.ForInfo(info) + + pkgMarkers, err := markers.PackageMarkers(p.Collector, typ.Package) + if err != nil { + typ.Package.AddError(err) + } + ctxForInfo.PackageMarkers = pkgMarkers + + schema := infoToSchema(ctxForInfo) + + p.Schemata[typ] = *schema +} + +func (p *Parser) NeedFlattenedSchemaFor(typ TypeIdent) { + p.init() + + if _, knownSchema := p.FlattenedSchemata[typ]; knownSchema { + return + } + + p.NeedSchemaFor(typ) + partialFlattened := p.flattener.FlattenType(typ) + fullyFlattened := FlattenEmbedded(partialFlattened, typ.Package) + + p.FlattenedSchemata[typ] = *fullyFlattened +} + +// NeedCRDFor lives off in spec.go + +// AddPackage indicates that types and type-checking information is needed +// for the the given package, *ignoring* overrides. +// Generally, consumers should call NeedPackage, while PackageOverrides should +// call AddPackage to continue with the normal loading procedure. +func (p *Parser) AddPackage(pkg *loader.Package) { + p.init() + if _, checked := p.packages[pkg]; checked { + return + } + p.indexTypes(pkg) + p.Checker.Check(pkg, filterTypesForCRDs) + p.packages[pkg] = struct{}{} +} + +// NeedPackage indicates that types and type-checking information +// is needed for the given package. +func (p *Parser) NeedPackage(pkg *loader.Package) { + p.init() + if _, checked := p.packages[pkg]; checked { + return + } + // overrides are going to be written without vendor. This is why we index by the actual + // object when we can. + if override, overridden := p.PackageOverrides[loader.NonVendorPath(pkg.PkgPath)]; overridden { + override(p, pkg) + p.packages[pkg] = struct{}{} + return + } + p.AddPackage(pkg) +} + +// filterTypesForCRDs filters out all nodes that aren't used in CRD generation, +// like interfaces and struct fields without JSON tag. +func filterTypesForCRDs(node ast.Node) bool { + switch node := node.(type) { + case *ast.InterfaceType: + // skip interfaces, we never care about references in them + return false + case *ast.StructType: + return true + case *ast.Field: + _, hasTag := loader.ParseAstTag(node.Tag).Lookup("json") + // fields without JSON tags mean we have custom serialization, + // so only visit fields with tags. + return hasTag + default: + return true + } +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/crd/schema.go b/vendor/sigs.k8s.io/controller-tools/pkg/crd/schema.go new file mode 100644 index 0000000000..89237e6a76 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/crd/schema.go @@ -0,0 +1,419 @@ +/* +Copyright 2019 The Kubernetes 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 crd + +import ( + "fmt" + "go/ast" + "go/types" + "strings" + + apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + + "sigs.k8s.io/controller-tools/pkg/loader" + "sigs.k8s.io/controller-tools/pkg/markers" +) + +// Schema flattening is done in a recursive mapping method. +// Start reading at infoToSchema. + +const ( + // defPrefix is the prefix used to link to definitions in the OpenAPI schema. + defPrefix = "#/definitions/" +) + +var ( + // byteType is the types.Type for byte (see the types documention + // for why we need to look this up in the Universe), saved + // for quick comparison. + byteType = types.Universe.Lookup("byte").Type() +) + +// SchemaMarker is any marker that needs to modify the schema of the underlying type or field. +type SchemaMarker interface { + // ApplyToSchema is called after the rest of the schema for a given type + // or field is generated, to modify the schema appropriately. + ApplyToSchema(*apiext.JSONSchemaProps) error +} + +// applyFirstMarker is applied before any other markers. It's a bit of a hack. +type applyFirstMarker interface { + ApplyFirst() +} + +// schemaRequester knows how to marker that another schema (e.g. via an external reference) is necessary. +type schemaRequester interface { + NeedSchemaFor(typ TypeIdent) +} + +// schemaContext stores and provides information across a hierarchy of schema generation. +type schemaContext struct { + pkg *loader.Package + info *markers.TypeInfo + + schemaRequester schemaRequester + PackageMarkers markers.MarkerValues +} + +// newSchemaContext constructs a new schemaContext for the given package and schema requester. +// It must have type info added before use via ForInfo. +func newSchemaContext(pkg *loader.Package, req schemaRequester) *schemaContext { + pkg.NeedTypesInfo() + return &schemaContext{ + pkg: pkg, + schemaRequester: req, + } +} + +// ForInfo produces a new schemaContext with containing the same information +// as this one, except with the given type information. +func (c *schemaContext) ForInfo(info *markers.TypeInfo) *schemaContext { + return &schemaContext{ + pkg: c.pkg, + info: info, + schemaRequester: c.schemaRequester, + } +} + +// requestSchema asks for the schema for a type in the package with the +// given import path. +func (c *schemaContext) requestSchema(pkgPath, typeName string) { + pkg := c.pkg + if pkgPath != "" { + pkg = c.pkg.Imports()[pkgPath] + } + c.schemaRequester.NeedSchemaFor(TypeIdent{ + Package: pkg, + Name: typeName, + }) +} + +// infoToSchema creates a schema for the type in the given set of type information. +func infoToSchema(ctx *schemaContext) *apiext.JSONSchemaProps { + return typeToSchema(ctx, ctx.info.RawSpec.Type) +} + +// applyMarkers applies schema markers to the given schema, respecting "apply first" markers. +func applyMarkers(ctx *schemaContext, markerSet markers.MarkerValues, props *apiext.JSONSchemaProps, node ast.Node) { + // apply "apply first" markers first... + for _, markerValues := range markerSet { + for _, markerValue := range markerValues { + if _, isApplyFirst := markerValue.(applyFirstMarker); !isApplyFirst { + continue + } + + schemaMarker, isSchemaMarker := markerValue.(SchemaMarker) + if !isSchemaMarker { + continue + } + + if err := schemaMarker.ApplyToSchema(props); err != nil { + ctx.pkg.AddError(loader.ErrFromNode(err /* an okay guess */, node)) + } + } + } + + // ...then the rest of the markers + for _, markerValues := range markerSet { + for _, markerValue := range markerValues { + if _, isApplyFirst := markerValue.(applyFirstMarker); isApplyFirst { + // skip apply-first markers, which were already applied + continue + } + + schemaMarker, isSchemaMarker := markerValue.(SchemaMarker) + if !isSchemaMarker { + continue + } + if err := schemaMarker.ApplyToSchema(props); err != nil { + ctx.pkg.AddError(loader.ErrFromNode(err /* an okay guess */, node)) + } + } + } +} + +// typeToSchema creates a schema for the given AST type. +func typeToSchema(ctx *schemaContext, rawType ast.Expr) *apiext.JSONSchemaProps { + var props *apiext.JSONSchemaProps + switch expr := rawType.(type) { + case *ast.Ident: + props = localNamedToSchema(ctx, expr) + case *ast.SelectorExpr: + props = namedToSchema(ctx, expr) + case *ast.ArrayType: + props = arrayToSchema(ctx, expr) + case *ast.MapType: + props = mapToSchema(ctx, expr) + case *ast.StarExpr: + props = typeToSchema(ctx, expr.X) + case *ast.StructType: + props = structToSchema(ctx, expr) + default: + ctx.pkg.AddError(loader.ErrFromNode(fmt.Errorf("unsupported AST kind %T", expr), rawType)) + // NB(directxman12): we explicitly don't handle interfaces + return &apiext.JSONSchemaProps{} + } + + props.Description = ctx.info.Doc + + applyMarkers(ctx, ctx.info.Markers, props, rawType) + + return props +} + +// qualifiedName constructs a JSONSchema-safe qualified name for a type +// (`` or `~0`, where `` +// is the package path with `/` replaced by `~1`, according to JSONPointer +// escapes). +func qualifiedName(pkgName, typeName string) string { + if pkgName != "" { + return strings.Replace(pkgName, "/", "~1", -1) + "~0" + typeName + } + return typeName +} + +// TypeRefLink creates a definition link for the given type and package. +func TypeRefLink(pkgName, typeName string) string { + return defPrefix + qualifiedName(pkgName, typeName) +} + +// localNamedToSchema creates a schema (ref) for a *potentially* local type reference +// (could be external from a dot-import). +func localNamedToSchema(ctx *schemaContext, ident *ast.Ident) *apiext.JSONSchemaProps { + typeInfo := ctx.pkg.TypesInfo.TypeOf(ident) + if typeInfo == types.Typ[types.Invalid] { + ctx.pkg.AddError(loader.ErrFromNode(fmt.Errorf("unknown type %s", ident.Name), ident)) + return &apiext.JSONSchemaProps{} + } + if basicInfo, isBasic := typeInfo.(*types.Basic); isBasic { + typ, fmt, err := builtinToType(basicInfo) + if err != nil { + ctx.pkg.AddError(loader.ErrFromNode(err, ident)) + } + return &apiext.JSONSchemaProps{ + Type: typ, + Format: fmt, + } + } + // NB(directxman12): if there are dot imports, this might be an external reference, + // so use typechecking info to get the actual object + typeNameInfo := typeInfo.(*types.Named).Obj() + pkg := typeNameInfo.Pkg() + pkgPath := loader.NonVendorPath(pkg.Path()) + if pkg == ctx.pkg.Types { + pkgPath = "" + } + ctx.requestSchema(pkgPath, typeNameInfo.Name()) + link := TypeRefLink(pkgPath, typeNameInfo.Name()) + return &apiext.JSONSchemaProps{ + Ref: &link, + } +} + +// namedSchema creates a schema (ref) for an explicitly external type reference. +func namedToSchema(ctx *schemaContext, named *ast.SelectorExpr) *apiext.JSONSchemaProps { + typeInfoRaw := ctx.pkg.TypesInfo.TypeOf(named) + if typeInfoRaw == types.Typ[types.Invalid] { + ctx.pkg.AddError(loader.ErrFromNode(fmt.Errorf("unknown type %v.%s", named.X, named.Sel.Name), named)) + return &apiext.JSONSchemaProps{} + } + typeInfo := typeInfoRaw.(*types.Named) + typeNameInfo := typeInfo.Obj() + nonVendorPath := loader.NonVendorPath(typeNameInfo.Pkg().Path()) + ctx.requestSchema(nonVendorPath, typeNameInfo.Name()) + link := TypeRefLink(nonVendorPath, typeNameInfo.Name()) + return &apiext.JSONSchemaProps{ + Ref: &link, + } + // NB(directxman12): we special-case things like resource.Quantity during the "collapse" phase. +} + +// arrayToSchema creates a schema for the items of the given array, dealing appropriately +// with the special `[]byte` type (according to OpenAPI standards). +func arrayToSchema(ctx *schemaContext, array *ast.ArrayType) *apiext.JSONSchemaProps { + eltType := ctx.pkg.TypesInfo.TypeOf(array.Elt) + if eltType == byteType && array.Len == nil { + // byte slices are represented as base64-encoded strings + // (the format is defined in OpenAPI v3, but not JSON Schema) + return &apiext.JSONSchemaProps{ + Type: "string", + Format: "byte", + } + } + // TODO(directxman12): backwards-compat would require access to markers from base info + items := typeToSchema(ctx.ForInfo(&markers.TypeInfo{}), array.Elt) + + return &apiext.JSONSchemaProps{ + Type: "array", + Items: &apiext.JSONSchemaPropsOrArray{Schema: items}, + } +} + +// mapToSchema creates a schema for items of the given map. Key types must eventually resolve +// to string (other types aren't allowed by JSON, and thus the kubernetes API standards). +func mapToSchema(ctx *schemaContext, mapType *ast.MapType) *apiext.JSONSchemaProps { + keyInfo := ctx.pkg.TypesInfo.TypeOf(mapType.Key) + // check that we've got a type that actually corresponds to a string + for keyInfo != nil { + switch typedKey := keyInfo.(type) { + case *types.Basic: + if typedKey.Info()&types.IsString == 0 { + ctx.pkg.AddError(loader.ErrFromNode(fmt.Errorf("map keys must be strings, not %s", keyInfo.String()), mapType.Key)) + return &apiext.JSONSchemaProps{} + } + keyInfo = nil // stop iterating + case *types.Named: + keyInfo = typedKey.Underlying() + default: + ctx.pkg.AddError(loader.ErrFromNode(fmt.Errorf("map keys must be strings, not %s", keyInfo.String()), mapType.Key)) + return &apiext.JSONSchemaProps{} + } + } + + // TODO(directxman12): backwards-compat would require access to markers from base info + var valSchema *apiext.JSONSchemaProps + switch val := mapType.Value.(type) { + case *ast.Ident: + valSchema = localNamedToSchema(ctx.ForInfo(&markers.TypeInfo{}), val) + case *ast.SelectorExpr: + valSchema = namedToSchema(ctx.ForInfo(&markers.TypeInfo{}), val) + case *ast.ArrayType: + valSchema = arrayToSchema(ctx.ForInfo(&markers.TypeInfo{}), val) + if valSchema.Type == "array" && valSchema.Items.Schema.Type != "string" { + ctx.pkg.AddError(loader.ErrFromNode(fmt.Errorf("map values must be a named type, not %T", mapType.Value), mapType.Value)) + return &apiext.JSONSchemaProps{} + } + default: + ctx.pkg.AddError(loader.ErrFromNode(fmt.Errorf("map values must be a named type, not %T", mapType.Value), mapType.Value)) + return &apiext.JSONSchemaProps{} + } + + return &apiext.JSONSchemaProps{ + Type: "object", + AdditionalProperties: &apiext.JSONSchemaPropsOrBool{ + Schema: valSchema, + Allows: true, /* set automatically by serialization, but useful for testing */ + }, + } +} + +// structToSchema creates a schema for the given struct. Embedded fields are placed in AllOf, +// and can be flattened later with a Flattener. +func structToSchema(ctx *schemaContext, structType *ast.StructType) *apiext.JSONSchemaProps { + props := &apiext.JSONSchemaProps{ + Type: "object", + Properties: make(map[string]apiext.JSONSchemaProps), + } + + if ctx.info.RawSpec.Type != structType { + ctx.pkg.AddError(loader.ErrFromNode(fmt.Errorf("encountered non-top-level struct (possibly embedded), those aren't allowed"), structType)) + return props + } + + for _, field := range ctx.info.Fields { + jsonTag, hasTag := field.Tag.Lookup("json") + if !hasTag { + // if the field doesn't have a JSON tag, it doesn't belong in output (and shouldn't exist in a serialized type) + ctx.pkg.AddError(loader.ErrFromNode(fmt.Errorf("encountered struct field %q without JSON tag in type %q", field.Name, ctx.info.Name), field.RawField)) + continue + } + jsonOpts := strings.Split(jsonTag, ",") + if len(jsonOpts) == 1 && jsonOpts[0] == "-" { + // skipped fields have the tag "-" (note that "-," means the field is named "-") + continue + } + + inline := false + omitEmpty := false + for _, opt := range jsonOpts[1:] { + switch opt { + case "inline": + inline = true + case "omitempty": + omitEmpty = true + } + } + fieldName := jsonOpts[0] + inline = inline || fieldName == "" // anonymous fields are inline fields in YAML/JSON + + // if no default required mode is set, default to required + defaultMode := "required" + if ctx.PackageMarkers.Get("kubebuilder:validation:Optional") != nil { + defaultMode = "optional" + } + + switch defaultMode { + // if this package isn't set to optional default... + case "required": + // ...everything that's not inline, omitempty, or explicitly optional is required + if !inline && !omitEmpty && field.Markers.Get("kubebuilder:validation:Optional") == nil && field.Markers.Get("optional") == nil { + props.Required = append(props.Required, fieldName) + } + + // if this package isn't set to required default... + case "optional": + // ...everything that isn't explicitly required is optional + if field.Markers.Get("kubebuilder:validation:Required") != nil { + props.Required = append(props.Required, fieldName) + } + } + + propSchema := typeToSchema(ctx.ForInfo(&markers.TypeInfo{}), field.RawField.Type) + propSchema.Description = field.Doc + + applyMarkers(ctx, field.Markers, propSchema, field.RawField) + + if inline { + props.AllOf = append(props.AllOf, *propSchema) + continue + } + + props.Properties[fieldName] = *propSchema + } + + return props +} + +// builtinToType converts builtin basic types to their equivalent JSON schema form. +// It *only* handles types allowed by the kubernetes API standards. Floats are not +// allowed. +func builtinToType(basic *types.Basic) (typ string, format string, err error) { + // NB(directxman12): formats from OpenAPI v3 are slightly different than those defined + // in JSONSchema. This'll use the OpenAPI v3 ones, since they're useful for bounding our + // non-string types. + basicInfo := basic.Info() + switch { + case basicInfo&types.IsBoolean != 0: + typ = "boolean" + case basicInfo&types.IsString != 0: + typ = "string" + case basicInfo&types.IsInteger != 0: + typ = "integer" + default: + // NB(directxman12): floats are *NOT* allowed in kubernetes APIs + return "", "", fmt.Errorf("unsupported type %q", basic.String()) + } + + switch basic.Kind() { + case types.Int32, types.Uint32: + format = "int32" + case types.Int64, types.Uint64: + format = "int64" + } + + return typ, format, nil +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/crd/schema_visitor.go b/vendor/sigs.k8s.io/controller-tools/pkg/crd/schema_visitor.go new file mode 100644 index 0000000000..fc12c528c5 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/crd/schema_visitor.go @@ -0,0 +1,104 @@ +/* +Copyright 2019 The Kubernetes 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 crd + +import ( + apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" +) + +// SchemaVisitor walks the nodes of a schema. +type SchemaVisitor interface { + // Visit is called for each schema node. If it returns a visitor, + // the visitor will be called on each direct child node, and then + // this visitor will be called again with `nil` to indicate that + // all children have been visited. If a nil visitor is returned, + // children are not visited. + Visit(schema *apiext.JSONSchemaProps) SchemaVisitor +} + +// EditSchema walks the given schema using the given visitor. Actual +// pointers to each schema node are passed to the visitor, so any changes +// made by the visitor will be reflected to the passed-in schema. +func EditSchema(schema *apiext.JSONSchemaProps, visitor SchemaVisitor) { + walker := schemaWalker{visitor: visitor} + walker.walkSchema(schema) +} + +// schemaWalker knows how to walk the schema, saving modifications +// made by the given visitor. +type schemaWalker struct { + visitor SchemaVisitor +} + +// walkSchema walks the given schema, saving modifications made by the +// visitor (this is as simple as passing a pointer in most cases, +// but special care needs to be taken to persist with maps). +func (w schemaWalker) walkSchema(schema *apiext.JSONSchemaProps) { + subVisitor := w.visitor.Visit(schema) + if subVisitor == nil { + return + } + defer subVisitor.Visit(nil) + + subWalker := schemaWalker{visitor: subVisitor} + if schema.Items != nil { + subWalker.walkPtr(schema.Items.Schema) + subWalker.walkSlice(schema.Items.JSONSchemas) + } + subWalker.walkSlice(schema.AllOf) + subWalker.walkSlice(schema.OneOf) + subWalker.walkSlice(schema.AnyOf) + subWalker.walkPtr(schema.Not) + subWalker.walkMap(schema.Properties) + if schema.AdditionalProperties != nil { + subWalker.walkPtr(schema.AdditionalProperties.Schema) + } + subWalker.walkMap(schema.PatternProperties) + for name, dep := range schema.Dependencies { + subWalker.walkPtr(dep.Schema) + schema.Dependencies[name] = dep + } + if schema.AdditionalItems != nil { + subWalker.walkPtr(schema.AdditionalItems.Schema) + } + subWalker.walkMap(schema.Definitions) +} + +// walkMap walks over values of the given map, saving changes to them. +func (w schemaWalker) walkMap(defs map[string]apiext.JSONSchemaProps) { + for name, def := range defs { + w.walkSchema(&def) + // make sure the edits actually go through since we can't + // take a reference to the value in the map + defs[name] = def + } +} + +// walkSlice walks over items of the given slice. +func (w schemaWalker) walkSlice(defs []apiext.JSONSchemaProps) { + for i := range defs { + w.walkSchema(&defs[i]) + } +} + +// walkPtr walks over the contents of the given pointer, if it's not nil. +func (w schemaWalker) walkPtr(def *apiext.JSONSchemaProps) { + if def == nil { + return + } + w.walkSchema(def) +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/crd/spec.go b/vendor/sigs.k8s.io/controller-tools/pkg/crd/spec.go new file mode 100644 index 0000000000..01bef20d49 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/crd/spec.go @@ -0,0 +1,161 @@ +/* +Copyright 2019 The Kubernetes 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 crd + +import ( + "fmt" + "sort" + "strings" + + "github.com/gobuffalo/flect" + + apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + + "sigs.k8s.io/controller-tools/pkg/loader" +) + +// SpecMarker is a marker that knows how to apply itself to a particular +// version in a CRD. +type SpecMarker interface { + // ApplyToCRD applies this marker to the given CRD, in the given version + // within that CRD. It's called after everything else in the CRD is populated. + ApplyToCRD(crd *apiext.CustomResourceDefinitionSpec, version string) error +} + +// NeedCRDFor requests the full CRD for the given group-kind. It requires +// that the packages containing the Go structs for that CRD have already +// been loaded with NeedPackage. +func (p *Parser) NeedCRDFor(groupKind schema.GroupKind, maxDescLen *int) { + p.init() + + if _, exists := p.CustomResourceDefinitions[groupKind]; exists { + return + } + + var packages []*loader.Package + for pkg, gv := range p.GroupVersions { + if gv.Group != groupKind.Group { + continue + } + packages = append(packages, pkg) + } + + defaultPlural := flect.Pluralize(strings.ToLower(groupKind.Kind)) + crd := apiext.CustomResourceDefinition{ + TypeMeta: metav1.TypeMeta{ + APIVersion: apiext.SchemeGroupVersion.String(), + Kind: "CustomResourceDefinition", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: defaultPlural + "." + groupKind.Group, + }, + Spec: apiext.CustomResourceDefinitionSpec{ + Group: groupKind.Group, + Names: apiext.CustomResourceDefinitionNames{ + Kind: groupKind.Kind, + ListKind: groupKind.Kind + "List", + Plural: defaultPlural, + Singular: strings.ToLower(groupKind.Kind), + }, + Scope: apiext.NamespaceScoped, + }, + } + + for _, pkg := range packages { + typeIdent := TypeIdent{Package: pkg, Name: groupKind.Kind} + typeInfo := p.Types[typeIdent] + if typeInfo == nil { + continue + } + p.NeedFlattenedSchemaFor(typeIdent) + fullSchema := p.FlattenedSchemata[typeIdent] + fullSchema = *fullSchema.DeepCopy() // don't mutate the cache (we might be truncating description, etc) + if maxDescLen != nil { + TruncateDescription(&fullSchema, *maxDescLen) + } + ver := apiext.CustomResourceDefinitionVersion{ + Name: p.GroupVersions[pkg].Version, + Served: true, + Schema: &apiext.CustomResourceValidation{ + OpenAPIV3Schema: &fullSchema, // fine to take a reference since we deepcopy above + }, + } + crd.Spec.Versions = append(crd.Spec.Versions, ver) + } + + // markers are applied *after* initial generation of objects + for _, pkg := range packages { + typeIdent := TypeIdent{Package: pkg, Name: groupKind.Kind} + typeInfo := p.Types[typeIdent] + if typeInfo == nil { + continue + } + ver := p.GroupVersions[pkg].Version + + for _, markerVals := range typeInfo.Markers { + for _, val := range markerVals { + crdMarker, isCrdMarker := val.(SpecMarker) + if !isCrdMarker { + continue + } + if err := crdMarker.ApplyToCRD(&crd.Spec, ver); err != nil { + pkg.AddError(loader.ErrFromNode(err /* an okay guess */, typeInfo.RawSpec)) + } + } + } + } + + // fix the name if the plural was changed (this is the form the name *has* to take, so no harm in chaning it). + crd.Name = crd.Spec.Names.Plural + "." + groupKind.Group + + // nothing to actually write + if len(crd.Spec.Versions) == 0 { + return + } + + // it is necessary to make sure the order of CRD versions in crd.Spec.Versions is stable and explicitly set crd.Spec.Version. + // Otherwise, crd.Spec.Version may point to different CRD versions across different runs. + sort.Slice(crd.Spec.Versions, func(i, j int) bool { return crd.Spec.Versions[i].Name < crd.Spec.Versions[j].Name }) + + // make sure we have *a* storage version + // (default it if we only have one, otherwise, bail) + if len(crd.Spec.Versions) == 1 { + crd.Spec.Versions[0].Storage = true + } + + hasStorage := false + for _, ver := range crd.Spec.Versions { + if ver.Storage { + hasStorage = true + break + } + } + if !hasStorage { + // just add the error to the first relevant package for this CRD, + // since there's no specific error location + packages[0].AddError(fmt.Errorf("CRD for %s has no storage version", groupKind)) + } + + // NB(directxman12): CRD's status doesn't have omitempty markers, which means things + // get serialized as null, which causes the validator to freak out. Manually set + // these to empty till we get a better solution. + crd.Status.Conditions = []apiext.CustomResourceDefinitionCondition{} + crd.Status.StoredVersions = []string{} + + p.CustomResourceDefinitions[groupKind] = crd +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/crd/zz_generated.markerhelp.go b/vendor/sigs.k8s.io/controller-tools/pkg/crd/zz_generated.markerhelp.go new file mode 100644 index 0000000000..605214a81e --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/crd/zz_generated.markerhelp.go @@ -0,0 +1,49 @@ +// +build !ignore_autogenerated + +/* +Copyright2019 The Kubernetes 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. +*/ + +// Code generated by helpgen. DO NOT EDIT. + +package crd + +import ( + "sigs.k8s.io/controller-tools/pkg/markers" +) + +func (Generator) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "", + DetailedHelp: markers.DetailedHelp{ + Summary: "generates CustomResourceDefinition objects.", + Details: "", + }, + FieldHelp: map[string]markers.DetailedHelp{ + "TrivialVersions": markers.DetailedHelp{ + Summary: "indicates that we should produce a single-version CRD. ", + Details: "Single \"trivial-version\" CRDs are compatible with older (pre 1.13) Kubernetes API servers. The storage version's schema will be used as the CRD's schema. \n Only works with the v1beta1 CRD version.", + }, + "MaxDescLen": markers.DetailedHelp{ + Summary: "specifies the maximum description length for fields in CRD's OpenAPI schema. ", + Details: "0 indicates drop the description for all fields completely. n indicates limit the description to at most n characters and truncate the description to closest sentence boundary if it exceeds n characters.", + }, + "CRDVersions": markers.DetailedHelp{ + Summary: "specifies the target API versions of the CRD type itself to generate. Defaults to v1beta1. ", + Details: "The first version listed will be assumed to be the \"default\" version and will not get a version suffix in the output filename. \n You'll need to use \"v1\" to get support for features like defaulting, along with an API server that supports it (Kubernetes 1.16+).", + }, + }, + } +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/deepcopy/doc.go b/vendor/sigs.k8s.io/controller-tools/pkg/deepcopy/doc.go new file mode 100644 index 0000000000..f4200f2fc1 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/deepcopy/doc.go @@ -0,0 +1,23 @@ +/* +Copyright 2019 The Kubernetes 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 deepcopy generates DeepCopy, DeepCopyInto, and DeepCopyObject +// implementations for types. +// +// It's ported from k8s.io/code-generator's / k8s.io/gengo's deepcopy-gen, +// but it's scoped specifically to runtime.Object and skips support for +// deepcopying interfaces, which aren't handled in CRDs anyway. +package deepcopy diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/deepcopy/gen.go b/vendor/sigs.k8s.io/controller-tools/pkg/deepcopy/gen.go new file mode 100644 index 0000000000..a10c705892 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/deepcopy/gen.go @@ -0,0 +1,300 @@ +/* +Copyright 2019 The Kubernetes 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 deepcopy + +import ( + "bytes" + "fmt" + "go/ast" + "go/format" + "io" + "sort" + "strings" + + "sigs.k8s.io/controller-tools/pkg/genall" + "sigs.k8s.io/controller-tools/pkg/loader" + "sigs.k8s.io/controller-tools/pkg/markers" +) + +// NB(directxman12): markers.LoadRoots ignores autogenerated code via a build tag +// so any time we check for existing deepcopy functions, we only seen manually written ones. + +const ( + runtimeObjPath = "k8s.io/apimachinery/pkg/runtime.Object" +) + +var ( + enablePkgMarker = markers.Must(markers.MakeDefinition("kubebuilder:object:generate", markers.DescribesPackage, false)) + enableTypeMarker = markers.Must(markers.MakeDefinition("kubebuilder:object:generate", markers.DescribesType, false)) + isObjectMarker = markers.Must(markers.MakeDefinition("kubebuilder:object:root", markers.DescribesType, false)) + + legacyEnablePkgMarker = markers.Must(markers.MakeDefinition("k8s:deepcopy-gen", markers.DescribesPackage, markers.RawArguments(nil))) + legacyEnableTypeMarker = markers.Must(markers.MakeDefinition("k8s:deepcopy-gen", markers.DescribesType, markers.RawArguments(nil))) + legacyIsObjectMarker = markers.Must(markers.MakeDefinition("k8s:deepcopy-gen:interfaces", markers.DescribesType, "")) +) + +// +controllertools:marker:generateHelp + +// Generator generates code containing DeepCopy, DeepCopyInto, and +// DeepCopyObject method implementations. +type Generator struct { + // HeaderFile specifies the header text (e.g. license) to prepend to generated files. + HeaderFile string `marker:",optional"` + // Year specifies the year to substitute for " YEAR" in the header file. + Year string `marker:",optional"` +} + +func (Generator) RegisterMarkers(into *markers.Registry) error { + if err := markers.RegisterAll(into, + enablePkgMarker, legacyEnablePkgMarker, enableTypeMarker, + legacyEnableTypeMarker, isObjectMarker, legacyIsObjectMarker); err != nil { + return err + } + into.AddHelp(enablePkgMarker, + markers.SimpleHelp("object", "enables or disables object interface & deepcopy implementation generation for this package")) + into.AddHelp( + enableTypeMarker, markers.SimpleHelp("object", "overrides enabling or disabling deepcopy generation for this type")) + into.AddHelp(isObjectMarker, + markers.SimpleHelp("object", "enables object interface implementation generation for this type")) + + into.AddHelp(legacyEnablePkgMarker, + markers.DeprecatedHelp(enablePkgMarker.Name, "object", "enables or disables object interface & deepcopy implementation generation for this package")) + into.AddHelp(legacyEnableTypeMarker, + markers.DeprecatedHelp(enableTypeMarker.Name, "object", "overrides enabling or disabling deepcopy generation for this type")) + into.AddHelp(legacyIsObjectMarker, + markers.DeprecatedHelp(isObjectMarker.Name, "object", "enables object interface implementation generation for this type")) + return nil +} + +func enabledOnPackage(col *markers.Collector, pkg *loader.Package) (bool, error) { + pkgMarkers, err := markers.PackageMarkers(col, pkg) + if err != nil { + return false, err + } + pkgMarker := pkgMarkers.Get(enablePkgMarker.Name) + if pkgMarker != nil { + return pkgMarker.(bool), nil + } + legacyMarker := pkgMarkers.Get(legacyEnablePkgMarker.Name) + if legacyMarker != nil { + legacyMarkerVal := string(legacyMarker.(markers.RawArguments)) + firstArg := strings.Split(legacyMarkerVal, ",")[0] + return firstArg == "package", nil + } + + return false, nil +} + +func enabledOnType(allTypes bool, info *markers.TypeInfo) bool { + if typeMarker := info.Markers.Get(enableTypeMarker.Name); typeMarker != nil { + return typeMarker.(bool) + } + legacyMarker := info.Markers.Get(legacyEnableTypeMarker.Name) + if legacyMarker != nil { + legacyMarkerVal := string(legacyMarker.(markers.RawArguments)) + return legacyMarkerVal == "true" + } + return allTypes || genObjectInterface(info) +} + +func genObjectInterface(info *markers.TypeInfo) bool { + objectEnabled := info.Markers.Get(isObjectMarker.Name) + if objectEnabled != nil { + return objectEnabled.(bool) + } + + for _, legacyEnabled := range info.Markers[legacyIsObjectMarker.Name] { + if legacyEnabled == runtimeObjPath { + return true + } + } + return false +} + +func (d Generator) Generate(ctx *genall.GenerationContext) error { + var headerText string + + if d.HeaderFile != "" { + headerBytes, err := ctx.ReadFile(d.HeaderFile) + if err != nil { + return err + } + headerText = string(headerBytes) + } + headerText = strings.ReplaceAll(headerText, " YEAR", " "+d.Year) + + objGenCtx := ObjectGenCtx{ + Collector: ctx.Collector, + Checker: ctx.Checker, + HeaderText: headerText, + } + + for _, root := range ctx.Roots { + outContents := objGenCtx.GenerateForPackage(root) + if outContents == nil { + continue + } + + writeOut(ctx, root, outContents) + } + + return nil +} + +// ObjectGenCtx contains the common info for generating deepcopy implementations. +// It mostly exists so that generating for a package can be easily tested without +// requiring a full set of output rules, etc. +type ObjectGenCtx struct { + Collector *markers.Collector + Checker *loader.TypeChecker + HeaderText string +} + +// writeHeader writes out the build tag, package declaration, and imports +func writeHeader(pkg *loader.Package, out io.Writer, packageName string, imports *importsList, headerText string) { + // NB(directxman12): blank line after build tags to distinguish them from comments + _, err := fmt.Fprintf(out, `// +build !ignore_autogenerated + +%[3]s + +// Code generated by controller-gen. DO NOT EDIT. + +package %[1]s + +import ( +%[2]s +) + +`, packageName, strings.Join(imports.ImportSpecs(), "\n"), headerText) + if err != nil { + pkg.AddError(err) + } + +} + +// GenerateForPackage generates DeepCopy and runtime.Object implementations for +// types in the given package, writing the formatted result to given writer. +// May return nil if source could not be generated. +func (ctx *ObjectGenCtx) GenerateForPackage(root *loader.Package) []byte { + allTypes, err := enabledOnPackage(ctx.Collector, root) + if err != nil { + root.AddError(err) + return nil + } + + ctx.Checker.Check(root, func(node ast.Node) bool { + // ignore interfaces + _, isIface := node.(*ast.InterfaceType) + return !isIface + }) + + root.NeedTypesInfo() + + byType := make(map[string][]byte) + imports := &importsList{ + byPath: make(map[string]string), + byAlias: make(map[string]string), + pkg: root, + } + // avoid confusing aliases by "reserving" the root package's name as an alias + imports.byAlias[root.Name] = "" + + if err := markers.EachType(ctx.Collector, root, func(info *markers.TypeInfo) { + outContent := new(bytes.Buffer) + + // copy when nabled for all types and not disabled, or enabled + // specifically on this type + if !enabledOnType(allTypes, info) { + return + } + + // avoid copying non-exported types, etc + if !shouldBeCopied(root, info) { + return + } + + copyCtx := ©MethodMaker{ + pkg: root, + importsList: imports, + codeWriter: &codeWriter{out: outContent}, + } + + copyCtx.GenerateMethodsFor(root, info) + + outBytes := outContent.Bytes() + if len(outBytes) > 0 { + byType[info.Name] = outBytes + } + }); err != nil { + root.AddError(err) + return nil + } + + if len(byType) == 0 { + return nil + } + + outContent := new(bytes.Buffer) + writeHeader(root, outContent, root.Name, imports, ctx.HeaderText) + writeMethods(root, outContent, byType) + + outBytes := outContent.Bytes() + formattedBytes, err := format.Source(outBytes) + if err != nil { + root.AddError(err) + // we still write the invalid source to disk to figure out what went wrong + } else { + outBytes = formattedBytes + } + + return outBytes +} + +// writeMethods writes each method to the file, sorted by type name. +func writeMethods(pkg *loader.Package, out io.Writer, byType map[string][]byte) { + sortedNames := make([]string, 0, len(byType)) + for name := range byType { + sortedNames = append(sortedNames, name) + } + sort.Strings(sortedNames) + + for _, name := range sortedNames { + _, err := out.Write(byType[name]) + if err != nil { + pkg.AddError(err) + } + } +} + +// writeFormatted outputs the given code, after gofmt-ing it. If we couldn't gofmt, +// we write the unformatted code for debugging purposes. +func writeOut(ctx *genall.GenerationContext, root *loader.Package, outBytes []byte) { + outputFile, err := ctx.Open(root, "zz_generated.deepcopy.go") + if err != nil { + root.AddError(err) + return + } + defer outputFile.Close() + n, err := outputFile.Write(outBytes) + if err != nil { + root.AddError(err) + return + } + if n < len(outBytes) { + root.AddError(io.ErrShortWrite) + } +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/deepcopy/traverse.go b/vendor/sigs.k8s.io/controller-tools/pkg/deepcopy/traverse.go new file mode 100644 index 0000000000..1c92e48a09 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/deepcopy/traverse.go @@ -0,0 +1,812 @@ +/* +Copyright 2019 The Kubernetes 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 deepcopy + +import ( + "fmt" + "go/ast" + "go/types" + "io" + "path" + "strings" + "unicode" + "unicode/utf8" + + "sigs.k8s.io/controller-tools/pkg/loader" + "sigs.k8s.io/controller-tools/pkg/markers" +) + +// NB(directxman12): This code is a bit of a byzantine mess. +// I've tried to clean it up a bit from the original in deepcopy-gen, +// but parts remain a bit convoluted. Exercise caution when changing. +// It's perhaps a tad over-commented now, but better safe than sorry. +// It also seriously needs auditing for sanity -- there's parts where we +// copy the original deepcopy-gen's output just to be safe, but some of that +// could be simplified away if we're careful. + +// codeWriter assists in writing out Go code lines and blocks to a writer. +type codeWriter struct { + out io.Writer +} + +// Line writes a single line. +func (c *codeWriter) Line(line string) { + fmt.Fprintln(c.out, line) +} + +// Linef writes a single line with formatting (as per fmt.Sprintf). +func (c *codeWriter) Linef(line string, args ...interface{}) { + fmt.Fprintf(c.out, line+"\n", args...) +} + +// If writes an if statement with the given setup/condition clause, executing +// the given function to write the contents of the block. +func (c *codeWriter) If(setup string, block func()) { + c.Linef("if %s {", setup) + block() + c.Line("}") +} + +// If writes if and else statements with the given setup/condition clause, executing +// the given functions to write the contents of the blocks. +func (c *codeWriter) IfElse(setup string, ifBlock func(), elseBlock func()) { + c.Linef("if %s {", setup) + ifBlock() + c.Line("} else {") + elseBlock() + c.Line("}") +} + +// For writes an for statement with the given setup/condition clause, executing +// the given function to write the contents of the block. +func (c *codeWriter) For(setup string, block func()) { + c.Linef("for %s {", setup) + block() + c.Line("}") +} + +// importsList keeps track of required imports, automatically assigning aliases +// to import statement. +type importsList struct { + byPath map[string]string + byAlias map[string]string + + pkg *loader.Package +} + +// NeedImport marks that the given package is needed in the list of imports, +// returning the ident (import alias) that should be used to reference the package. +func (l *importsList) NeedImport(importPath string) string { + // we get an actual path from Package, which might include venddored + // packages if running on a package in vendor. + if ind := strings.LastIndex(importPath, "/vendor/"); ind != -1 { + importPath = importPath[ind+8: /* len("/vendor/") */] + } + + // check to see if we've already assigned an alias, and just return that. + alias, exists := l.byPath[importPath] + if exists { + return alias + } + + // otherwise, calculate an import alias by joining path parts till we get something unique + restPath, nextWord := path.Split(importPath) + + for otherPath, exists := "", true; exists && otherPath != importPath; otherPath, exists = l.byAlias[alias] { + if restPath == "" { + // do something else to disambiguate if we're run out of parts and + // still have duplicates, somehow + alias += "x" + } + + // can't have a first digit, per Go identifier rules, so just skip them + for firstRune, runeLen := utf8.DecodeRuneInString(nextWord); unicode.IsDigit(firstRune); firstRune, runeLen = utf8.DecodeRuneInString(nextWord) { + nextWord = nextWord[runeLen:] + } + + // make a valid identifier by replacing "bad" characters with underscores + nextWord = strings.Map(func(r rune) rune { + if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' { + return r + } + return '_' + }, nextWord) + + alias = nextWord + alias + if len(restPath) > 0 { + restPath, nextWord = path.Split(restPath[:len(restPath)-1] /* chop off final slash */) + } + } + + l.byPath[importPath] = alias + l.byAlias[alias] = importPath + return alias +} + +// ImportSpecs returns a string form of each import spec +// (i.e. `alias "path/to/import"). Aliases are only present +// when they don't match the package name. +func (l *importsList) ImportSpecs() []string { + res := make([]string, 0, len(l.byPath)) + for importPath, alias := range l.byPath { + pkg := l.pkg.Imports()[importPath] + if pkg != nil && pkg.Name == alias { + // don't print if alias is the same as package name + // (we've already taken care of duplicates). + res = append(res, fmt.Sprintf("%q", importPath)) + } else { + res = append(res, fmt.Sprintf("%s %q", alias, importPath)) + } + } + return res +} + +// namingInfo holds package and syntax for referencing a field, type, +// etc. It's used to allow lazily marking import usage. +// You should generally retrieve the syntax using Syntax. +type namingInfo struct { + // typeInfo is the type being named. + typeInfo types.Type + nameOverride string +} + +// Syntax calculates the code representation of the given type or name, +// and marks that is used (potentially marking an import as used). +func (n *namingInfo) Syntax(basePkg *loader.Package, imports *importsList) string { + if n.nameOverride != "" { + return n.nameOverride + } + + // NB(directxman12): typeInfo.String gets us most of the way there, + // but fails (for us) on named imports, since it uses the full package path. + switch typeInfo := n.typeInfo.(type) { + case *types.Named: + // register that we need an import for this type, + // so we can get the appropriate alias to use. + typeName := typeInfo.Obj() + otherPkg := typeName.Pkg() + if otherPkg == basePkg.Types { + // local import + return typeName.Name() + } + alias := imports.NeedImport(loader.NonVendorPath(otherPkg.Path())) + return alias + "." + typeName.Name() + case *types.Basic: + return typeInfo.String() + case *types.Pointer: + return "*" + (&namingInfo{typeInfo: typeInfo.Elem()}).Syntax(basePkg, imports) + case *types.Slice: + return "[]" + (&namingInfo{typeInfo: typeInfo.Elem()}).Syntax(basePkg, imports) + case *types.Map: + return fmt.Sprintf( + "map[%s]%s", + (&namingInfo{typeInfo: typeInfo.Key()}).Syntax(basePkg, imports), + (&namingInfo{typeInfo: typeInfo.Elem()}).Syntax(basePkg, imports)) + default: + basePkg.AddError(fmt.Errorf("name requested for invalid type %s", typeInfo)) + return typeInfo.String() + } +} + +// copyMethodMakers makes DeepCopy (and related) methods for Go types, +// writing them to its codeWriter. +type copyMethodMaker struct { + pkg *loader.Package + *importsList + *codeWriter +} + +// GenerateMethodsFor makes DeepCopy, DeepCopyInto, and DeepCopyObject methods +// for the given type, when appropriate +func (c *copyMethodMaker) GenerateMethodsFor(root *loader.Package, info *markers.TypeInfo) { + typeInfo := root.TypesInfo.TypeOf(info.RawSpec.Name) + if typeInfo == types.Typ[types.Invalid] { + root.AddError(loader.ErrFromNode(fmt.Errorf("unknown type %s", info.Name), info.RawSpec)) + } + + // figure out if we need to use a pointer receiver -- most types get a pointer receiver, + // except those that are aliases to types that are already pass-by-reference (pointers, + // interfaces. maps, slices). + ptrReceiver := usePtrReceiver(typeInfo) + + hasManualDeepCopyInto := hasDeepCopyIntoMethod(root, typeInfo) + hasManualDeepCopy, deepCopyOnPtr := hasDeepCopyMethod(root, typeInfo) + + // only generate each method if it hasn't been implemented. + if !hasManualDeepCopyInto { + c.Line("// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.") + if ptrReceiver { + c.Linef("func (in *%s) DeepCopyInto(out *%s) {", info.Name, info.Name) + } else { + c.Linef("func (in %s) DeepCopyInto(out *%s) {", info.Name, info.Name) + c.Line("{in := &in") // add an extra block so that we can redefine `in` without type issues + } + + // just wrap the existing deepcopy if present + if hasManualDeepCopy { + if deepCopyOnPtr { + c.Line("clone := in.DeepCopy()") + c.Line("*out = *clone") + } else { + c.Line("*out = in.DeepCopy()") + } + } else { + c.genDeepCopyIntoBlock(&namingInfo{nameOverride: info.Name}, typeInfo) + } + + if !ptrReceiver { + c.Line("}") // close our extra "in redefinition" block + } + c.Line("}") + } + + if !hasManualDeepCopy { + // these are both straightforward, so we just template them out. + if ptrReceiver { + c.Linef(ptrDeepCopy, info.Name) + } else { + c.Linef(bareDeepCopy, info.Name) + } + + // maybe also generate DeepCopyObject, if asked. + if genObjectInterface(info) { + // we always need runtime.Object for DeepCopyObject + runtimeAlias := c.NeedImport("k8s.io/apimachinery/pkg/runtime") + if ptrReceiver { + c.Linef(ptrDeepCopyObj, info.Name, runtimeAlias) + } else { + c.Linef(bareDeepCopyObj, info.Name, runtimeAlias) + } + } + } +} + +// genDeepCopyBody generates a DeepCopyInto block for the given type. The +// block is *not* wrapped in curly braces. +func (c *copyMethodMaker) genDeepCopyIntoBlock(actualName *namingInfo, typeInfo types.Type) { + // we calculate *how* we should copy mostly based on the "eventual" type of + // a given type (i.e. the type that results from following all aliases) + last := eventualUnderlyingType(typeInfo) + + // we might hit a type that has a manual deepcopy method written on non-root types + // (this case is handled for root types in GenerateMethodFor). + // In that case (when we're not dealing with a pointer, since those need special handling + // to match 1-to-1 with k8s deepcopy-gen), just use that. + if _, isPtr := last.(*types.Pointer); !isPtr && hasAnyDeepCopyMethod(c.pkg, typeInfo) { + c.Line("*out = in.DeepCopy()") + return + } + + switch last := last.(type) { + case *types.Basic: + // basic types themselves can be "shallow" copied, so all we need + // to do is check if our *actual* type (not the underlying one) has + // a custom method implemented. + if hasMethod, _ := hasDeepCopyMethod(c.pkg, typeInfo); hasMethod { + c.Line("*out = in.DeepCopy()") + } + c.Line("*out = *in") + case *types.Map: + c.genMapDeepCopy(actualName, last) + case *types.Slice: + c.genSliceDeepCopy(actualName, last) + case *types.Struct: + c.genStructDeepCopy(actualName, last) + case *types.Pointer: + c.genPointerDeepCopy(actualName, last) + case *types.Named: + // handled via the above loop, should never happen + c.pkg.AddError(fmt.Errorf("interface type %s encountered directly, invalid condition", last)) + default: + c.pkg.AddError(fmt.Errorf("invalid type %s", last)) + } +} + +// genMapDeepCopy generates DeepCopy code for the given named type whose eventual +// type is the given map type. +func (c *copyMethodMaker) genMapDeepCopy(actualName *namingInfo, mapType *types.Map) { + // maps *must* have shallow-copiable types, since we just iterate + // through the keys, only trying to deepcopy the values. + if !fineToShallowCopy(mapType.Key()) { + c.pkg.AddError(fmt.Errorf("invalid map key type %s", mapType.Key())) + return + } + + // make our actual type (not the underlying one)... + c.Linef("*out = make(%[1]s, len(*in))", actualName.Syntax(c.pkg, c.importsList)) + + // ...and copy each element appropriately + c.For("key, val := range *in", func() { + // check if we have manually written methods, + // in which case we'll just try and use those + hasDeepCopy, copyOnPtr := hasDeepCopyMethod(c.pkg, mapType.Elem()) + hasDeepCopyInto := hasDeepCopyIntoMethod(c.pkg, mapType.Elem()) + switch { + case hasDeepCopyInto || hasDeepCopy: + // use the manually-written methods + _, fieldIsPtr := mapType.Elem().(*types.Pointer) // is "out" actually a pointer + inIsPtr := resultWillBePointer(mapType.Elem(), hasDeepCopy, copyOnPtr) // does copying "in" produce a pointer + if hasDeepCopy { + // If we're calling DeepCopy, check if it's receiver needs a pointer + inIsPtr = copyOnPtr + } + if inIsPtr == fieldIsPtr { + c.Line("(*out)[key] = val.DeepCopy()") + } else if fieldIsPtr { + c.Line("{") // use a block because we use `x` as a temporary + c.Line("x := val.DeepCopy()") + c.Line("(*out)[key] = &x") + c.Line("}") + } else { + c.Line("(*out)[key] = *val.DeepCopy()") + } + case fineToShallowCopy(mapType.Elem()): + // just shallow copy types for which it's safe to do so + c.Line("(*out)[key] = val") + default: + // otherwise, we've got some kind-specific actions, + // based on the element's eventual type. + + underlyingElem := eventualUnderlyingType(mapType.Elem()) + + // if it passes by reference, let the main switch handle it + if passesByReference(underlyingElem) { + c.Linef("var outVal %[1]s", (&namingInfo{typeInfo: underlyingElem}).Syntax(c.pkg, c.importsList)) + c.IfElse("val == nil", func() { + c.Line("(*out)[key] = nil") + }, func() { + c.Line("in, out := &val, &outVal") + c.genDeepCopyIntoBlock(&namingInfo{typeInfo: mapType.Elem()}, mapType.Elem()) + }) + c.Line("(*out)[key] = outVal") + + return + } + + // otherwise... + switch underlyingElem := underlyingElem.(type) { + case *types.Struct: + // structs will have deepcopy generated for them, so use that + c.Line("(*out)[key] = *val.DeepCopy()") + default: + c.pkg.AddError(fmt.Errorf("invalid map value type %s", underlyingElem)) + return + } + } + }) +} + +// genSliceDeepCopy generates DeepCopy code for the given named type whose +// underlying type is the given slice. +func (c *copyMethodMaker) genSliceDeepCopy(actualName *namingInfo, sliceType *types.Slice) { + underlyingElem := eventualUnderlyingType(sliceType.Elem()) + + // make the actual type (not the underlying) + c.Linef("*out = make(%[1]s, len(*in))", actualName.Syntax(c.pkg, c.importsList)) + + // check if we need to do anything special, or just copy each element appropriately + switch { + case hasAnyDeepCopyMethod(c.pkg, sliceType.Elem()): + // just use deepcopy if it's present (deepcopyinto will be filled in by our code) + c.For("i := range *in", func() { + c.Line("(*in)[i].DeepCopyInto(&(*out)[i])") + }) + case fineToShallowCopy(underlyingElem): + // shallow copy if ok + c.Line("copy(*out, *in)") + default: + // copy each element appropriately + c.For("i := range *in", func() { + // fall back to normal code for reference types or those with custom logic + if passesByReference(underlyingElem) || hasAnyDeepCopyMethod(c.pkg, sliceType.Elem()) { + c.If("(*in)[i] != nil", func() { + c.Line("in, out := &(*in)[i], &(*out)[i]") + c.genDeepCopyIntoBlock(&namingInfo{typeInfo: sliceType.Elem()}, sliceType.Elem()) + }) + return + } + + switch underlyingElem.(type) { + case *types.Struct: + // structs will always have deepcopy + c.Linef("(*in)[i].DeepCopyInto(&(*out)[i])") + default: + c.pkg.AddError(fmt.Errorf("invalid slice element type %s", underlyingElem)) + } + }) + } +} + +// genStructDeepCopy generates DeepCopy code for the given named type whose +// underlying type is the given struct. +func (c *copyMethodMaker) genStructDeepCopy(_ *namingInfo, structType *types.Struct) { + c.Line("*out = *in") + + for i := 0; i < structType.NumFields(); i++ { + field := structType.Field(i) + + // if we have a manual deepcopy, use that + hasDeepCopy, copyOnPtr := hasDeepCopyMethod(c.pkg, field.Type()) + hasDeepCopyInto := hasDeepCopyIntoMethod(c.pkg, field.Type()) + if hasDeepCopyInto || hasDeepCopy { + // NB(directxman12): yes, I know this is kind-of weird that we + // have all this special-casing here, but it's nice for testing + // purposes to be 1-to-1 with deepcopy-gen, which does all sorts of + // stuff like this (I'm pretty sure I found some codepaths that + // never execute there, because they're pretty clearly invalid + // syntax). + + _, fieldIsPtr := field.Type().(*types.Pointer) + inIsPtr := resultWillBePointer(field.Type(), hasDeepCopy, copyOnPtr) + if fieldIsPtr { + // we'll need a if block to check for nilness + // we'll let genDeepCopyIntoBlock handle the details, we just needed the setup + c.If(fmt.Sprintf("in.%s != nil", field.Name()), func() { + c.Linef("in, out := &in.%[1]s, &out.%[1]s", field.Name()) + c.genDeepCopyIntoBlock(&namingInfo{typeInfo: field.Type()}, field.Type()) + }) + } else { + // special-case for compatibility with deepcopy-gen + if inIsPtr == fieldIsPtr { + c.Linef("out.%[1]s = in.%[1]s.DeepCopy()", field.Name()) + } else { + c.Linef("in.%[1]s.DeepCopyInto(&out.%[1]s)", field.Name()) + } + } + continue + } + + // pass-by-reference fields get delegated to the main type + underlyingField := eventualUnderlyingType(field.Type()) + if passesByReference(underlyingField) { + c.If(fmt.Sprintf("in.%s != nil", field.Name()), func() { + c.Linef("in, out := &in.%[1]s, &out.%[1]s", field.Name()) + c.genDeepCopyIntoBlock(&namingInfo{typeInfo: field.Type()}, field.Type()) + }) + continue + } + + // otherwise... + switch underlyingField := underlyingField.(type) { + case *types.Basic: + // nothing to do, initial assignment copied this + case *types.Struct: + if fineToShallowCopy(field.Type()) { + c.Linef("out.%[1]s = in.%[1]s", field.Name()) + } else { + c.Linef("in.%[1]s.DeepCopyInto(&out.%[1]s)", field.Name()) + } + default: + c.pkg.AddError(fmt.Errorf("invalid field type %s", underlyingField)) + return + } + } +} + +// genPointerDeepCopy generates DeepCopy code for the given named type whose +// underlying type is the given struct. +func (c *copyMethodMaker) genPointerDeepCopy(_ *namingInfo, pointerType *types.Pointer) { + underlyingElem := eventualUnderlyingType(pointerType.Elem()) + + // if we have a manually written deepcopy, just use that + hasDeepCopy, copyOnPtr := hasDeepCopyMethod(c.pkg, pointerType.Elem()) + hasDeepCopyInto := hasDeepCopyIntoMethod(c.pkg, pointerType.Elem()) + if hasDeepCopyInto || hasDeepCopy { + outNeedsPtr := resultWillBePointer(pointerType.Elem(), hasDeepCopy, copyOnPtr) + if hasDeepCopy { + outNeedsPtr = copyOnPtr + } + if outNeedsPtr { + c.Line("*out = (*in).DeepCopy()") + } else { + c.Line("x := (*in).DeepCopy()") + c.Line("*out = &x") + } + return + } + + // shallow-copiable types are pretty easy + if fineToShallowCopy(underlyingElem) { + c.Linef("*out = new(%[1]s)", (&namingInfo{typeInfo: pointerType.Elem()}).Syntax(c.pkg, c.importsList)) + c.Line("**out = **in") + return + } + + // pass-by-reference types get delegated to the main switch + if passesByReference(underlyingElem) { + c.Linef("*out = new(%s)", (&namingInfo{typeInfo: underlyingElem}).Syntax(c.pkg, c.importsList)) + c.If("**in != nil", func() { + c.Line("in, out := *in, *out") + c.genDeepCopyIntoBlock(&namingInfo{typeInfo: underlyingElem}, eventualUnderlyingType(underlyingElem)) + }) + return + } + + // otherwise... + switch underlyingElem := underlyingElem.(type) { + case *types.Struct: + c.Linef("*out = new(%[1]s)", (&namingInfo{typeInfo: pointerType.Elem()}).Syntax(c.pkg, c.importsList)) + c.Line("(*in).DeepCopyInto(*out)") + default: + c.pkg.AddError(fmt.Errorf("invalid pointer element type %s", underlyingElem)) + return + } +} + +// usePtrReceiver checks if we need a pointer receiver on methods for the given type +// Pass-by-reference types don't get pointer receivers. +func usePtrReceiver(typeInfo types.Type) bool { + switch typeInfo.(type) { + case *types.Pointer: + return false + case *types.Map: + return false + case *types.Slice: + return false + case *types.Named: + return usePtrReceiver(typeInfo.Underlying()) + default: + return true + } +} + +func resultWillBePointer(typeInfo types.Type, hasDeepCopy, deepCopyOnPtr bool) bool { + // if we have a manual deepcopy, we can just check what that returns + if hasDeepCopy { + return deepCopyOnPtr + } + + // otherwise, we'll need to check its type + switch typeInfo := typeInfo.(type) { + case *types.Pointer: + // NB(directxman12): we don't have to worry about the elem having a deepcopy, + // since hasManualDeepCopy would've caught that. + + // we'll be calling on the elem, so check that + return resultWillBePointer(typeInfo.Elem(), false, false) + case *types.Map: + return false + case *types.Slice: + return false + case *types.Named: + return resultWillBePointer(typeInfo.Underlying(), false, false) + default: + return true + } +} + +// shouldBeCopied checks if we're supposed to make deepcopy methods the given type. +// +// This is the case if it's exported *and* either: +// - has a partial manual DeepCopy implementation (in which case we fill in the rest) +// - aliases to a non-basic type eventually +// - is a struct +func shouldBeCopied(pkg *loader.Package, info *markers.TypeInfo) bool { + if !ast.IsExported(info.Name) { + return false + } + + typeInfo := pkg.TypesInfo.TypeOf(info.RawSpec.Name) + if typeInfo == types.Typ[types.Invalid] { + pkg.AddError(loader.ErrFromNode(fmt.Errorf("unknown type %s", info.Name), info.RawSpec)) + return false + } + + // according to gengo, everything named is an alias, except for an alias to a pointer, + // which is just a pointer, afaict. Just roll with it. + if asPtr, isPtr := typeInfo.(*types.Named).Underlying().(*types.Pointer); isPtr { + typeInfo = asPtr + } + + lastType := typeInfo + if _, isNamed := typeInfo.(*types.Named); isNamed { + // if it has a manual deepcopy or deepcopyinto, we're fine + if hasAnyDeepCopyMethod(pkg, typeInfo) { + return true + } + + for underlyingType := typeInfo.Underlying(); underlyingType != lastType; lastType, underlyingType = underlyingType, underlyingType.Underlying() { + // if it has a manual deepcopy or deepcopyinto, we're fine + if hasAnyDeepCopyMethod(pkg, underlyingType) { + return true + } + + // aliases to other things besides basics need copy methods + // (basics can be straight-up shallow-copied) + if _, isBasic := underlyingType.(*types.Basic); !isBasic { + return true + } + } + } + + // structs are the only thing that's not a basic that's copiable by default + _, isStruct := lastType.(*types.Struct) + return isStruct +} + +// hasDeepCopyMethod checks if this type has a manual DeepCopy method and if +// the method has a pointer receiver. +func hasDeepCopyMethod(pkg *loader.Package, typeInfo types.Type) (bool, bool) { + deepCopyMethod, ind, _ := types.LookupFieldOrMethod(typeInfo, true /* check pointers too */, pkg.Types, "DeepCopy") + if len(ind) != 1 { + // ignore embedded methods + return false, false + } + if deepCopyMethod == nil { + return false, false + } + + methodSig := deepCopyMethod.Type().(*types.Signature) + if methodSig.Params() != nil && methodSig.Params().Len() != 0 { + return false, false + } + if methodSig.Results() == nil || methodSig.Results().Len() != 1 { + return false, false + } + + recvAsPtr, recvIsPtr := methodSig.Recv().Type().(*types.Pointer) + if recvIsPtr { + // NB(directxman12): the pointer type returned here isn't comparable even though they + // have the same underlying type, for some reason (probably that + // LookupFieldOrMethod calls types.NewPointer for us), so check the + // underlying values. + + resultPtr, resultIsPtr := methodSig.Results().At(0).Type().(*types.Pointer) + if !resultIsPtr { + // pointer vs non-pointer are different types + return false, false + } + + if recvAsPtr.Elem() != resultPtr.Elem() { + return false, false + } + } else if methodSig.Results().At(0).Type() != methodSig.Recv().Type() { + return false, false + } + + return true, recvIsPtr +} + +// hasDeepCopyIntoMethod checks if this type has a manual DeepCopyInto method. +func hasDeepCopyIntoMethod(pkg *loader.Package, typeInfo types.Type) bool { + deepCopyMethod, ind, _ := types.LookupFieldOrMethod(typeInfo, true /* check pointers too */, pkg.Types, "DeepCopyInto") + if len(ind) != 1 { + // ignore embedded methods + return false + } + if deepCopyMethod == nil { + return false + } + + methodSig := deepCopyMethod.Type().(*types.Signature) + if methodSig.Params() == nil || methodSig.Params().Len() != 1 { + return false + } + paramPtr, isPtr := methodSig.Params().At(0).Type().(*types.Pointer) + if !isPtr { + return false + } + if methodSig.Results() != nil && methodSig.Results().Len() != 0 { + return false + } + + if recvPtr, recvIsPtr := methodSig.Recv().Type().(*types.Pointer); recvIsPtr { + // NB(directxman12): the pointer type returned here isn't comparable even though they + // have the same underlying type, for some reason (probably that + // LookupFieldOrMethod calls types.NewPointer for us), so check the + // underlying values. + return paramPtr.Elem() == recvPtr.Elem() + } + return methodSig.Recv().Type() == paramPtr.Elem() +} + +// hasAnyDeepCopyMethod checks if the given method has DeepCopy or DeepCopyInto +// (either of which implies the other will exist eventually). +func hasAnyDeepCopyMethod(pkg *loader.Package, typeInfo types.Type) bool { + hasDeepCopy, _ := hasDeepCopyMethod(pkg, typeInfo) + return hasDeepCopy || hasDeepCopyIntoMethod(pkg, typeInfo) +} + +// eventualUnderlyingType gets the "final" type in a sequence of named aliases. +// It's effectively a shortcut for calling Underlying in a loop. +func eventualUnderlyingType(typeInfo types.Type) types.Type { + last := typeInfo + for underlying := typeInfo.Underlying(); underlying != last; last, underlying = underlying, underlying.Underlying() { + // get the actual underlying type + } + return last +} + +// fineToShallowCopy checks if a shallow-copying a type is equivalent to deepcopy-ing it. +func fineToShallowCopy(typeInfo types.Type) bool { + switch typeInfo := typeInfo.(type) { + case *types.Basic: + // basic types (int, string, etc) are always fine to shallow-copy + return true + case *types.Named: + // aliases are fine to shallow-copy as long as they resolve to a shallow-copyable type + return fineToShallowCopy(typeInfo.Underlying()) + case *types.Struct: + // structs are fine to shallow-copy if they have all shallow-copyable fields + for i := 0; i < typeInfo.NumFields(); i++ { + field := typeInfo.Field(i) + if !fineToShallowCopy(field.Type()) { + return false + } + } + return true + default: + return false + } +} + +// passesByReference checks if the given type passesByReference +// (except for interfaces, which are handled separately). +func passesByReference(typeInfo types.Type) bool { + switch typeInfo.(type) { + case *types.Slice: + return true + case *types.Map: + return true + case *types.Pointer: + return true + default: + return false + } +} + +var ( + // ptrDeepCopy is a DeepCopy for a type with an existing DeepCopyInto and a pointer receiver. + ptrDeepCopy = ` +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new %[1]s. +func (in *%[1]s) DeepCopy() *%[1]s { + if in == nil { return nil } + out := new(%[1]s) + in.DeepCopyInto(out) + return out +} +` + + // ptrDeepCopy is a DeepCopy for a type with an existing DeepCopyInto and a non-pointer receiver. + bareDeepCopy = ` +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new %[1]s. +func (in %[1]s) DeepCopy() %[1]s { + if in == nil { return nil } + out := new(%[1]s) + in.DeepCopyInto(out) + return *out +} +` + + // ptrDeepCopy is a DeepCopyObject for a type with an existing DeepCopyInto and a pointer receiver. + ptrDeepCopyObj = ` +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *%[1]s) DeepCopyObject() %[2]s.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} +` + // ptrDeepCopy is a DeepCopyObject for a type with an existing DeepCopyInto and a non-pointer receiver. + bareDeepCopyObj = ` +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in %[1]s) DeepCopyObject() %[2]s.Object { + return in.DeepCopy() +} +` +) diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/deepcopy/zz_generated.markerhelp.go b/vendor/sigs.k8s.io/controller-tools/pkg/deepcopy/zz_generated.markerhelp.go new file mode 100644 index 0000000000..a04355836f --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/deepcopy/zz_generated.markerhelp.go @@ -0,0 +1,45 @@ +// +build !ignore_autogenerated + +/* +Copyright2019 The Kubernetes 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. +*/ + +// Code generated by helpgen. DO NOT EDIT. + +package deepcopy + +import ( + "sigs.k8s.io/controller-tools/pkg/markers" +) + +func (Generator) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "", + DetailedHelp: markers.DetailedHelp{ + Summary: "generates code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.", + Details: "", + }, + FieldHelp: map[string]markers.DetailedHelp{ + "HeaderFile": markers.DetailedHelp{ + Summary: "specifies the header text (e.g. license) to prepend to generated files.", + Details: "", + }, + "Year": markers.DetailedHelp{ + Summary: "specifies the year to substitute for \" YEAR\" in the header file.", + Details: "", + }, + }, + } +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/genall/doc.go b/vendor/sigs.k8s.io/controller-tools/pkg/genall/doc.go new file mode 100644 index 0000000000..df8dd71389 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/genall/doc.go @@ -0,0 +1,58 @@ +/* +Copyright 2019 The Kubernetes 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 genall defines entrypoints for generation tools to hook into and +// share the same set of parsing, typechecking, and marker information. +// +// Generators +// +// Each Generator knows how to register its markers into a central Registry, +// and then how to generate output using a Collector and some root packages. +// Each generator can be considered to be the output type of a marker, for easy +// command line parsing. +// +// Output and Input +// +// Generators output artifacts via an OutputRule. OutputRules know how to +// write output for different package-associated (code) files, as well as +// config files. Each OutputRule should also be considered to be the output +// type as a marker, for easy command-line parsing. +// +// OutputRules groups together an OutputRule per generator, plus a default +// output rule for any not explicitly specified. +// +// OutputRules are defined for stdout, file writing, and sending to /dev/null +// (useful for doing "type-checking" without actually saving the results). +// +// InputRule defines custom input loading, but its shared across all +// Generators. There's currently only a filesystem implementation. +// +// Runtime and Context +// +// Runtime maps together Generators, and constructs "contexts" which provide +// the common collector and roots, plus the output rule for that generator, and +// a handle for reading files (like boilerplate headers). +// +// It will run all associated generators, printing errors and automatically +// skipping type-checking errors (since those are commonly caused by the +// partial type-checking of loader.TypeChecker). +// +// Options +// +// The FromOptions (and associated helpers) function makes it easy to use generators +// and output rules as markers that can be parsed from the command line, producing +// a registry from command line args. +package genall diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/genall/genall.go b/vendor/sigs.k8s.io/controller-tools/pkg/genall/genall.go new file mode 100644 index 0000000000..9fcc76236b --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/genall/genall.go @@ -0,0 +1,178 @@ +/* +Copyright 2019 The Kubernetes 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 genall + +import ( + "fmt" + "io" + "io/ioutil" + "os" + + "golang.org/x/tools/go/packages" + "sigs.k8s.io/yaml" + + "sigs.k8s.io/controller-tools/pkg/loader" + "sigs.k8s.io/controller-tools/pkg/markers" +) + +// Generators are a list of Generators. +// NB(directxman12): this is a pointer so that we can uniquely identify each +// instance of a generator, even if it's not hashable. Different *instances* +// of a generator are treated differently. +type Generators []*Generator + +// RegisterMarkers registers all markers defined by each of the Generators in +// this list into the given registry. +func (g Generators) RegisterMarkers(reg *markers.Registry) error { + for _, gen := range g { + if err := (*gen).RegisterMarkers(reg); err != nil { + return err + } + } + return nil +} + +// Generator knows how to register some set of markers, and then produce +// output artifacts based on loaded code containing those markers, +// sharing common loaded data. +type Generator interface { + // RegisterMarkers registers all markers needed by this Generator + // into the given registry. + RegisterMarkers(into *markers.Registry) error + // Generate generates artifacts produced by this marker. + // It's called *after* RegisterMarkers has been called. + Generate(*GenerationContext) error +} + +// HasHelp is some Generator, OutputRule, etc with a help method. +type HasHelp interface { + // Help returns help for this generator. + Help() *markers.DefinitionHelp +} + +// Runtime collects generators, loaded program data (Collector, root Packages), +// and I/O rules, running them together. +type Runtime struct { + // Generators are the Generators to be run by this Runtime. + Generators Generators + // GenerationContext is the base generation context that's copied + // to produce the context for each Generator. + GenerationContext + // OutputRules defines how to output artifacts for each Generator. + OutputRules OutputRules +} + +// GenerationContext defines the common information needed for each Generator +// to run. +type GenerationContext struct { + // Collector is the shared marker collector. + Collector *markers.Collector + // Roots are the base packages to be processed. + Roots []*loader.Package + // Checker is the shared partial type-checker. + Checker *loader.TypeChecker + // OutputRule describes how to output artifacts. + OutputRule + // InputRule describes how to load associated boilerplate artifacts. + // It should *not* be used to load source files. + InputRule +} + +// WriteYAML writes the given objects out, serialized as YAML, using the +// context's OutputRule. Objects are written as separate documents, separated +// from each other by `---` (as per the YAML spec). +func (g GenerationContext) WriteYAML(itemPath string, objs ...interface{}) error { + out, err := g.Open(nil, itemPath) + if err != nil { + return err + } + defer out.Close() + + for _, obj := range objs { + yamlContent, err := yaml.Marshal(obj) + if err != nil { + return err + } + n, err := out.Write(append([]byte("\n---\n"), yamlContent...)) + if err != nil { + return err + } + if n < len(yamlContent) { + return io.ErrShortWrite + } + } + + return nil +} + +// ReadFile reads the given boilerplate artifact using the context's InputRule. +func (g GenerationContext) ReadFile(path string) ([]byte, error) { + file, err := g.OpenForRead(path) + if err != nil { + return nil, err + } + defer file.Close() + return ioutil.ReadAll(file) +} + +// ForRoots produces a Runtime to run the given generators against the +// given packages. It outputs to /dev/null by default. +func (g Generators) ForRoots(rootPaths ...string) (*Runtime, error) { + roots, err := loader.LoadRoots(rootPaths...) + if err != nil { + return nil, err + } + rt := &Runtime{ + Generators: g, + GenerationContext: GenerationContext{ + Collector: &markers.Collector{ + Registry: &markers.Registry{}, + }, + Roots: roots, + InputRule: InputFromFileSystem, + Checker: &loader.TypeChecker{}, + }, + OutputRules: OutputRules{Default: OutputToNothing}, + } + if err := rt.Generators.RegisterMarkers(rt.Collector.Registry); err != nil { + return nil, err + } + return rt, nil +} + +// Run runs the Generators in this Runtime against its packages, printing +// errors (except type errors, which common result from using TypeChecker with +// filters), returning true if errors were found. +func (r *Runtime) Run() bool { + // TODO(directxman12): we could make this parallel, + // but we'd need to ensure all underlying machinery is threadsafe + if len(r.Generators) == 0 { + fmt.Fprintln(os.Stderr, "no generators to run") + return true + } + + for _, gen := range r.Generators { + ctx := r.GenerationContext // make a shallow copy + ctx.OutputRule = r.OutputRules.ForGenerator(gen) + if err := (*gen).Generate(&ctx); err != nil { + fmt.Fprintln(os.Stderr, err) + } + } + + // skip TypeErrors -- they're probably just from partial typechecking in crd-gen + return loader.PrintErrors(r.Roots, packages.TypeError) +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/genall/help/doc.go b/vendor/sigs.k8s.io/controller-tools/pkg/genall/help/doc.go new file mode 100644 index 0000000000..d84d1798b4 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/genall/help/doc.go @@ -0,0 +1,23 @@ +/* +Copyright 2019 The Kubernetes 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 help contains utilities for actually writing out marker help. +// +// Namely, it contains a series of structs (and helpers for producing them) +// that represent a merged view of marker definition and help that can be used +// for consumption by the pretty subpackage (for terminal help) or serialized +// as JSON (e.g. for generating HTML help). +package help diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/genall/help/pretty/doc.go b/vendor/sigs.k8s.io/controller-tools/pkg/genall/help/pretty/doc.go new file mode 100644 index 0000000000..06622db744 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/genall/help/pretty/doc.go @@ -0,0 +1,30 @@ +/* +Copyright 2019 The Kubernetes 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 pretty contains utilities for formatting terminal help output, +// and a use of those to display marker help. +// +// Terminal Output +// +// The Span interface and Table struct allow you to construct tables with +// colored formatting, without causing ANSI formatting characters to mess up width +// calculations. +// +// Marker Help +// +// The MarkersSummary prints a summary table for marker help, while the MarkersDetails +// prints out more detailed information, with explainations of the individual marker fields. +package pretty diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/genall/help/pretty/help.go b/vendor/sigs.k8s.io/controller-tools/pkg/genall/help/pretty/help.go new file mode 100644 index 0000000000..3e34cc0d6c --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/genall/help/pretty/help.go @@ -0,0 +1,171 @@ +package pretty + +import ( + "fmt" + "io" + + "sigs.k8s.io/controller-tools/pkg/genall/help" + + "github.com/fatih/color" +) + +var ( + headingStyle = Decoration(*color.New(color.Bold, color.Underline)) + markerNameStyle = Decoration(*color.New(color.Bold)) + fieldSummaryStyle = Decoration(*color.New(color.FgGreen, color.Italic)) + markerTargetStyle = Decoration(*color.New(color.Faint)) + fieldDetailStyle = Decoration(*color.New(color.Italic, color.FgGreen)) + deprecatedStyle = Decoration(*color.New(color.CrossedOut)) +) + +// MarkersSummary returns a condensed summary of help for the given markers. +func MarkersSummary(groupName string, markers []help.MarkerDoc) Span { + out := new(SpanWriter) + + out.Print(Text("\n")) + out.Print(headingStyle.Containing(Text(groupName))) + out.Print(Text("\n\n")) + + table := &Table{Sizing: &TableCalculator{Padding: 2}} + for _, marker := range markers { + table.StartRow() + table.Column(MarkerSyntaxHelp(marker)) + table.Column(markerTargetStyle.Containing(Text(marker.Target))) + + summary := new(SpanWriter) + if marker.DeprecatedInFavorOf != nil && len(*marker.DeprecatedInFavorOf) > 0 { + summary.Print(markerNameStyle.Containing(Text("(use "))) + summary.Print(markerNameStyle.Containing(Text(*marker.DeprecatedInFavorOf))) + summary.Print(markerNameStyle.Containing(Text(") "))) + } + summary.Print(Text(marker.Summary)) + table.Column(summary) + + table.EndRow() + } + out.Print(table) + + out.Print(Text("\n")) + + return out +} + +// MarkersDetails returns detailed help for the given markers, including detailed field help. +func MarkersDetails(fullDetail bool, groupName string, markers []help.MarkerDoc) Span { + out := new(SpanWriter) + + out.Print(Line(headingStyle.Containing(Text(groupName)))) + out.Print(Newlines(2)) + + for _, marker := range markers { + out.Print(Line(markerName(marker))) + out.Print(Text(" ")) + out.Print(markerTargetStyle.Containing(Text(marker.Target))) + + summary := new(SpanWriter) + if marker.DeprecatedInFavorOf != nil && len(*marker.DeprecatedInFavorOf) > 0 { + summary.Print(markerNameStyle.Containing(Text("(use "))) + summary.Print(markerNameStyle.Containing(Text(*marker.DeprecatedInFavorOf))) + summary.Print(markerNameStyle.Containing(Text(") "))) + } + summary.Print(Text(marker.Summary)) + + if !marker.AnonymousField() { + out.Print(Indented(1, Line(summary))) + if len(marker.Details) > 0 && fullDetail { + out.Print(Indented(1, Line(Text(marker.Details)))) + } + } + + if marker.AnonymousField() { + out.Print(Indented(1, Line(fieldDetailStyle.Containing(FieldSyntaxHelp(marker.Fields[0]))))) + out.Print(Text(" ")) + out.Print(summary) + if len(marker.Details) > 0 && fullDetail { + out.Print(Indented(2, Line(Text(marker.Details)))) + } + out.Print(Newlines(1)) + } else if !marker.Empty() { + out.Print(Newlines(1)) + if fullDetail { + for _, arg := range marker.Fields { + out.Print(Indented(1, Line(fieldDetailStyle.Containing(FieldSyntaxHelp(arg))))) + out.Print(Indented(2, Line(Text(arg.Summary)))) + if len(arg.Details) > 0 && fullDetail { + out.Print(Indented(2, Line(Text(arg.Details)))) + out.Print(Newlines(1)) + } + } + out.Print(Newlines(1)) + } else { + table := &Table{Sizing: &TableCalculator{Padding: 2}} + for _, arg := range marker.Fields { + table.StartRow() + table.Column(fieldDetailStyle.Containing(FieldSyntaxHelp(arg))) + table.Column(Text(arg.Summary)) + table.EndRow() + } + + out.Print(Indented(1, table)) + } + } else { + out.Print(Newlines(1)) + } + } + + return out +} + +func FieldSyntaxHelp(arg help.FieldHelp) Span { + return fieldSyntaxHelp(arg, "") +} + +// fieldSyntaxHelp prints the syntax help for a particular marker argument. +func fieldSyntaxHelp(arg help.FieldHelp, sep string) Span { + if arg.Optional { + return FromWriter(func(out io.Writer) error { + _, err := fmt.Fprintf(out, "[%s%s=<%s>]", sep, arg.Name, arg.TypeString()) + return err + }) + } + return FromWriter(func(out io.Writer) error { + _, err := fmt.Fprintf(out, "%s%s=<%s>", sep, arg.Name, arg.TypeString()) + return err + }) +} + +// markerName returns a span containing just the appropriately-formatted marker name. +func markerName(def help.MarkerDoc) Span { + if def.DeprecatedInFavorOf != nil { + return deprecatedStyle.Containing(Text("+" + def.Name)) + } + return markerNameStyle.Containing(Text("+" + def.Name)) +} + +// MarkerSyntaxHelp assembles syntax help for a given marker. +func MarkerSyntaxHelp(def help.MarkerDoc) Span { + out := new(SpanWriter) + + out.Print(markerName(def)) + + if def.Empty() { + return out + } + + sep := ":" + if def.AnonymousField() { + sep = "" + } + + fieldStyle := fieldSummaryStyle + if def.DeprecatedInFavorOf != nil { + fieldStyle = deprecatedStyle + } + + for _, arg := range def.Fields { + out.Print(fieldStyle.Containing(fieldSyntaxHelp(arg, sep))) + sep = "," + } + + return out +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/genall/help/pretty/print.go b/vendor/sigs.k8s.io/controller-tools/pkg/genall/help/pretty/print.go new file mode 100644 index 0000000000..8d7452a0b2 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/genall/help/pretty/print.go @@ -0,0 +1,304 @@ +package pretty + +import ( + "bytes" + "fmt" + "io" + + "github.com/fatih/color" +) + +// NB(directxman12): this isn't particularly elegant, but it's also +// sufficiently simple as to be maintained here. Man (roff) would've +// probably worked, but it's not necessarily on Windows by default. + +// Span is a chunk of content that is writable to an output, but knows how to +// calculate its apparent visual "width" on the terminal (not to be confused +// with the raw length, which may include zero-width coloring sequences). +type Span interface { + // VisualLength reports the "width" as perceived by the user on the terminal + // (i.e. widest line, ignoring ANSI escape characters). + VisualLength() int + // WriteTo writes the full span contents to the given writer. + WriteTo(io.Writer) error +} + +// Table is a Span that writes its data in table form, with sizing controlled +// by the given table calculator. Rows are started with StartRow, followed by +// some calls to Column, followed by a call to EndRow. Once all rows are +// added, the table can be used as a Span. +type Table struct { + Sizing *TableCalculator + + cellsByRow [][]Span + colSizes []int +} + +// StartRow starts a new row. +// It must eventually be followed by EndRow. +func (t *Table) StartRow() { + t.cellsByRow = append(t.cellsByRow, []Span(nil)) +} + +// EndRow ends the currently started row. +func (t *Table) EndRow() { + lastRow := t.cellsByRow[len(t.cellsByRow)-1] + sizes := make([]int, len(lastRow)) + for i, cell := range lastRow { + sizes[i] = cell.VisualLength() + } + t.Sizing.AddRowSizes(sizes...) +} + +// Column adds the given span as a new column to the current row. +func (t *Table) Column(contents Span) { + currentRowInd := len(t.cellsByRow) - 1 + t.cellsByRow[currentRowInd] = append(t.cellsByRow[currentRowInd], contents) +} + +// SkipRow prints a span without having it contribute to the table calculation. +func (t *Table) SkipRow(contents Span) { + t.cellsByRow = append(t.cellsByRow, []Span{contents}) +} + +func (t *Table) WriteTo(out io.Writer) error { + if t.colSizes == nil { + t.colSizes = t.Sizing.ColumnWidths() + } + + for _, cells := range t.cellsByRow { + currentPosition := 0 + for colInd, cell := range cells { + colSize := t.colSizes[colInd] + diff := colSize - cell.VisualLength() + + if err := cell.WriteTo(out); err != nil { + return err + } + + if diff > 0 { + if err := writePadding(out, columnPadding, diff); err != nil { + return err + } + } + currentPosition += colSize + } + + if _, err := fmt.Fprint(out, "\n"); err != nil { + return err + } + } + + return nil +} + +func (t *Table) VisualLength() int { + if t.colSizes == nil { + t.colSizes = t.Sizing.ColumnWidths() + } + + res := 0 + for _, colSize := range t.colSizes { + res += colSize + } + return res +} + +// Text is a span that simply contains raw text. It's a good starting point. +type Text string + +func (t Text) VisualLength() int { return len(t) } +func (t Text) WriteTo(w io.Writer) error { + _, err := w.Write([]byte(t)) + return err +} + +// indented is a span that indents all lines by the given number of tabs. +type indented struct { + Amount int + Content Span +} + +func (i *indented) VisualLength() int { return i.Content.VisualLength() } +func (i *indented) WriteTo(w io.Writer) error { + var out bytes.Buffer + if err := i.Content.WriteTo(&out); err != nil { + return err + } + + lines := bytes.Split(out.Bytes(), []byte("\n")) + for lineInd, line := range lines { + if lineInd != 0 { + if _, err := w.Write([]byte("\n")); err != nil { + return err + } + } + if len(line) == 0 { + continue + } + + if err := writePadding(w, indentPadding, i.Amount); err != nil { + return err + } + if _, err := w.Write(line); err != nil { + return err + } + } + return nil +} + +// Indented returns a span that indents all lines by the given number of tabs. +func Indented(amt int, content Span) Span { + return &indented{Amount: amt, Content: content} +} + +// fromWriter is a span that takes content from a function expecting a Writer. +type fromWriter struct { + cache []byte + cacheError error + run func(io.Writer) error +} + +func (f *fromWriter) VisualLength() int { + if f.cache == nil { + var buf bytes.Buffer + if err := f.run(&buf); err != nil { + f.cacheError = err + } + f.cache = buf.Bytes() + } + return len(f.cache) +} +func (f *fromWriter) WriteTo(w io.Writer) error { + if f.cache != nil { + if f.cacheError != nil { + return f.cacheError + } + _, err := w.Write(f.cache) + return err + } + return f.run(w) +} + +// FromWriter returns a span that takes content from a function expecting a Writer. +func FromWriter(run func(io.Writer) error) Span { + return &fromWriter{run: run} +} + +// Decoration represents a terminal decoration. +type Decoration color.Color + +// Containing returns a Span that has the given decoration applied. +func (d Decoration) Containing(contents Span) Span { + return &decorated{ + Contents: contents, + Attributes: color.Color(d), + } +} + +// decorated is a span that has some terminal decoration applied. +type decorated struct { + Contents Span + Attributes color.Color +} + +func (d *decorated) VisualLength() int { return d.Contents.VisualLength() } +func (d *decorated) WriteTo(w io.Writer) error { + oldOut := color.Output + color.Output = w + defer func() { color.Output = oldOut }() + + d.Attributes.Set() + defer color.Unset() + + return d.Contents.WriteTo(w) +} + +// SpanWriter is a span that contains multiple sub-spans. +type SpanWriter struct { + contents []Span +} + +func (m *SpanWriter) VisualLength() int { + res := 0 + for _, span := range m.contents { + res += span.VisualLength() + } + return res +} +func (m *SpanWriter) WriteTo(w io.Writer) error { + for _, span := range m.contents { + if err := span.WriteTo(w); err != nil { + return err + } + } + return nil +} + +// Print adds a new span to this SpanWriter. +func (m *SpanWriter) Print(s Span) { + m.contents = append(m.contents, s) +} + +// lines is a span that adds some newlines, optionally followed by some content. +type lines struct { + content Span + amountBefore int +} + +func (l *lines) VisualLength() int { + if l.content == nil { + return 0 + } + return l.content.VisualLength() +} +func (l *lines) WriteTo(w io.Writer) error { + if err := writePadding(w, linesPadding, l.amountBefore); err != nil { + return err + } + if l.content != nil { + if err := l.content.WriteTo(w); err != nil { + return err + } + } + return nil +} + +// Newlines returns a span just containing some newlines. +func Newlines(amt int) Span { + return &lines{amountBefore: amt} +} + +// Line returns a span that emits a newline, followed by the given content. +func Line(content Span) Span { + return &lines{amountBefore: 1, content: content} +} + +var ( + columnPadding = []byte(" ") + indentPadding = []byte("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t") + linesPadding = []byte("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n") +) + +// writePadding writes out padding of the given type in the given amount to the writer. +// Each byte in the padding buffer contributes 1 to the amount -- the padding being +// a buffer is just for efficiency. +func writePadding(out io.Writer, typ []byte, amt int) error { + if amt <= len(typ) { + _, err := out.Write(typ[:amt]) + return err + } + + num := amt / len(typ) + rem := amt % len(typ) + for i := 0; i < num; i++ { + if _, err := out.Write(typ); err != nil { + return err + } + } + + if _, err := out.Write(typ[:rem]); err != nil { + return err + } + return nil +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/genall/help/pretty/table.go b/vendor/sigs.k8s.io/controller-tools/pkg/genall/help/pretty/table.go new file mode 100644 index 0000000000..5a0b4752af --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/genall/help/pretty/table.go @@ -0,0 +1,64 @@ +/* +Copyright 2019 The Kubernetes 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 pretty + +// TableCalculator calculates column widths (with optional padding) +// for a table based on the maximum required column width. +type TableCalculator struct { + cellSizesByCol [][]int + + Padding int + MaxWidth int +} + +// AddRowSizes registers a new row with cells of the given sizes. +func (c *TableCalculator) AddRowSizes(cellSizes ...int) { + if len(cellSizes) > len(c.cellSizesByCol) { + for range cellSizes[len(c.cellSizesByCol):] { + c.cellSizesByCol = append(c.cellSizesByCol, []int(nil)) + } + } + for i, size := range cellSizes { + c.cellSizesByCol[i] = append(c.cellSizesByCol[i], size) + } +} + +// ColumnWidths calculates the appropriate column sizes given the +// previously registered rows. +func (c *TableCalculator) ColumnWidths() []int { + maxColWidths := make([]int, len(c.cellSizesByCol)) + + for colInd, cellSizes := range c.cellSizesByCol { + max := 0 + for _, cellSize := range cellSizes { + if max < cellSize { + max = cellSize + } + } + maxColWidths[colInd] = max + } + + actualMaxWidth := c.MaxWidth - c.Padding + for i, width := range maxColWidths { + if actualMaxWidth > 0 && width > actualMaxWidth { + maxColWidths[i] = actualMaxWidth + } + maxColWidths[i] += c.Padding + } + + return maxColWidths +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/genall/help/sort.go b/vendor/sigs.k8s.io/controller-tools/pkg/genall/help/sort.go new file mode 100644 index 0000000000..53c923e34b --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/genall/help/sort.go @@ -0,0 +1,106 @@ +/* +Copyright 2019 The Kubernetes 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 help + +import ( + "strings" + + "sigs.k8s.io/controller-tools/pkg/markers" +) + +// SortGroup knows how to sort and group marker definitions. +type SortGroup interface { + // Less is equivalent to the Less function from sort, and is used to sort the markers. + Less(*markers.Definition, *markers.Definition) bool + // Group returns the "group" that a given marker belongs to. + Group(*markers.Definition, *markers.DefinitionHelp) string +} + +var ( + // SortByCategory sorts the markers by name and groups them by their help category. + SortByCategory = sortByCategory{} + + // SortByOption sorts by the generator that the option belongs to. + SortByOption = optionsSort{} +) + +type sortByCategory struct{} + +func (sortByCategory) Group(_ *markers.Definition, help *markers.DefinitionHelp) string { + if help == nil { + return "" + } + return help.Category +} +func (sortByCategory) Less(i, j *markers.Definition) bool { + return i.Name < j.Name +} + +type optionsSort struct{} + +func (optionsSort) Less(i, j *markers.Definition) bool { + iParts := strings.Split(i.Name, ":") + jParts := strings.Split(j.Name, ":") + + iGen := "" + iRule := "" + jGen := "" + jRule := "" + + switch len(iParts) { + case 1: + iGen = iParts[0] + // two means a default output rule, so ignore + case 2: + iRule = iParts[1] + case 3: + iGen = iParts[1] + iRule = iParts[2] + } + switch len(jParts) { + case 1: + jGen = jParts[0] + // two means a default output rule, so ignore + case 2: + jRule = jParts[1] + case 3: + jGen = jParts[1] + jRule = jParts[2] + } + + if iGen != jGen { + return iGen > jGen + } + + return iRule < jRule +} +func (optionsSort) Group(def *markers.Definition, _ *markers.DefinitionHelp) string { + parts := strings.Split(def.Name, ":") + + switch len(parts) { + case 1: + if parts[0] == "paths" { + return "generic" + } + return "generators" + case 2: + return "output rules (optionally as output::...)" + default: + return "" + // three means a marker-specific output rule, ignore + } +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/genall/help/types.go b/vendor/sigs.k8s.io/controller-tools/pkg/genall/help/types.go new file mode 100644 index 0000000000..be11104324 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/genall/help/types.go @@ -0,0 +1,215 @@ +/* +Copyright 2019 The Kubernetes 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 help + +import ( + "sort" + "strings" + + "sigs.k8s.io/controller-tools/pkg/markers" +) + +// DetailedHelp contains both a summary and further details. +type DetailedHelp struct { + // Summary contains a one-line description. + Summary string `json:"summary"` + // Details contains further information. + Details string `json:"details,omitempty"` +} + +// Argument is the type data for a marker argument. +type Argument struct { + // Type is the data type of the argument (string, bool, int, slice, any, raw, invalid) + Type string `json:"type"` + // Optional marks this argument as optional. + Optional bool `json:"optional"` + // ItemType contains the type of the slice item, if this is a slice + ItemType *Argument `json:"itemType,omitempty"` +} + +func (a Argument) typeString(out *strings.Builder) { + if a.Type == "slice" { + out.WriteString("[]") + a.ItemType.typeString(out) + return + } + + out.WriteString(a.Type) +} + +// TypeString returns a string roughly equivalent +// (but not identical) to the underlying Go type that +// this argument would parse to. It's mainly useful +// for user-friendly formatting of this argument (e.g. +// help strings). +func (a Argument) TypeString() string { + out := &strings.Builder{} + a.typeString(out) + return out.String() +} + +// FieldHelp contains information required to print documentation for a marker field. +type FieldHelp struct { + // Name is the field name. + Name string `json:"name"` + // Argument is the type of the field. + Argument `json:",inline"` + + // DetailedHelp contains the textual help for the field. + DetailedHelp `json:",inline"` +} + +// MarkerDoc contains information required to print documentation for a marker. +type MarkerDoc struct { + // definition + + // Name is the name of the marker. + Name string `json:"name"` + // Target is the target (field, package, type) of the marker. + Target string `json:"target"` + + // help + + // DetailedHelp is the textual help for the marker. + DetailedHelp `json:",inline"` + // Category is the general "category" that this marker belongs to. + Category string `json:"category"` + // DeprecatedInFavorOf marks that this marker shouldn't be used when + // non-nil. If also non-empty, another marker should be used instead. + DeprecatedInFavorOf *string `json:"deprecatedInFavorOf,omitempty"` + // Fields is the type and help data for each field of this marker. + Fields []FieldHelp `json:"fields,omitempty"` +} + +// Empty checks if this marker has any arguments, returning true if not. +func (m MarkerDoc) Empty() bool { + return len(m.Fields) == 0 +} + +// AnonymousField chekcs if this is an single-valued marker +// (as opposed to having named fields). +func (m MarkerDoc) AnonymousField() bool { + return len(m.Fields) == 1 && m.Fields[0].Name == "" +} + +// ForArgument returns the equivalent documentation for a marker argument. +func ForArgument(argRaw markers.Argument) Argument { + res := Argument{ + Optional: argRaw.Optional, + } + + if argRaw.ItemType != nil { + itemType := ForArgument(*argRaw.ItemType) + res.ItemType = &itemType + } + + switch argRaw.Type { + case markers.IntType: + res.Type = "int" + case markers.StringType: + res.Type = "string" + case markers.BoolType: + res.Type = "bool" + case markers.AnyType: + res.Type = "any" + case markers.SliceType: + res.Type = "slice" + case markers.RawType: + res.Type = "raw" + case markers.InvalidType: + res.Type = "invalid" + } + + return res +} + +// ForDefinition returns the equivalent marker documentation for a given marker definition and spearate help. +func ForDefinition(defn *markers.Definition, maybeHelp *markers.DefinitionHelp) MarkerDoc { + var help markers.DefinitionHelp + if maybeHelp != nil { + help = *maybeHelp + } + + res := MarkerDoc{ + Name: defn.Name, + Category: help.Category, + DeprecatedInFavorOf: help.DeprecatedInFavorOf, + Target: defn.Target.String(), + DetailedHelp: DetailedHelp{Summary: help.Summary, Details: help.Details}, + } + + helpByField := help.FieldsHelp(defn) + + // TODO(directxman12): deterministic ordering + for fieldName, fieldHelpRaw := range helpByField { + fieldInfo := defn.Fields[fieldName] + fieldHelp := FieldHelp{ + Name: fieldName, + DetailedHelp: DetailedHelp{Summary: fieldHelpRaw.Summary, Details: fieldHelpRaw.Details}, + Argument: ForArgument(fieldInfo), + } + + res.Fields = append(res.Fields, fieldHelp) + } + + sort.Slice(res.Fields, func(i, j int) bool { return res.Fields[i].Name < res.Fields[j].Name }) + + return res +} + +// CategoryDoc contains help information for all markers in a Category. +type CategoryDoc struct { + Category string `json:"category"` + Markers []MarkerDoc `json:"markers"` +} + +// ByCategory returns the marker help for markers in the given +// registry, grouped and sorted according to the given method. +func ByCategory(reg *markers.Registry, sorter SortGroup) []CategoryDoc { + groupedMarkers := make(map[string][]*markers.Definition) + + for _, marker := range reg.AllDefinitions() { + group := sorter.Group(marker, reg.HelpFor(marker)) + groupedMarkers[group] = append(groupedMarkers[group], marker) + } + allGroups := make([]string, 0, len(groupedMarkers)) + for groupName := range groupedMarkers { + allGroups = append(allGroups, groupName) + } + + sort.Strings(allGroups) + + res := make([]CategoryDoc, len(allGroups)) + for i, groupName := range allGroups { + markers := groupedMarkers[groupName] + sort.Slice(markers, func(i, j int) bool { + return sorter.Less(markers[i], markers[j]) + }) + + markerDocs := make([]MarkerDoc, len(markers)) + for i, marker := range markers { + markerDocs[i] = ForDefinition(marker, reg.HelpFor(marker)) + } + + res[i] = CategoryDoc{ + Category: groupName, + Markers: markerDocs, + } + } + + return res +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/genall/input.go b/vendor/sigs.k8s.io/controller-tools/pkg/genall/input.go new file mode 100644 index 0000000000..46e191c0c2 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/genall/input.go @@ -0,0 +1,37 @@ +/* +Copyright 2019 The Kubernetes 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 genall + +import ( + "io" + "os" +) + +// InputRule describes how to load non-code boilerplate artifacts. +// It's not used for loading code. +type InputRule interface { + // OpenForRead opens the given non-code artifact for reading. + OpenForRead(path string) (io.ReadCloser, error) +} +type inputFromFileSystem struct{} + +func (inputFromFileSystem) OpenForRead(path string) (io.ReadCloser, error) { + return os.Open(path) +} + +// InputFromFileSystem reads from the filesystem as normal. +var InputFromFileSystem = inputFromFileSystem{} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/genall/options.go b/vendor/sigs.k8s.io/controller-tools/pkg/genall/options.go new file mode 100644 index 0000000000..31076a5cb7 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/genall/options.go @@ -0,0 +1,192 @@ +/* +Copyright 2019 The Kubernetes 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 genall + +import ( + "fmt" + "strings" + + "sigs.k8s.io/controller-tools/pkg/markers" +) + +var ( + InputPathsMarker = markers.Must(markers.MakeDefinition("paths", markers.DescribesPackage, InputPaths(nil))) +) + +// +controllertools:marker:generateHelp:category="" + +// InputPaths represents paths and go-style path patterns to use as package roots. +type InputPaths []string + +// RegisterOptionsMarkers registers "mandatory" options markers for FromOptions into the given registry. +// At this point, that's just InputPaths. +func RegisterOptionsMarkers(into *markers.Registry) error { + if err := into.Register(InputPathsMarker); err != nil { + return err + } + // NB(directxman12): we make this optional so we don't have a bootstrap problem with helpgen + if helpGiver, hasHelp := ((interface{})(InputPaths(nil))).(HasHelp); hasHelp { + into.AddHelp(InputPathsMarker, helpGiver.Help()) + } + return nil +} + +// RegistryFromOptions produces just the marker registry that would be used by FromOptions, without +// attempting to produce a full Runtime. This can be useful if you want to display help without +// trying to load roots. +func RegistryFromOptions(optionsRegistry *markers.Registry, options []string) (*markers.Registry, error) { + protoRt, err := protoFromOptions(optionsRegistry, options) + if err != nil { + return nil, err + } + reg := &markers.Registry{} + if err := protoRt.Generators.RegisterMarkers(reg); err != nil { + return nil, err + } + return reg, nil +} + +// FromOptions parses the options from markers stored in the given registry out into a runtime. +// The markers in the registry must be either +// +// a) Generators +// b) OutputRules +// c) InputPaths +// +// The paths specified in InputPaths are loaded as package roots, and the combined with +// the generators and the specified output rules to produce a runtime that can be run or +// further modified. Not default generators are used if none are specified -- you can check +// the output and rerun for that. +func FromOptions(optionsRegistry *markers.Registry, options []string) (*Runtime, error) { + + protoRt, err := protoFromOptions(optionsRegistry, options) + if err != nil { + return nil, err + } + + // make the runtime + genRuntime, err := protoRt.Generators.ForRoots(protoRt.Paths...) + if err != nil { + return nil, err + } + + // attempt to figure out what the user wants without a lot of verbose specificity: + // if the user specifies a default rule, assume that they probably want to fall back + // to that. Otherwise, assume that they just wanted to customize one option from the + // set, and leave the rest in the standard configuration. + if protoRt.OutputRules.Default != nil { + genRuntime.OutputRules = protoRt.OutputRules + return genRuntime, nil + } + + outRules := DirectoryPerGenerator("config", protoRt.GeneratorsByName) + for gen, rule := range protoRt.OutputRules.ByGenerator { + outRules.ByGenerator[gen] = rule + } + + genRuntime.OutputRules = outRules + return genRuntime, nil +} + +// protoFromOptions returns a proto-Runtime from the given options registry and +// options set. This can then be used to construct an actual Runtime. See the +// FromOptions function for more details about how the options work. +func protoFromOptions(optionsRegistry *markers.Registry, options []string) (protoRuntime, error) { + var gens Generators + rules := OutputRules{ + ByGenerator: make(map[*Generator]OutputRule), + } + var paths []string + + // collect the generators first, so that we can key the output on the actual + // generator, which matters if there's settings in the gen object and it's not a pointer. + outputByGen := make(map[string]OutputRule) + gensByName := make(map[string]*Generator) + + for _, rawOpt := range options { + if rawOpt[0] != '+' { + rawOpt = "+" + rawOpt // add a `+` to make it acceptable for usage with the registry + } + defn := optionsRegistry.Lookup(rawOpt, markers.DescribesPackage) + if defn == nil { + return protoRuntime{}, fmt.Errorf("unknown option %q", rawOpt[1:]) + } + + val, err := defn.Parse(rawOpt) + if err != nil { + return protoRuntime{}, fmt.Errorf("unable to parse option %q: %v", rawOpt[1:], err) + } + + switch val := val.(type) { + case Generator: + gens = append(gens, &val) + gensByName[defn.Name] = &val + case OutputRule: + _, genName := splitOutputRuleOption(defn.Name) + if genName == "" { + // it's a default rule + rules.Default = val + continue + } + + outputByGen[genName] = val + continue + case InputPaths: + paths = append(paths, val...) + default: + return protoRuntime{}, fmt.Errorf("unknown option marker %q", defn.Name) + } + } + + // actually associate the rules now that we know the generators + for genName, outputRule := range outputByGen { + gen, knownGen := gensByName[genName] + if !knownGen { + return protoRuntime{}, fmt.Errorf("non-invoked generator %q", genName) + } + + rules.ByGenerator[gen] = outputRule + } + + return protoRuntime{ + Paths: paths, + Generators: Generators(gens), + OutputRules: rules, + GeneratorsByName: gensByName, + }, nil +} + +// protoRuntime represents the raw pieces needed to compose a runtime, as +// parsed from some options. +type protoRuntime struct { + Paths []string + Generators Generators + OutputRules OutputRules + GeneratorsByName map[string]*Generator +} + +// splitOutputRuleOption splits a marker name of "output:rule:gen" or "output:rule" +// into its compontent rule and generator name. +func splitOutputRuleOption(name string) (ruleName string, genName string) { + parts := strings.SplitN(name, ":", 3) + if len(parts) == 3 { + // output:: + return parts[2], parts[1] + } + // output: + return parts[1], "" +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/genall/output.go b/vendor/sigs.k8s.io/controller-tools/pkg/genall/output.go new file mode 100644 index 0000000000..b5a09e1692 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/genall/output.go @@ -0,0 +1,160 @@ +/* +Copyright 2019 The Kubernetes 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 genall + +import ( + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + + "sigs.k8s.io/controller-tools/pkg/loader" +) + +// nopCloser is a WriteCloser whose Close +// is a no-op. +type nopCloser struct { + io.Writer +} + +func (n nopCloser) Close() error { + return nil +} + +// DirectoryPerGenerator produces output rules mapping output to a different subdirectory +// of the given base directory for each generator (with each subdirectory specified as +// the key in the input map). +func DirectoryPerGenerator(base string, generators map[string]*Generator) OutputRules { + rules := OutputRules{ + Default: OutputArtifacts{Config: OutputToDirectory(base)}, + ByGenerator: make(map[*Generator]OutputRule, len(generators)), + } + + for name, gen := range generators { + rules.ByGenerator[gen] = OutputArtifacts{ + Config: OutputToDirectory(filepath.Join(base, name)), + } + } + + return rules +} + +// OutputRules defines how to output artificats on a per-generator basis. +type OutputRules struct { + // Default is the output rule used when no specific per-generator overrides match. + Default OutputRule + // ByGenerator contains specific per-generator overrides. + // NB(directxman12): this is a pointer to avoid issues if a given Generator becomes unhashable + // (interface values compare by "dereferencing" their internal pointer first, whereas pointers + // compare by the actual pointer itself). + ByGenerator map[*Generator]OutputRule +} + +// ForGenerator returns the output rule that should be used +// by the given Generator. +func (o OutputRules) ForGenerator(gen *Generator) OutputRule { + if forGen, specific := o.ByGenerator[gen]; specific { + return forGen + } + return o.Default +} + +// OutputRule defines how to output artifacts from a generator. +type OutputRule interface { + // Open opens the given artifact path for writing. If a package is passed, + // the artifact is considered to be used as part of the package (e.g. + // generated code), while a nil package indicates that the artifact is + // config (or something else not involved in Go compilation). + Open(pkg *loader.Package, path string) (io.WriteCloser, error) +} + +// OutputToNothing skips outputting anything. +var OutputToNothing = outputToNothing{} + +// +controllertools:marker:generateHelp:category="" + +// outputToNothing skips outputting anything. +type outputToNothing struct{} + +func (o outputToNothing) Open(_ *loader.Package, _ string) (io.WriteCloser, error) { + return nopCloser{ioutil.Discard}, nil +} + +// +controllertools:marker:generateHelp:category="" + +// OutputToDirectory outputs each artifact to the given directory, regardless +// of if it's package-associated or not. +type OutputToDirectory string + +func (o OutputToDirectory) Open(_ *loader.Package, itemPath string) (io.WriteCloser, error) { + // ensure the directory exists + if err := os.MkdirAll(string(o), os.ModePerm); err != nil { + return nil, err + } + path := filepath.Join(string(o), itemPath) + return os.Create(path) +} + +// OutputToStdout outputs everything to standard-out, with no separation. +// +// Generally useful for single-artifact outputs. +var OutputToStdout = outputToStdout{} + +// +controllertools:marker:generateHelp:category="" + +// outputToStdout outputs everything to standard-out, with no separation. +// +// Generally useful for single-artifact outputs. +type outputToStdout struct{} + +func (o outputToStdout) Open(_ *loader.Package, itemPath string) (io.WriteCloser, error) { + return nopCloser{os.Stdout}, nil +} + +// +controllertools:marker:generateHelp:category="" + +// OutputArtifacts outputs artifacts to different locations, depending on +// whether they're package-associated or not. +// +// Non-package associated artifacts +// are output to the Config directory, while package-associated ones are output +// to their package's source files' directory, unless an alternate path is +// specified in Code. +type OutputArtifacts struct { + // Config points to the directory to which to write configuration. + Config OutputToDirectory + // Code overrides the directory in which to write new code (defaults to where the existing code lives). + Code OutputToDirectory `marker:",optional"` +} + +func (o OutputArtifacts) Open(pkg *loader.Package, itemPath string) (io.WriteCloser, error) { + if pkg == nil { + return o.Config.Open(pkg, itemPath) + } + + if o.Code != "" { + return o.Code.Open(pkg, itemPath) + } + + if len(pkg.CompiledGoFiles) == 0 { + return nil, fmt.Errorf("cannot output to a package with no path on disk") + } + outDir := filepath.Dir(pkg.CompiledGoFiles[0]) + outPath := filepath.Join(outDir, itemPath) + return os.Create(outPath) +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/genall/zz_generated.markerhelp.go b/vendor/sigs.k8s.io/controller-tools/pkg/genall/zz_generated.markerhelp.go new file mode 100644 index 0000000000..824d3af3f8 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/genall/zz_generated.markerhelp.go @@ -0,0 +1,89 @@ +// +build !ignore_autogenerated + +/* +Copyright2019 The Kubernetes 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. +*/ + +// Code generated by helpgen. DO NOT EDIT. + +package genall + +import ( + "sigs.k8s.io/controller-tools/pkg/markers" +) + +func (InputPaths) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "", + DetailedHelp: markers.DetailedHelp{ + Summary: "represents paths and go-style path patterns to use as package roots.", + Details: "", + }, + FieldHelp: map[string]markers.DetailedHelp{}, + } +} + +func (OutputArtifacts) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "", + DetailedHelp: markers.DetailedHelp{ + Summary: "outputs artifacts to different locations, depending on whether they're package-associated or not. ", + Details: "Non-package associated artifacts are output to the Config directory, while package-associated ones are output to their package's source files' directory, unless an alternate path is specified in Code.", + }, + FieldHelp: map[string]markers.DetailedHelp{ + "Config": markers.DetailedHelp{ + Summary: "points to the directory to which to write configuration.", + Details: "", + }, + "Code": markers.DetailedHelp{ + Summary: "overrides the directory in which to write new code (defaults to where the existing code lives).", + Details: "", + }, + }, + } +} + +func (OutputToDirectory) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "", + DetailedHelp: markers.DetailedHelp{ + Summary: "outputs each artifact to the given directory, regardless of if it's package-associated or not.", + Details: "", + }, + FieldHelp: map[string]markers.DetailedHelp{}, + } +} + +func (outputToNothing) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "", + DetailedHelp: markers.DetailedHelp{ + Summary: "skips outputting anything.", + Details: "", + }, + FieldHelp: map[string]markers.DetailedHelp{}, + } +} + +func (outputToStdout) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "", + DetailedHelp: markers.DetailedHelp{ + Summary: "outputs everything to standard-out, with no separation. ", + Details: "Generally useful for single-artifact outputs.", + }, + FieldHelp: map[string]markers.DetailedHelp{}, + } +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/loader/doc.go b/vendor/sigs.k8s.io/controller-tools/pkg/loader/doc.go new file mode 100644 index 0000000000..a80065ec74 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/loader/doc.go @@ -0,0 +1,60 @@ +/* +Copyright 2019 The Kubernetes 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 loader defines helpers for loading packages from sources. It wraps +// go/packages, allow incremental loading of source code and manual control +// over which packages get type-checked. This allows for faster loading in +// cases where you don't actually care about certain imports. +// +// Because it uses go/packages, it's modules-aware, and works in both modules- +// and non-modules environments. +// +// Loading +// +// The main entrypoint for loading is LoadRoots, which traverse the package +// graph starting at the given patterns (file, package, path, or ...-wildcard, +// as one might pass to go list). Packages beyond the roots can be accessed +// via the Imports() method. Packages are initially loaded with export data +// paths, filenames, and imports. +// +// Packages are suitable for comparison, as each unique package only ever has +// one *Package object returned. +// +// Syntax and TypeChecking +// +// ASTs and type-checking information can be loaded with NeedSyntax and +// NeedTypesInfo, respectively. Both are idempotent -- repeated calls will +// simply re-use the cached contents. Note that NeedTypesInfo will *only* type +// check the current package -- if you want to type-check imports as well, +// you'll need to type-check them first. +// +// Reference Pruning and Recursive Checking +// +// In order to type-check using only the packages you care about, you can use a +// TypeChecker. TypeChecker will visit each top-level type declaration, +// collect (optionally filtered) references, and type-check references +// packages. +// +// Errors +// +// Errors can be added to each package. Use ErrFromNode to create an error +// from an AST node. Errors can then be printed (complete with file and +// position information) using PrintErrors, optionally filtered by error type. +// It's generally a good idea to filter out TypeErrors when doing incomplete +// type-checking with TypeChecker. You can use MaybeErrList to return multiple +// errors if you need to return an error instead of adding it to a package. +// AddError will later unroll it into individual errors. +package loader diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/loader/errors.go b/vendor/sigs.k8s.io/controller-tools/pkg/loader/errors.go new file mode 100644 index 0000000000..aae8ef39f7 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/loader/errors.go @@ -0,0 +1,63 @@ +/* +Copyright 2019 The Kubernetes 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 loader + +import ( + "fmt" + "go/ast" + "go/token" +) + +// PositionedError represents some error with an associated position. +// The position is tied to some external token.FileSet. +type PositionedError struct { + Pos token.Pos + error +} + +// ErrFromNode returns the given error, with additional information +// attaching it to the given AST node. It will automatically map +// over error lists. +func ErrFromNode(err error, node ast.Node) error { + if asList, isList := err.(ErrList); isList { + resList := make(ErrList, len(asList)) + for i, baseErr := range asList { + resList[i] = ErrFromNode(baseErr, node) + } + return resList + } + return PositionedError{ + Pos: node.Pos(), + error: err, + } +} + +// MaybeErrList constructs an ErrList if the given list of +// errors has any errors, otherwise returning nil. +func MaybeErrList(errs []error) error { + if len(errs) == 0 { + return nil + } + return ErrList(errs) +} + +// ErrList is a list of errors aggregated together into a single error. +type ErrList []error + +func (l ErrList) Error() string { + return fmt.Sprintf("%v", []error(l)) +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/loader/loader.go b/vendor/sigs.k8s.io/controller-tools/pkg/loader/loader.go new file mode 100644 index 0000000000..7b3dff30f8 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/loader/loader.go @@ -0,0 +1,361 @@ +/* +Copyright 2019 The Kubernetes 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 loader + +import ( + "fmt" + "go/ast" + "go/parser" + "go/scanner" + "go/token" + "go/types" + "io/ioutil" + "os" + "sync" + + "golang.org/x/tools/go/packages" +) + +// Much of this is strongly inspired by the contents of go/packages, +// except that it allows for lazy loading of syntax and type-checking +// information to speed up cases where full traversal isn't needed. + +// PrintErrors print errors associated with all packages +// in the given package graph, starting at the given root +// packages and traversing through all imports. It will skip +// any errors of the kinds specified in filterKinds. It will +// return true if any errors were printed. +func PrintErrors(pkgs []*Package, filterKinds ...packages.ErrorKind) bool { + pkgsRaw := make([]*packages.Package, len(pkgs)) + for i, pkg := range pkgs { + pkgsRaw[i] = pkg.Package + } + toSkip := make(map[packages.ErrorKind]struct{}) + for _, errKind := range filterKinds { + toSkip[errKind] = struct{}{} + } + hadErrors := false + packages.Visit(pkgsRaw, nil, func(pkgRaw *packages.Package) { + for _, err := range pkgRaw.Errors { + if _, skip := toSkip[err.Kind]; skip { + continue + } + hadErrors = true + fmt.Fprintln(os.Stderr, err) + } + }) + return hadErrors +} + +// Package is a single, unique Go package that can be +// lazily parsed and type-checked. Packages should not +// be constructed directly -- instead, use LoadRoots. +// For a given call to LoadRoots, only a single instance +// of each package exists, and thus they may be used as keys +// and for comparison. +type Package struct { + *packages.Package + + imports map[string]*Package + + loader *loader + sync.Mutex +} + +// Imports returns the imports for the given package, indexed by +// package path (*not* name in any particular file). +func (p *Package) Imports() map[string]*Package { + if p.imports == nil { + p.imports = p.loader.packagesFor(p.Package.Imports) + } + + return p.imports +} + +// NeedTypesInfo indicates that type-checking information is needed for this package. +// Actual type-checking information can be accessed via the Types and TypesInfo fields. +func (p *Package) NeedTypesInfo() { + if p.TypesInfo != nil { + return + } + p.NeedSyntax() + p.loader.typeCheck(p) +} + +// NeedSyntax indicates that a parsed AST is needed for this package. +// Actual ASTs can be accessed via the Syntax field. +func (p *Package) NeedSyntax() { + if p.Syntax != nil { + return + } + out := make([]*ast.File, len(p.CompiledGoFiles)) + var wg sync.WaitGroup + wg.Add(len(p.CompiledGoFiles)) + for i, filename := range p.CompiledGoFiles { + go func(i int, filename string) { + defer wg.Done() + src, err := ioutil.ReadFile(filename) + if err != nil { + p.AddError(err) + return + } + out[i], err = p.loader.parseFile(filename, src) + if err != nil { + p.AddError(err) + return + } + }(i, filename) + } + wg.Wait() + for _, file := range out { + if file == nil { + return + } + } + p.Syntax = out +} + +// AddError adds an error to the errors associated with the given package. +func (p *Package) AddError(err error) { + switch typedErr := err.(type) { + case *os.PathError: + // file-reading errors + p.Errors = append(p.Errors, packages.Error{ + Pos: typedErr.Path + ":1", + Msg: typedErr.Err.Error(), + Kind: packages.ParseError, + }) + case scanner.ErrorList: + // parsing/scanning errors + for _, subErr := range typedErr { + p.Errors = append(p.Errors, packages.Error{ + Pos: subErr.Pos.String(), + Msg: subErr.Msg, + Kind: packages.ParseError, + }) + } + case types.Error: + // type-checking errors + p.Errors = append(p.Errors, packages.Error{ + Pos: typedErr.Fset.Position(typedErr.Pos).String(), + Msg: typedErr.Msg, + Kind: packages.TypeError, + }) + case ErrList: + for _, subErr := range typedErr { + p.AddError(subErr) + } + case PositionedError: + p.Errors = append(p.Errors, packages.Error{ + Pos: p.loader.cfg.Fset.Position(typedErr.Pos).String(), + Msg: typedErr.Error(), + Kind: packages.UnknownError, + }) + default: + // should only happen for external errors, like ref checking + p.Errors = append(p.Errors, packages.Error{ + Pos: p.ID + ":-", + Msg: err.Error(), + Kind: packages.UnknownError, + }) + } +} + +// loader loads packages and their imports. Loaded packages will have +// type size, imports, and exports file information populated. Additional +// information, like ASTs and type-checking information, can be accessed +// via methods on individual packages. +type loader struct { + // Roots are the loaded "root" packages in the package graph loaded via + // LoadRoots. + Roots []*Package + + // cfg contains the package loading config (initialized on demand) + cfg *packages.Config + // packages contains the cache of Packages indexed by the underlying + // package.Package, so that we don't ever produce two Packages with + // the same underlying packages.Package. + packages map[*packages.Package]*Package + packagesMu sync.Mutex +} + +// packageFor returns a wrapped Package for the given packages.Package, +// ensuring that there's a one-to-one mapping between the two. +// It's *not* threadsafe -- use packagesFor for that. +func (l *loader) packageFor(pkgRaw *packages.Package) *Package { + if l.packages[pkgRaw] == nil { + l.packages[pkgRaw] = &Package{ + Package: pkgRaw, + loader: l, + } + } + return l.packages[pkgRaw] +} + +// packagesFor returns a map of Package objects for each packages.Package in the input +// map, ensuring that there's a one-to-one mapping between package.Package and Package +// (as per packageFor). +func (l *loader) packagesFor(pkgsRaw map[string]*packages.Package) map[string]*Package { + l.packagesMu.Lock() + defer l.packagesMu.Unlock() + + out := make(map[string]*Package, len(pkgsRaw)) + for name, rawPkg := range pkgsRaw { + out[name] = l.packageFor(rawPkg) + } + return out +} + +// typeCheck type-checks the given package. +func (l *loader) typeCheck(pkg *Package) { + // don't conflict with typeCheckFromExportData + + pkg.TypesInfo = &types.Info{ + Types: make(map[ast.Expr]types.TypeAndValue), + Defs: make(map[*ast.Ident]types.Object), + Uses: make(map[*ast.Ident]types.Object), + Implicits: make(map[ast.Node]types.Object), + Scopes: make(map[ast.Node]*types.Scope), + Selections: make(map[*ast.SelectorExpr]*types.Selection), + } + + pkg.Fset = l.cfg.Fset + pkg.Types = types.NewPackage(pkg.PkgPath, pkg.Name) + + importer := importerFunc(func(path string) (*types.Package, error) { + if path == "unsafe" { + return types.Unsafe, nil + } + + // The imports map is keyed by import path. + importedPkg := pkg.Imports()[path] + + // it's possible to have a call to check in parallel to a call to this + // if one package in the package graph gets its dependency filtered out, + // but another doesn't (so one wants a "dummy" package here, and another + // wants the full check). + // + // Thus, we need to lock here (at least for the time being) to avoid + // races between the above write to `pkg.Types` and this checking of + // importedPkg.Types. + importedPkg.Lock() + defer importedPkg.Unlock() + + if importedPkg == nil { + return nil, fmt.Errorf("no package information for %q", path) + } + + if importedPkg.Types != nil && importedPkg.Types.Complete() { + return importedPkg.Types, nil + } + + // if we haven't already loaded typecheck data, we don't care about this package's types + return types.NewPackage(importedPkg.PkgPath, importedPkg.Name), nil + }) + + var errs []error + + // type-check + checkConfig := &types.Config{ + Importer: importer, + + IgnoreFuncBodies: true, // we only need decl-level info + + Error: func(err error) { + errs = append(errs, err) + }, + + Sizes: pkg.TypesSizes, + } + if err := types.NewChecker(checkConfig, l.cfg.Fset, pkg.Types, pkg.TypesInfo).Files(pkg.Syntax); err != nil { + errs = append(errs, err) + } + + // make sure that if a given sub-import is ill-typed, we mark this package as ill-typed as well. + illTyped := len(errs) > 0 + if !illTyped { + for _, importedPkg := range pkg.Imports() { + if importedPkg.IllTyped { + illTyped = true + break + } + } + } + pkg.IllTyped = illTyped + + // publish errors to the package error list. + for _, err := range errs { + pkg.AddError(err) + } +} + +// parseFile parses the given file, including comments. +func (l *loader) parseFile(filename string, src []byte) (*ast.File, error) { + // skip function bodies + file, err := parser.ParseFile(l.cfg.Fset, filename, src, parser.AllErrors|parser.ParseComments) + if err != nil { + return nil, err + } + + return file, nil +} + +// LoadRoots loads the given "root" packages by path, transitively loading +// and all imports as well. +// +// Loaded packages will have type size, imports, and exports file information +// populated. Additional information, like ASTs and type-checking information, +// can be accessed via methods on individual packages. +func LoadRoots(roots ...string) ([]*Package, error) { + return LoadRootsWithConfig(&packages.Config{}, roots...) +} + +// LoadRootsWithConfig functions like LoadRoots, except that it allows passing +// a custom loading config. The config will be modified to suit the needs of +// the loader. +// +// This is generally only useful for use in testing when you need to modify +// loading settings to load from a fake location. +func LoadRootsWithConfig(cfg *packages.Config, roots ...string) ([]*Package, error) { + l := &loader{ + cfg: cfg, + packages: make(map[*packages.Package]*Package), + } + l.cfg.Mode |= packages.LoadImports | packages.NeedTypesSizes + if l.cfg.Fset == nil { + l.cfg.Fset = token.NewFileSet() + } + // put our build flags first so that callers can override them + l.cfg.BuildFlags = append([]string{"-tags", "ignore_autogenerated"}, l.cfg.BuildFlags...) + + rawPkgs, err := packages.Load(l.cfg, roots...) + if err != nil { + return nil, err + } + + for _, rawPkg := range rawPkgs { + l.Roots = append(l.Roots, l.packageFor(rawPkg)) + } + + return l.Roots, nil +} + +// importFunc is an implementation of the single-method +// types.Importer interface based on a function value. +type importerFunc func(path string) (*types.Package, error) + +func (f importerFunc) Import(path string) (*types.Package, error) { return f(path) } diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/loader/paths.go b/vendor/sigs.k8s.io/controller-tools/pkg/loader/paths.go new file mode 100644 index 0000000000..3b783e1685 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/loader/paths.go @@ -0,0 +1,32 @@ +/* +Copyright 2019 The Kubernetes 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 loader + +import ( + "strings" +) + +// NonVendorPath returns a package path that does not include anything before the +// last vendor directory. This is useful for when using vendor directories, +// and using go/types.Package.Path(), which returns the full path including vendor. +// +// If you're using this, make sure you really need it -- it's better to index by +// the actual Package object when you can. +func NonVendorPath(rawPath string) string { + parts := strings.Split(rawPath, "/vendor/") + return parts[len(parts)-1] +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/loader/refs.go b/vendor/sigs.k8s.io/controller-tools/pkg/loader/refs.go new file mode 100644 index 0000000000..9e2989cf54 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/loader/refs.go @@ -0,0 +1,254 @@ +/* +Copyright 2019 The Kubernetes 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 loader + +import ( + "fmt" + + "go/ast" + "strconv" + "sync" +) + +// NB(directxman12): most of this is done by the typechecker, +// but it's a bit slow/heavyweight for what we want -- we want +// to resolve external imports *only* if we actually need them. + +// Basically, what we do is: +// 1. Map imports to names +// 2. Find all explicit external references (`name.type`) +// 3. Find all referenced packages by merging explicit references and dot imports +// 4. Only type-check those packages +// 5. Ignore type-checking errors from the missing packages, because we won't ever +// touch unloaded types (they're probably used in ignored fields/types, variables, or functions) +// (done using PrintErrors with an ignore argument from the caller). +// 6. Notice any actual type-checking errors via invalid types + +// importsMap saves import aliases, mapping them to underlying packages. +type importsMap struct { + // dotImports maps package IDs to packages for any packages that have/ been imported as `.` + dotImports map[string]*Package + // byName maps package aliases or names to the underlying package. + byName map[string]*Package +} + +// mapImports maps imports from the names they use in the given file to the underlying package, +// using a map of package import paths to packages (generally from Package.Imports()). +func mapImports(file *ast.File, importedPkgs map[string]*Package) (*importsMap, error) { + m := &importsMap{ + dotImports: make(map[string]*Package), + byName: make(map[string]*Package), + } + for _, importSpec := range file.Imports { + path, err := strconv.Unquote(importSpec.Path.Value) + if err != nil { + return nil, ErrFromNode(err, importSpec.Path) + } + importedPkg := importedPkgs[path] + if importedPkg == nil { + return nil, ErrFromNode(fmt.Errorf("no such package located"), importSpec.Path) + } + if importSpec.Name == nil { + m.byName[importedPkg.Name] = importedPkg + continue + } + if importSpec.Name.Name == "." { + m.dotImports[importedPkg.ID] = importedPkg + continue + } + m.byName[importSpec.Name.Name] = importedPkg + } + + return m, nil +} + +// referenceSet finds references to external packages' types in the given file, +// without otherwise calling into the type-checker. When checking structs, +// it only checks fields with JSON tags. +type referenceSet struct { + file *ast.File + imports *importsMap + pkg *Package + + externalRefs map[*Package]struct{} +} + +func (r *referenceSet) init() { + if r.externalRefs == nil { + r.externalRefs = make(map[*Package]struct{}) + } +} + +// NodeFilter filters nodes, accepting them for reference collection +// when true is returned and rejecting them when false is returned. +type NodeFilter func(ast.Node) bool + +// collectReferences saves all references to external types in the given info. +func (r *referenceSet) collectReferences(rawType ast.Expr, filterNode NodeFilter) { + r.init() + col := &referenceCollector{ + refs: r, + filterNode: filterNode, + } + ast.Walk(col, rawType) +} + +// external saves an external reference to the given named package. +func (r *referenceSet) external(pkgName string) { + pkg := r.imports.byName[pkgName] + if pkg == nil { + r.pkg.AddError(fmt.Errorf("use of unimported package %q", pkgName)) + return + } + r.externalRefs[pkg] = struct{}{} +} + +// referenceCollector visits nodes in an AST, adding external references to a +// referenceSet. +type referenceCollector struct { + refs *referenceSet + filterNode NodeFilter +} + +func (c *referenceCollector) Visit(node ast.Node) ast.Visitor { + if !c.filterNode(node) { + return nil + } + switch typedNode := node.(type) { + case *ast.Ident: + // local reference or dot-import, ignore + return nil + case *ast.SelectorExpr: + pkgName := typedNode.X.(*ast.Ident).Name + c.refs.external(pkgName) + return nil + default: + return c + } +} + +// allReferencedPackages finds all directly referenced packages in the given package. +func allReferencedPackages(pkg *Package, filterNodes NodeFilter) []*Package { + pkg.NeedSyntax() + refsByFile := make(map[*ast.File]*referenceSet) + for _, file := range pkg.Syntax { + imports, err := mapImports(file, pkg.Imports()) + if err != nil { + pkg.AddError(err) + return nil + } + refs := &referenceSet{ + file: file, + imports: imports, + pkg: pkg, + } + refsByFile[file] = refs + } + + EachType(pkg, func(file *ast.File, decl *ast.GenDecl, spec *ast.TypeSpec) { + refs := refsByFile[file] + refs.collectReferences(spec.Type, filterNodes) + }) + + allPackages := make(map[*Package]struct{}) + for _, refs := range refsByFile { + for _, pkg := range refs.imports.dotImports { + allPackages[pkg] = struct{}{} + } + for ref := range refs.externalRefs { + allPackages[ref] = struct{}{} + } + } + + res := make([]*Package, 0, len(allPackages)) + for pkg := range allPackages { + res = append(res, pkg) + } + return res +} + +// TypeChecker performs type-checking on a limitted subset of packages by +// checking each package's types' externally-referenced types, and only +// type-checking those packages. +type TypeChecker struct { + checkedPackages map[*Package]struct{} + filterNodes NodeFilter + sync.Mutex +} + +// Check type-checks the given package and all packages referenced +// by types that pass through (have true returned by) filterNodes. +func (c *TypeChecker) Check(root *Package, filterNodes NodeFilter) { + c.init() + + if filterNodes == nil { + filterNodes = c.filterNodes + } + + // use a sub-checker with the appropriate settings + (&TypeChecker{ + filterNodes: filterNodes, + checkedPackages: c.checkedPackages, + }).check(root) +} + +func (c *TypeChecker) init() { + if c.checkedPackages == nil { + c.checkedPackages = make(map[*Package]struct{}) + } + if c.filterNodes == nil { + // check every type by default + c.filterNodes = func(_ ast.Node) bool { + return true + } + } +} + +// check recursively type-checks the given package, only loading packages that +// are actually referenced by our types (it's the actual implementation of Check, +// without initialization). +func (c *TypeChecker) check(root *Package) { + root.Lock() + defer root.Unlock() + + c.Lock() + _, ok := c.checkedPackages[root] + c.Unlock() + if ok { + return + } + + refedPackages := allReferencedPackages(root, c.filterNodes) + + // first, resolve imports for all leaf packages... + var wg sync.WaitGroup + for _, pkg := range refedPackages { + wg.Add(1) + go func(pkg *Package) { + defer wg.Done() + c.check(pkg) + }(pkg) + } + wg.Wait() + + // ...then, we can safely type-check ourself + root.NeedTypesInfo() + + c.Lock() + defer c.Unlock() + c.checkedPackages[root] = struct{}{} +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/loader/visit.go b/vendor/sigs.k8s.io/controller-tools/pkg/loader/visit.go new file mode 100644 index 0000000000..b5646fde1d --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/loader/visit.go @@ -0,0 +1,81 @@ +/* +Copyright 2019 The Kubernetes 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 loader + +import ( + "go/ast" + "reflect" + "strconv" +) + +// TypeCallback is a callback called for each raw AST (gendecl, typespec) combo. +type TypeCallback func(file *ast.File, decl *ast.GenDecl, spec *ast.TypeSpec) + +// EachType calls the given callback for each (gendecl, typespec) combo in the +// given package. Generally, using markers.EachType is better when working +// with marker data, and has a more convinient representation. +func EachType(pkg *Package, cb TypeCallback) { + visitor := &typeVisitor{ + callback: cb, + } + pkg.NeedSyntax() + for _, file := range pkg.Syntax { + visitor.file = file + ast.Walk(visitor, file) + } +} + +// typeVisitor visits all TypeSpecs, calling the given callback for each. +type typeVisitor struct { + callback TypeCallback + decl *ast.GenDecl + file *ast.File +} + +// Visit visits all TypeSpecs. +func (v *typeVisitor) Visit(node ast.Node) ast.Visitor { + if node == nil { + v.decl = nil + return v + } + + switch typedNode := node.(type) { + case *ast.File: + v.file = typedNode + return v + case *ast.GenDecl: + v.decl = typedNode + return v + case *ast.TypeSpec: + v.callback(v.file, v.decl, typedNode) + return nil // don't recurse + default: + return nil + } +} + +// ParseAstTag parses the given raw tag literal into a reflect.StructTag. +func ParseAstTag(tag *ast.BasicLit) reflect.StructTag { + if tag == nil { + return reflect.StructTag("") + } + tagStr, err := strconv.Unquote(tag.Value) + if err != nil { + return reflect.StructTag("") + } + return reflect.StructTag(tagStr) +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/markers/collect.go b/vendor/sigs.k8s.io/controller-tools/pkg/markers/collect.go new file mode 100644 index 0000000000..c5ea2345fd --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/markers/collect.go @@ -0,0 +1,422 @@ +/* +Copyright 2019 The Kubernetes 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 markers + +import ( + "go/ast" + "go/token" + "strings" + "sync" + + "sigs.k8s.io/controller-tools/pkg/loader" +) + +// Collector collects and parses marker comments defined in the registry +// from package source code. If no registry is provided, an empty one will +// be initialized on the first call to MarkersInPackage. +type Collector struct { + *Registry + + byPackage map[string]map[ast.Node]MarkerValues + mu sync.Mutex +} + +// MarkerValues are all the values for some set of markers. +type MarkerValues map[string][]interface{} + +// Get fetches the first value that for the given marker, returning +// nil if no values are available. +func (v MarkerValues) Get(name string) interface{} { + vals := v[name] + if len(vals) == 0 { + return nil + } + return vals[0] +} + +func (c *Collector) init() { + if c.Registry == nil { + c.Registry = &Registry{} + } + if c.byPackage == nil { + c.byPackage = make(map[string]map[ast.Node]MarkerValues) + } +} + +// MarkersInPackage computes the marker values by node for the given package. Results +// are cached by package ID, so this is safe to call repeatedly from different functions. +// Each file in the package is treated as a distinct node. +// +// We consider a marker to be associated with a given AST node if either of the following are true: +// +// - it's in the Godoc for that AST node +// +// - it's in the closest non-godoc comment group above that node, +// *and* that node is a type or field node, *and* [it's either +// registered as type-level *or* it's not registered as being +// package-level] +// +// - it's not in the Godoc of a node, doesn't meet the above criteria, and +// isn't in a struct definition (in which case it's package-level) +func (c *Collector) MarkersInPackage(pkg *loader.Package) (map[ast.Node]MarkerValues, error) { + c.mu.Lock() + c.init() + if markers, exist := c.byPackage[pkg.ID]; exist { + c.mu.Unlock() + return markers, nil + } + // unlock early, it's ok if we do a bit extra work rather than locking while we're working + c.mu.Unlock() + + pkg.NeedSyntax() + nodeMarkersRaw := c.associatePkgMarkers(pkg) + markers, err := c.parseMarkersInPackage(nodeMarkersRaw) + if err != nil { + return nil, err + } + + c.mu.Lock() + defer c.mu.Unlock() + c.byPackage[pkg.ID] = markers + + return markers, nil +} + +// parseMarkersInPackage parses the given raw marker comments into output values using the registry. +func (c *Collector) parseMarkersInPackage(nodeMarkersRaw map[ast.Node][]markerComment) (map[ast.Node]MarkerValues, error) { + var errors []error + nodeMarkerValues := make(map[ast.Node]MarkerValues) + for node, markersRaw := range nodeMarkersRaw { + var target TargetType + switch node.(type) { + case *ast.File: + target = DescribesPackage + case *ast.Field: + target = DescribesField + default: + target = DescribesType + } + markerVals := make(map[string][]interface{}) + for _, markerRaw := range markersRaw { + markerText := markerRaw.Text() + def := c.Registry.Lookup(markerText, target) + if def == nil { + continue + } + val, err := def.Parse(markerText) + if err != nil { + errors = append(errors, loader.ErrFromNode(err, markerRaw)) + continue + } + markerVals[def.Name] = append(markerVals[def.Name], val) + } + nodeMarkerValues[node] = markerVals + } + + return nodeMarkerValues, loader.MaybeErrList(errors) +} + +// associatePkgMarkers associates markers with AST nodes in the given package. +func (c *Collector) associatePkgMarkers(pkg *loader.Package) map[ast.Node][]markerComment { + nodeMarkers := make(map[ast.Node][]markerComment) + for _, file := range pkg.Syntax { + fileNodeMarkers := c.associateFileMarkers(file) + for node, markers := range fileNodeMarkers { + nodeMarkers[node] = append(nodeMarkers[node], markers...) + } + } + + return nodeMarkers +} + +// associateFileMarkers associates markers with AST nodes in the given file. +func (c *Collector) associateFileMarkers(file *ast.File) map[ast.Node][]markerComment { + // grab all the raw marker comments by node + visitor := markerSubVisitor{ + collectPackageLevel: true, + markerVisitor: &markerVisitor{ + nodeMarkers: make(map[ast.Node][]markerComment), + allComments: file.Comments, + }, + } + ast.Walk(visitor, file) + + // grab the last package-level comments at the end of the file (if any) + lastFileMarkers := visitor.markersBetween(false, visitor.commentInd, len(visitor.allComments)) + visitor.pkgMarkers = append(visitor.pkgMarkers, lastFileMarkers...) + + // figure out if any type-level markers are actually package-level markers + for node, markers := range visitor.nodeMarkers { + _, isType := node.(*ast.TypeSpec) + if !isType { + continue + } + endOfMarkers := 0 + for _, marker := range markers { + if marker.fromGodoc { + // markers from godoc are never package level + markers[endOfMarkers] = marker + endOfMarkers++ + continue + } + markerText := marker.Text() + typeDef := c.Registry.Lookup(markerText, DescribesType) + if typeDef != nil { + // prefer assuming type-level markers + markers[endOfMarkers] = marker + endOfMarkers++ + continue + } + def := c.Registry.Lookup(markerText, DescribesPackage) + if def == nil { + // assume type-level unless proven otherwise + markers[endOfMarkers] = marker + endOfMarkers++ + continue + } + // it's package-level, since a package-level definition exists + visitor.pkgMarkers = append(visitor.pkgMarkers, marker) + } + visitor.nodeMarkers[node] = markers[:endOfMarkers] // re-set after trimming the package markers + } + visitor.nodeMarkers[file] = visitor.pkgMarkers + + return visitor.nodeMarkers +} + +// markerComment is an AST comment that contains a marker. +// It may or may not be from a Godoc comment, which affects +// marker re-associated (from type-level to package-level) +type markerComment struct { + *ast.Comment + fromGodoc bool +} + +// Text returns the text of the marker, stripped of the comment +// marker and leading spaces, as should be passed to Registry.Lookup +// and Registry.Parse. +func (c markerComment) Text() string { + return strings.TrimSpace(c.Comment.Text[2:]) +} + +// markerVisistor visits AST nodes, recording markers associated with each node. +type markerVisitor struct { + allComments []*ast.CommentGroup + commentInd int + + declComments []markerComment + lastLineCommentGroup *ast.CommentGroup + + pkgMarkers []markerComment + nodeMarkers map[ast.Node][]markerComment +} + +// isMarkerComment checks that the given comment is a single-line (`//`) +// comment and it's first non-space content is `+`. +func isMarkerComment(comment string) bool { + if comment[0:2] != "//" { + return false + } + stripped := strings.TrimSpace(comment[2:]) + if len(stripped) < 1 || stripped[0] != '+' { + return false + } + return true +} + +// markersBetween grabs the markers between the given indicies in the list of all comments. +func (v *markerVisitor) markersBetween(fromGodoc bool, start, end int) []markerComment { + if start < 0 || end < 0 { + return nil + } + var res []markerComment + for i := start; i < end; i++ { + commentGroup := v.allComments[i] + for _, comment := range commentGroup.List { + if !isMarkerComment(comment.Text) { + continue + } + res = append(res, markerComment{Comment: comment, fromGodoc: fromGodoc}) + } + } + return res +} + +type markerSubVisitor struct { + *markerVisitor + node ast.Node + collectPackageLevel bool +} + +// Visit collects markers for each node in the AST, optionally +// collecting unassociated markers as package-level. +func (v markerSubVisitor) Visit(node ast.Node) ast.Visitor { + if node == nil { + // end of the node, so we might need to advance comments beyond the end + // of the block if we don't want to collect package-level markers in + // this block. + + if !v.collectPackageLevel { + if v.commentInd < len(v.allComments) { + lastCommentInd := v.commentInd + nextGroup := v.allComments[lastCommentInd] + for nextGroup.Pos() < v.node.End() { + lastCommentInd++ + if lastCommentInd >= len(v.allComments) { + // after the increment so our decrement below still makes sense + break + } + nextGroup = v.allComments[lastCommentInd] + } + v.commentInd = lastCommentInd + } + } + + return nil + } + + // skip comments on the same line as the previous node + // making sure to double-check for the case where we've gone past the end of the comments + // but still have to finish up typespec-gendecl association (see below). + if v.lastLineCommentGroup != nil && v.commentInd < len(v.allComments) && v.lastLineCommentGroup.Pos() == v.allComments[v.commentInd].Pos() { + v.commentInd++ + } + + // stop visiting if there are no more comments in the file + // NB(directxman12): we can't just stop immediately, because we + // still need to check if there are typespecs associated with gendecls. + var markerCommentBlock []markerComment + var docCommentBlock []markerComment + lastCommentInd := v.commentInd + if v.commentInd < len(v.allComments) { + // figure out the first comment after the node in question... + nextGroup := v.allComments[lastCommentInd] + for nextGroup.Pos() < node.Pos() { + lastCommentInd++ + if lastCommentInd >= len(v.allComments) { + // after the increment so our decrement below still makes sense + break + } + nextGroup = v.allComments[lastCommentInd] + } + lastCommentInd-- // ...then decrement to get the last comment before the node in question + + // figure out the godoc comment so we can deal with it separately + var docGroup *ast.CommentGroup + docGroup, v.lastLineCommentGroup = associatedCommentsFor(node) + + // find the last comment group that's not godoc + markerCommentInd := lastCommentInd + if docGroup != nil && v.allComments[markerCommentInd].Pos() == docGroup.Pos() { + markerCommentInd-- + } + + // check if we have freestanding package markers, + // and find the markers in our "closest non-godoc" comment block, + // plus our godoc comment block + if markerCommentInd >= v.commentInd { + if v.collectPackageLevel { + // assume anything between the comment ind and the marker ind (not including it) + // are package-level + v.pkgMarkers = append(v.pkgMarkers, v.markersBetween(false, v.commentInd, markerCommentInd)...) + } + markerCommentBlock = v.markersBetween(false, markerCommentInd, markerCommentInd+1) + docCommentBlock = v.markersBetween(true, markerCommentInd+1, lastCommentInd+1) + } else { + docCommentBlock = v.markersBetween(true, markerCommentInd+1, lastCommentInd+1) + } + } + + resVisitor := markerSubVisitor{ + collectPackageLevel: false, // don't collect package level by default + markerVisitor: v.markerVisitor, + node: node, + } + + // associate those markers with a node + switch typedNode := node.(type) { + case *ast.GenDecl: + // save the comments associated with the gen-decl if it's a single-line type decl + if typedNode.Lparen != token.NoPos || typedNode.Tok != token.TYPE { + // not a single-line type spec, treat them as free comments + v.pkgMarkers = append(v.pkgMarkers, markerCommentBlock...) + break + } + // save these, we'll need them when we encounter the actual type spec + v.declComments = append(v.declComments, markerCommentBlock...) + v.declComments = append(v.declComments, docCommentBlock...) + case *ast.TypeSpec: + // add in comments attributed to the gen-decl, if any, + // as well as comments associated with the actual type + v.nodeMarkers[node] = append(v.nodeMarkers[node], v.declComments...) + v.nodeMarkers[node] = append(v.nodeMarkers[node], markerCommentBlock...) + v.nodeMarkers[node] = append(v.nodeMarkers[node], docCommentBlock...) + + v.declComments = nil + v.collectPackageLevel = false // don't collect package-level inside type structs + case *ast.Field: + v.nodeMarkers[node] = append(v.nodeMarkers[node], markerCommentBlock...) + v.nodeMarkers[node] = append(v.nodeMarkers[node], docCommentBlock...) + case *ast.File: + v.pkgMarkers = append(v.pkgMarkers, markerCommentBlock...) + v.pkgMarkers = append(v.pkgMarkers, docCommentBlock...) + + // collect markers in root file scope + resVisitor.collectPackageLevel = true + default: + // assume markers before anything else are package-level markers, + // *but* don't include any markers in godoc + if v.collectPackageLevel { + v.pkgMarkers = append(v.pkgMarkers, markerCommentBlock...) + } + } + + // increment the comment ind so that we start at the right place for the next node + v.commentInd = lastCommentInd + 1 + + return resVisitor + +} + +// associatedCommentsFor returns the doc comment group (if relevant and present) and end-of-line comment +// (again if relevant and present) for the given AST node. +func associatedCommentsFor(node ast.Node) (docGroup *ast.CommentGroup, lastLineCommentGroup *ast.CommentGroup) { + switch typedNode := node.(type) { + case *ast.Field: + docGroup = typedNode.Doc + lastLineCommentGroup = typedNode.Comment + case *ast.File: + docGroup = typedNode.Doc + case *ast.FuncDecl: + docGroup = typedNode.Doc + case *ast.GenDecl: + docGroup = typedNode.Doc + case *ast.ImportSpec: + docGroup = typedNode.Doc + lastLineCommentGroup = typedNode.Comment + case *ast.TypeSpec: + docGroup = typedNode.Doc + lastLineCommentGroup = typedNode.Comment + case *ast.ValueSpec: + docGroup = typedNode.Doc + lastLineCommentGroup = typedNode.Comment + default: + lastLineCommentGroup = nil + } + + return docGroup, lastLineCommentGroup +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/markers/doc.go b/vendor/sigs.k8s.io/controller-tools/pkg/markers/doc.go new file mode 100644 index 0000000000..707036de8f --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/markers/doc.go @@ -0,0 +1,113 @@ +/* +Copyright 2019 The Kubernetes 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 markers contains utilities for defining and parsing "marker +// comments", also occasionally called tag comments (we use the term marker to +// avoid confusing with struct tags). Parsed result (output) values take the +// form of Go values, much like the "encoding/json" package. +// +// Definitions and Parsing +// +// Markers are defined as structured Definitions which can be used to +// consistently parse marker comments. A Definition contains an concrete +// output type for the marker, which can be a simple type (like string), a +// struct, or a wrapper type (useful for defining additional methods on marker +// types). +// +// Markers take the general form +// +// +path:to:marker=val +// +// +path:to:marker:arg1=val,arg2=val2 +// +// +path:to:marker +// +// Arguments may be ints, bools, strings, and slices. Ints and bool take their +// standard form from Go. Strings may take any of their standard forms, or any +// sequence of unquoted characters up until a `,` or `;` is encountered. Lists +// take either of the following forms: +// +// val;val;val +// +// {val, val, val} +// +// Note that the first form will not properly parse nested slices, but is +// generally convenient and is the form used in many existing markers. +// +// Each of those argument types maps to the corresponding go type. Pointers +// mark optional fields (a struct tag, below, may also be used). The empty +// interface will match any type. +// +// Struct fields may optionally be annotated with the `marker` struct tag. The +// first argument is a name override. If it's left blank (or the tag isn't +// present), the camelCase version of the name will be used. The only +// additional argument defined is `optional`, which marks a field as optional +// without using a pointer. +// +// All parsed values are unmarshalled into the output type. If any +// non-optional fields aren't mentioned, an error will be raised unless +// `Strict` is set to false. +// +// Registries and Lookup +// +// Definitions can be added to registries to facilitate lookups. Each +// definition is marked as either describing a type, struct field, or package +// (unassociated). The same marker name may be registered multiple times, as +// long as each describes a different construct (type, field, or package). +// Definitions can then be looked up by passing unparsed markers. +// +// Collection and Extraction +// +// Markers can be collected from a loader.Package using a Collector. The +// Collector will read from a given Registry, collecting comments that look +// like markers and parsing them if they match some definition on the registry. +// +// Markers are considered associated with a particular field or type if they +// exist in the Godoc, or the closest non-godoc comment. Any other markers not +// inside a some other block (e.g. a struct definition, interface definition, +// etc) are considered package level. Markers in a "closest non-Go comment +// block" may also be considered package level if registered as such and no +// identical type-level definition exists. +// +// Like loader.Package, Collector's methods are idempotent and will not +// reperform work. +// +// Traversal +// +// EachType function iterates over each type in a Package, providing +// conveniently structured type and field information with marker values +// associated. +// +// PackageMarkers can be used to fetch just package-level markers. +// +// Help +// +// Help can be defined for each marker using the DefinitionHelp struct. It's +// mostly intended to be generated off of godocs using cmd/helpgen, which takes +// the first line as summary (removing the type/field name), and considers the +// rest as details. It looks for the +// +// +controllertools:generateHelp[:category=] +// +// marker to start generation. +// +// If you can't use godoc-based generation for whatever reasons (e.g. +// primitive-typed markers), you can use the SimpleHelp and DeprecatedHelp +// helper functions to generate help structs. +// +// Help is then registered into a registry as associated with the actual +// definition, and can then be later retrieved from the registry. +package markers diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/markers/help.go b/vendor/sigs.k8s.io/controller-tools/pkg/markers/help.go new file mode 100644 index 0000000000..26ca059bc8 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/markers/help.go @@ -0,0 +1,81 @@ +/* +Copyright 2019 The Kubernetes 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 markers + +// You *probably* don't want to write these structs by hand +// -- use cmd/helpgen if you can write Godoc, and {Simple,Deprecated}Help +// otherwise. + +// DetailedHelp contains brief help, as well as more details. +// For the "full" help, join the two together. +type DetailedHelp struct { + Summary string + Details string +} + +// DefinitionHelp contains overall help for a marker Definition, +// as well as per-field help. +type DefinitionHelp struct { + // DetailedHelp contains the overall help for the marker. + DetailedHelp + // Category describes what kind of marker this is. + Category string + // DeprecatedInFavorOf marks the marker as deprecated. + // If non-nil & empty, it's assumed to just mean deprecated permanently. + // If non-empty, it's assumed to be a marker name. + DeprecatedInFavorOf *string + + // NB(directxman12): we make FieldHelp be in terms of the Go struct field + // names so that we don't have to know the conversion or processing rules + // for struct fields at compile-time for help generation. + + // FieldHelp defines the per-field help for this marker, *in terms of the + // go struct field names. Use the FieldsHelp method to map this to + // marker argument names. + FieldHelp map[string]DetailedHelp +} + +// FieldsHelp maps per-field help to the actual marker argument names from the +// given definition. +func (d *DefinitionHelp) FieldsHelp(def *Definition) map[string]DetailedHelp { + fieldsHelp := make(map[string]DetailedHelp, len(def.FieldNames)) + for fieldName, argName := range def.FieldNames { + fieldsHelp[fieldName] = d.FieldHelp[argName] + } + return fieldsHelp +} + +// SimpleHelp returns help that just has marker-level summary information +// (e.g. for use with empty or primitive-typed markers, where Godoc-based +// generation isn't possible). +func SimpleHelp(category, summary string) *DefinitionHelp { + return &DefinitionHelp{ + Category: category, + DetailedHelp: DetailedHelp{Summary: summary}, + } +} + +// DeprecatedHelp returns simple help (a la SimpleHelp), except marked as +// deprecated in favor of the given marker (or an empty string for just +// deprecated). +func DeprecatedHelp(inFavorOf, category, summary string) *DefinitionHelp { + return &DefinitionHelp{ + Category: category, + DetailedHelp: DetailedHelp{Summary: summary}, + DeprecatedInFavorOf: &inFavorOf, + } +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/markers/parse.go b/vendor/sigs.k8s.io/controller-tools/pkg/markers/parse.go new file mode 100644 index 0000000000..d617835608 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/markers/parse.go @@ -0,0 +1,908 @@ +/* +Copyright 2019 The Kubernetes 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 markers + +import ( + "bytes" + "fmt" + "reflect" + "strconv" + "strings" + sc "text/scanner" + "unicode" + + "sigs.k8s.io/controller-tools/pkg/loader" +) + +// expect checks that the next token of the scanner is the given token, adding an error +// to the scanner if not. It returns whether the token was as expected. +func expect(scanner *sc.Scanner, expected rune, errDesc string) bool { + tok := scanner.Scan() + if tok != expected { + scanner.Error(scanner, fmt.Sprintf("expected %s, got %q", errDesc, scanner.TokenText())) + return false + } + return true +} + +// peekNoSpace is equivalent to scanner.Peek, except that it will consume intervening whitespace. +func peekNoSpace(scanner *sc.Scanner) rune { + hint := scanner.Peek() + for ; hint <= rune(' ') && ((1<") + case IntType: + out.WriteString("int") + case StringType: + out.WriteString("string") + case BoolType: + out.WriteString("bool") + case AnyType: + out.WriteString("") + case SliceType: + out.WriteString("[]") + // arguments can't be non-pointer optional, so just call into typeString again. + a.ItemType.typeString(out) + case MapType: + out.WriteString("map[string]") + a.ItemType.typeString(out) + case RawType: + out.WriteString("") + } +} + +// TypeString returns a string roughly equivalent +// (but not identical) to the underlying Go type that +// this argument would parse to. It's mainly useful +// for user-friendly formatting of this argument (e.g. +// help strings). +func (a Argument) TypeString() string { + out := &strings.Builder{} + a.typeString(out) + return out.String() +} + +func (a Argument) String() string { + if a.Optional { + return fmt.Sprintf("", a.TypeString()) + } + return fmt.Sprintf("", a.TypeString()) +} + +// castAndSet casts val to out's type if needed, +// then sets out to val. +func castAndSet(out, val reflect.Value) { + outType := out.Type() + if outType != val.Type() { + val = val.Convert(outType) + } + out.Set(val) +} + +// makeSliceType makes a reflect.Type for a slice of the given type. +// Useful for constructing the out value for when AnyType's guess returns a slice. +func makeSliceType(itemType Argument) (reflect.Type, error) { + var itemReflectedType reflect.Type + switch itemType.Type { + case IntType: + itemReflectedType = reflect.TypeOf(int(0)) + case StringType: + itemReflectedType = reflect.TypeOf("") + case BoolType: + itemReflectedType = reflect.TypeOf(false) + case SliceType: + subItemType, err := makeSliceType(*itemType.ItemType) + if err != nil { + return nil, err + } + itemReflectedType = subItemType + case MapType: + subItemType, err := makeMapType(*itemType.ItemType) + if err != nil { + return nil, err + } + itemReflectedType = subItemType + // TODO(directxman12): support non-uniform slices? (probably not) + default: + return nil, fmt.Errorf("invalid type when constructing guessed slice out: %v", itemType.Type) + } + + if itemType.Pointer { + itemReflectedType = reflect.PtrTo(itemReflectedType) + } + + return reflect.SliceOf(itemReflectedType), nil +} + +// makeMapType makes a reflect.Type for a map of the given item type. +// Useful for constructing the out value for when AnyType's guess returns a map. +func makeMapType(itemType Argument) (reflect.Type, error) { + var itemReflectedType reflect.Type + switch itemType.Type { + case IntType: + itemReflectedType = reflect.TypeOf(int(0)) + case StringType: + itemReflectedType = reflect.TypeOf("") + case BoolType: + itemReflectedType = reflect.TypeOf(false) + case SliceType: + subItemType, err := makeSliceType(*itemType.ItemType) + if err != nil { + return nil, err + } + itemReflectedType = subItemType + // TODO(directxman12): support non-uniform slices? (probably not) + case MapType: + subItemType, err := makeMapType(*itemType.ItemType) + if err != nil { + return nil, err + } + itemReflectedType = subItemType + case AnyType: + // NB(directxman12): maps explicitly allow non-uniform item types, unlike slices at the moment + itemReflectedType = interfaceType + default: + return nil, fmt.Errorf("invalid type when constructing guessed slice out: %v", itemType.Type) + } + + if itemType.Pointer { + itemReflectedType = reflect.PtrTo(itemReflectedType) + } + + return reflect.MapOf(reflect.TypeOf(""), itemReflectedType), nil +} + +// guessType takes an educated guess about the type of the next field. If allowSlice +// is false, it will not guess slices. It's less efficient than parsing with actual +// type information, since we need to allocate to peek ahead full tokens, and the scanner +// only allows peeking ahead one character. +// Maps are *always* non-uniform (i.e. type the AnyType item type), since they're frequently +// used to represent things like defaults for an object in JSON. +func guessType(scanner *sc.Scanner, raw string, allowSlice bool) *Argument { + if allowSlice { + maybeItem := guessType(scanner, raw, false) + + subRaw := raw[scanner.Pos().Offset:] + subScanner := parserScanner(subRaw, scanner.Error) + + var tok rune + for tok = subScanner.Scan(); tok != ',' && tok != sc.EOF && tok != ';'; tok = subScanner.Scan() { + // wait till we get something interesting + } + + // semicolon means it's a legacy slice + if tok == ';' { + return &Argument{ + Type: SliceType, + ItemType: maybeItem, + } + } + + return maybeItem + } + + // everything else needs a duplicate scanner to scan properly + // (so we don't consume our scanner tokens until we actually + // go to use this -- Go doesn't like scanners that can be rewound). + subRaw := raw[scanner.Pos().Offset:] + subScanner := parserScanner(subRaw, scanner.Error) + + // skip whitespace + hint := peekNoSpace(subScanner) + + // first, try the easy case -- quoted strings strings + switch hint { + case '"', '\'', '`': + return &Argument{Type: StringType} + } + + // next, check for slices or maps + if hint == '{' { + subScanner.Scan() + + // TODO(directxman12): this can't guess at empty objects, but that's generally ok. + // We'll cross that bridge when we get there. + + // look ahead till we can figure out if this is a map or a slice + firstElemType := guessType(subScanner, subRaw, false) + if firstElemType.Type == StringType { + // might be a map or slice, parse the string and check for colon + // (blech, basically arbitrary look-ahead due to raw strings). + var keyVal string // just ignore this + (&Argument{Type: StringType}).parseString(subScanner, raw, reflect.Indirect(reflect.ValueOf(&keyVal))) + + if subScanner.Scan() == ':' { + // it's got a string followed by a colon -- it's a map + return &Argument{ + Type: MapType, + ItemType: &Argument{Type: AnyType}, + } + } + } + + // definitely a slice -- maps have to have string keys and have a value followed by a colon + return &Argument{ + Type: SliceType, + ItemType: firstElemType, + } + } + + // then, bools... + probablyString := false + if hint == 't' || hint == 'f' { + // maybe a bool + if nextTok := subScanner.Scan(); nextTok == sc.Ident { + switch subScanner.TokenText() { + case "true", "false": + // definitely a bool + return &Argument{Type: BoolType} + } + // probably a string + probablyString = true + } else { + // we shouldn't ever get here + scanner.Error(scanner, fmt.Sprintf("got a token (%q) that looked like an ident, but was not", scanner.TokenText())) + return &Argument{Type: InvalidType} + } + } + + if !probablyString { + if nextTok := subScanner.Scan(); nextTok == sc.Int { + return &Argument{Type: IntType} + } + } + + // otherwise assume bare strings + return &Argument{Type: StringType} +} + +// parseString parses either of the two accepted string forms (quoted, or bare tokens). +func (a *Argument) parseString(scanner *sc.Scanner, raw string, out reflect.Value) { + // strings are a bit weird -- the "easy" case is quoted strings (tokenized as strings), + // the "hard" case (present for backwards compat) is a bare sequence of tokens that aren't + // a comma. + tok := scanner.Scan() + if tok == sc.String || tok == sc.RawString { + // the easy case + val, err := strconv.Unquote(scanner.TokenText()) + if err != nil { + scanner.Error(scanner, fmt.Sprintf("unable to parse string: %v", err)) + return + } + castAndSet(out, reflect.ValueOf(val)) + return + } + + // the "hard" case -- bare tokens not including ',' (the argument + // separator), ';' (the slice separator), ':' (the map separator), or '}' + // (delimitted slice ender) + startPos := scanner.Position.Offset + for hint := peekNoSpace(scanner); hint != ',' && hint != ';' && hint != ':' && hint != '}' && hint != sc.EOF; hint = peekNoSpace(scanner) { + // skip this token + scanner.Scan() + } + endPos := scanner.Position.Offset + len(scanner.TokenText()) + castAndSet(out, reflect.ValueOf(raw[startPos:endPos])) +} + +// parseSlice parses either of the two slice forms (curly-brace-delimitted and semicolon-separated). +func (a *Argument) parseSlice(scanner *sc.Scanner, raw string, out reflect.Value) { + // slices have two supported formats, like string: + // - `{val, val, val}` (preferred) + // - `val;val;val` (legacy) + resSlice := reflect.Zero(out.Type()) + elem := reflect.Indirect(reflect.New(out.Type().Elem())) + + // preferred case + if peekNoSpace(scanner) == '{' { + // NB(directxman12): supporting delimitted slices in bare slices + // would require an extra look-ahead here :-/ + + scanner.Scan() // skip '{' + for hint := peekNoSpace(scanner); hint != '}' && hint != sc.EOF; hint = peekNoSpace(scanner) { + a.ItemType.parse(scanner, raw, elem, true /* parsing a slice */) + resSlice = reflect.Append(resSlice, elem) + tok := peekNoSpace(scanner) + if tok == '}' { + break + } + if !expect(scanner, ',', "comma") { + return + } + } + if !expect(scanner, '}', "close curly brace") { + return + } + castAndSet(out, resSlice) + return + } + + // legacy case + for hint := peekNoSpace(scanner); hint != ',' && hint != '}' && hint != sc.EOF; hint = peekNoSpace(scanner) { + a.ItemType.parse(scanner, raw, elem, true /* parsing a slice */) + resSlice = reflect.Append(resSlice, elem) + tok := peekNoSpace(scanner) + if tok == ',' || tok == '}' || tok == sc.EOF { + break + } + scanner.Scan() + if tok != ';' { + scanner.Error(scanner, fmt.Sprintf("expected comma, got %q", scanner.TokenText())) + return + } + } + castAndSet(out, resSlice) +} + +// parseMap parses a map of the form {string: val, string: val, string: val} +func (a *Argument) parseMap(scanner *sc.Scanner, raw string, out reflect.Value) { + resMap := reflect.MakeMap(out.Type()) + elem := reflect.Indirect(reflect.New(out.Type().Elem())) + key := reflect.Indirect(reflect.New(out.Type().Key())) + + if !expect(scanner, '{', "open curly brace") { + return + } + + for hint := peekNoSpace(scanner); hint != '}' && hint != sc.EOF; hint = peekNoSpace(scanner) { + a.parseString(scanner, raw, key) + if !expect(scanner, ':', "colon") { + return + } + a.ItemType.parse(scanner, raw, elem, false /* not in a slice */) + resMap.SetMapIndex(key, elem) + + if peekNoSpace(scanner) == '}' { + break + } + if !expect(scanner, ',', "comma") { + return + } + } + + if !expect(scanner, '}', "close curly brace") { + return + } + + castAndSet(out, resMap) +} + +// parse functions like Parse, except that it allows passing down whether or not we're +// already in a slice, to avoid duplicate legacy slice detection for AnyType +func (a *Argument) parse(scanner *sc.Scanner, raw string, out reflect.Value, inSlice bool) { + // nolint:gocyclo + if a.Type == InvalidType { + scanner.Error(scanner, fmt.Sprintf("cannot parse invalid type")) + return + } + if a.Pointer { + out.Set(reflect.New(out.Type().Elem())) + out = reflect.Indirect(out) + } + switch a.Type { + case RawType: + // raw consumes everything else + castAndSet(out, reflect.ValueOf(raw[scanner.Pos().Offset:])) + // consume everything else + for tok := scanner.Scan(); tok != sc.EOF; tok = scanner.Scan() { + } + case IntType: + if !expect(scanner, sc.Int, "integer") { + return + } + // TODO(directxman12): respect the size when parsing + val, err := strconv.Atoi(scanner.TokenText()) + if err != nil { + scanner.Error(scanner, fmt.Sprintf("unable to parse integer: %v", err)) + return + } + castAndSet(out, reflect.ValueOf(val)) + case StringType: + // strings are a bit weird -- the "easy" case is quoted strings (tokenized as strings), + // the "hard" case (present for backwards compat) is a bare sequence of tokens that aren't + // a comma. + a.parseString(scanner, raw, out) + case BoolType: + if !expect(scanner, sc.Ident, "true or false") { + return + } + switch scanner.TokenText() { + case "true": + castAndSet(out, reflect.ValueOf(true)) + case "false": + castAndSet(out, reflect.ValueOf(false)) + default: + scanner.Error(scanner, fmt.Sprintf("expected true or false, got %q", scanner.TokenText())) + return + } + case AnyType: + guessedType := guessType(scanner, raw, !inSlice) + newOut := out + + // we need to be able to construct the right element types, below + // in parse, so construct a concretely-typed value to use as "out" + switch guessedType.Type { + case SliceType: + newType, err := makeSliceType(*guessedType.ItemType) + if err != nil { + scanner.Error(scanner, err.Error()) + return + } + newOut = reflect.Indirect(reflect.New(newType)) + case MapType: + newType, err := makeMapType(*guessedType.ItemType) + if err != nil { + scanner.Error(scanner, err.Error()) + return + } + newOut = reflect.Indirect(reflect.New(newType)) + } + if !newOut.CanSet() { + panic("at the disco") // TODO(directxman12): this is left over from debugging -- it might need to be an error + } + guessedType.Parse(scanner, raw, newOut) + castAndSet(out, newOut) + case SliceType: + // slices have two supported formats, like string: + // - `{val, val, val}` (preferred) + // - `val;val;val` (legacy) + a.parseSlice(scanner, raw, out) + case MapType: + // maps are {string: val, string: val, string: val} + a.parseMap(scanner, raw, out) + } +} + +// Parse attempts to consume the argument from the given scanner (based on the given +// raw input as well for collecting ranges of content), and places the output value +// in the given reflect.Value. Errors are reported via the given scanner. +func (a *Argument) Parse(scanner *sc.Scanner, raw string, out reflect.Value) { + a.parse(scanner, raw, out, false) +} + +// ArgumentFromType constructs an Argument by examining the given +// raw reflect.Type. It can construct arguments from the Go types +// corresponding to any of the types listed in ArgumentType. +func ArgumentFromType(rawType reflect.Type) (Argument, error) { + if rawType == rawArgsType { + return Argument{ + Type: RawType, + }, nil + } + + if rawType == interfaceType { + return Argument{ + Type: AnyType, + }, nil + } + + arg := Argument{} + if rawType.Kind() == reflect.Ptr { + rawType = rawType.Elem() + arg.Pointer = true + arg.Optional = true + } + + switch rawType.Kind() { + case reflect.String: + arg.Type = StringType + case reflect.Int, reflect.Int32: // NB(directxman12): all ints in kubernetes are int32, so explicitly support that + arg.Type = IntType + case reflect.Bool: + arg.Type = BoolType + case reflect.Slice: + arg.Type = SliceType + itemType, err := ArgumentFromType(rawType.Elem()) + if err != nil { + return Argument{}, fmt.Errorf("bad slice item type: %v", err) + } + arg.ItemType = &itemType + case reflect.Map: + arg.Type = MapType + if rawType.Key().Kind() != reflect.String { + return Argument{}, fmt.Errorf("bad map key type: map keys must be strings") + } + itemType, err := ArgumentFromType(rawType.Elem()) + if err != nil { + return Argument{}, fmt.Errorf("bad slice item type: %v", err) + } + arg.ItemType = &itemType + default: + return Argument{}, fmt.Errorf("type has unsupported kind %s", rawType.Kind()) + } + + return arg, nil +} + +// TargetType describes which kind of node a given marker is associated with. +type TargetType int + +const ( + // DescribesPackage indicates that a marker is associated with a package. + DescribesPackage TargetType = iota + // DescribesType indicates that a marker is associated with a type declaration. + DescribesType + // DescribesField indicates that a marker is associated with a struct field. + DescribesField +) + +func (t TargetType) String() string { + switch t { + case DescribesPackage: + return "package" + case DescribesType: + return "type" + case DescribesField: + return "field" + default: + return "(unknown)" + } +} + +// Definition is a parsed definition of a marker. +type Definition struct { + // Output is the deserialized Go type of the marker. + Output reflect.Type + // Name is the marker's name. + Name string + // Target indicates which kind of node this marker can be associated with. + Target TargetType + // Fields lists out the types of each field that this marker has, by + // argument name as used in the marker (if the output type isn't a struct, + // it'll have a single, blank field name). This only lists exported fields, + // (as per reflection rules). + Fields map[string]Argument + // FieldNames maps argument names (as used in the marker) to struct field name + // in the output type. + FieldNames map[string]string + // Strict indicates that this definition should error out when parsing if + // not all non-optional fields were seen. + Strict bool +} + +// AnonymousField indicates that the definition has one field, +// (actually the original object), and thus the field +// doesn't get named as part of the name. +func (d *Definition) AnonymousField() bool { + if len(d.Fields) != 1 { + return false + } + _, hasAnonField := d.Fields[""] + return hasAnonField +} + +// Empty indicates that this definition has no fields. +func (d *Definition) Empty() bool { + return len(d.Fields) == 0 +} + +// argumentInfo returns information about an argument field as the marker parser's field loader +// would see it. This can be useful if you have to interact with marker definition structs +// externally (e.g. at compile time). +func argumentInfo(fieldName string, tag reflect.StructTag) (argName string, optionalOpt bool) { + argName = lowerCamelCase(fieldName) + markerTag, tagSpecified := tag.Lookup("marker") + markerTagParts := strings.Split(markerTag, ",") + if tagSpecified && markerTagParts[0] != "" { + // allow overriding to support legacy cases where we don't follow camelCase conventions + argName = markerTagParts[0] + } + optionalOpt = false + for _, tagOption := range markerTagParts[1:] { + switch tagOption { + case "optional": + optionalOpt = true + } + } + + return argName, optionalOpt +} + +// loadFields uses reflection to populate argument information from the Output type. +func (d *Definition) loadFields() error { + if d.Fields == nil { + d.Fields = make(map[string]Argument) + d.FieldNames = make(map[string]string) + } + if d.Output.Kind() != reflect.Struct { + // anonymous field type + argType, err := ArgumentFromType(d.Output) + if err != nil { + return err + } + d.Fields[""] = argType + d.FieldNames[""] = "" + return nil + } + + for i := 0; i < d.Output.NumField(); i++ { + field := d.Output.Field(i) + if field.PkgPath != "" { + // as per the reflect package docs, pkgpath is empty for exported fields, + // so non-empty package path means a private field, which we should skip + continue + } + argName, optionalOpt := argumentInfo(field.Name, field.Tag) + + argType, err := ArgumentFromType(field.Type) + if err != nil { + return fmt.Errorf("unable to extract type information for field %q: %v", field.Name, err) + } + + if argType.Type == RawType { + return fmt.Errorf("RawArguments must be the direct type of a marker, and not a field") + } + + argType.Optional = optionalOpt || argType.Optional + + d.Fields[argName] = argType + d.FieldNames[argName] = field.Name + } + + return nil +} + +// parserScanner makes a new scanner appropriate for use in parsing definitions and arguments. +func parserScanner(raw string, err func(*sc.Scanner, string)) *sc.Scanner { + scanner := &sc.Scanner{} + scanner.Init(bytes.NewBufferString(raw)) + scanner.Mode = sc.ScanIdents | sc.ScanInts | sc.ScanStrings | sc.ScanRawStrings | sc.SkipComments + scanner.Error = err + + return scanner +} + +// Parse uses the type information in this Definition to parse the given +// raw marker in the form `+a:b:c=arg,d=arg` into an output object of the +// type specified in the definition. +func (d *Definition) Parse(rawMarker string) (interface{}, error) { + name, anonName, fields := splitMarker(rawMarker) + + out := reflect.Indirect(reflect.New(d.Output)) + + // if we're a not a struct or have no arguments, treat the full `a:b:c` as the name, + // otherwise, treat `c` as a field name, and `a:b` as the marker name. + if !d.AnonymousField() && !d.Empty() && len(anonName) >= len(name)+1 { + fields = anonName[len(name)+1:] + "=" + fields + } + + var errs []error + scanner := parserScanner(fields, func(scanner *sc.Scanner, msg string) { + errs = append(errs, &ScannerError{Msg: msg, Pos: scanner.Position}) + }) + + // TODO(directxman12): strict parsing where we error out if certain fields aren't optional + seen := make(map[string]struct{}, len(d.Fields)) + if d.AnonymousField() && scanner.Peek() != sc.EOF { + // might still be a struct that something fiddled with, so double check + structFieldName := d.FieldNames[""] + outTarget := out + if structFieldName != "" { + // it's a struct field mapped to an anonymous marker + outTarget = out.FieldByName(structFieldName) + if !outTarget.CanSet() { + scanner.Error(scanner, fmt.Sprintf("cannot set field %q (might not exist)", structFieldName)) + return out.Interface(), loader.MaybeErrList(errs) + } + } + + // no need for trying to parse field names if we're not a struct + field := d.Fields[""] + field.Parse(scanner, fields, outTarget) + seen[""] = struct{}{} // mark as seen for strict definitions + } else if !d.Empty() && scanner.Peek() != sc.EOF { + // if we expect *and* actually have arguments passed + for { + // parse the argument name + if !expect(scanner, sc.Ident, "argument name") { + break + } + argName := scanner.TokenText() + if !expect(scanner, '=', "equals") { + break + } + + // make sure we know the field + fieldName, known := d.FieldNames[argName] + if !known { + scanner.Error(scanner, fmt.Sprintf("unknown argument %q", argName)) + break + } + fieldType, known := d.Fields[argName] + if !known { + scanner.Error(scanner, fmt.Sprintf("unknown argument %q", argName)) + break + } + seen[argName] = struct{}{} // mark as seen for strict definitions + + // parse the field value + fieldVal := out.FieldByName(fieldName) + if !fieldVal.CanSet() { + scanner.Error(scanner, fmt.Sprintf("cannot set field %q (might not exist)", fieldName)) + break + } + fieldType.Parse(scanner, fields, fieldVal) + + if len(errs) > 0 { + break + } + + if scanner.Peek() == sc.EOF { + break + } + if !expect(scanner, ',', "comma") { + break + } + } + } + + if tok := scanner.Scan(); tok != sc.EOF { + scanner.Error(scanner, fmt.Sprintf("extra arguments provided: %q", fields[scanner.Position.Offset:])) + } + + if d.Strict { + for argName, arg := range d.Fields { + if _, wasSeen := seen[argName]; !wasSeen && !arg.Optional { + scanner.Error(scanner, fmt.Sprintf("missing argument %q", argName)) + } + } + } + + return out.Interface(), loader.MaybeErrList(errs) +} + +// MakeDefinition constructs a definition from a name, type, and the output type. +// All such definitions are strict by default. If a struct is passed as the output +// type, its public fields will automatically be populated into Fields (and similar +// fields in Definition). Other values will have a single, empty-string-named Fields +// entry. +func MakeDefinition(name string, target TargetType, output interface{}) (*Definition, error) { + def := &Definition{ + Name: name, + Target: target, + Output: reflect.TypeOf(output), + Strict: true, + } + + if err := def.loadFields(); err != nil { + return nil, err + } + + return def, nil +} + +// MakeAnyTypeDefinition constructs a definition for an output struct with a +// field named `Value` of type `interface{}`. The argument to the marker will +// be parsed as AnyType and assigned to the field named `Value`. +func MakeAnyTypeDefinition(name string, target TargetType, output interface{}) (*Definition, error) { + defn, err := MakeDefinition(name, target, output) + if err != nil { + return nil, err + } + defn.FieldNames = map[string]string{"": "Value"} + defn.Fields = map[string]Argument{"": defn.Fields["value"]} + return defn, nil +} + +// splitMarker takes a marker in the form of `+a:b:c=arg,d=arg` and splits it +// into the name (`a:b`), the name if it's not a struct (`a:b:c`), and the parts +// that are definitely fields (`arg,d=arg`). +func splitMarker(raw string) (name string, anonymousName string, restFields string) { + raw = raw[1:] // get rid of the leading '+' + nameFieldParts := strings.SplitN(raw, "=", 2) + if len(nameFieldParts) == 1 { + return nameFieldParts[0], nameFieldParts[0], "" + } + anonymousName = nameFieldParts[0] + name = anonymousName + restFields = nameFieldParts[1] + + nameParts := strings.Split(name, ":") + if len(nameParts) > 1 { + name = strings.Join(nameParts[:len(nameParts)-1], ":") + } + return name, anonymousName, restFields +} + +type ScannerError struct { + Msg string + Pos sc.Position +} + +func (e *ScannerError) Error() string { + return fmt.Sprintf("%s (at %s)", e.Msg, e.Pos) +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/markers/reg.go b/vendor/sigs.k8s.io/controller-tools/pkg/markers/reg.go new file mode 100644 index 0000000000..b0124630f8 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/markers/reg.go @@ -0,0 +1,153 @@ +/* +Copyright 2019 The Kubernetes 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 markers + +import ( + "fmt" + "sync" +) + +// Registry keeps track of registered definitions, and allows for easy lookup. +// It's thread-safe, and the zero-value can be safely used. +type Registry struct { + forPkg map[string]*Definition + forType map[string]*Definition + forField map[string]*Definition + helpFor map[*Definition]*DefinitionHelp + + mu sync.RWMutex + initOnce sync.Once +} + +func (r *Registry) init() { + r.initOnce.Do(func() { + if r.forPkg == nil { + r.forPkg = make(map[string]*Definition) + } + if r.forType == nil { + r.forType = make(map[string]*Definition) + } + if r.forField == nil { + r.forField = make(map[string]*Definition) + } + if r.helpFor == nil { + r.helpFor = make(map[*Definition]*DefinitionHelp) + } + }) +} + +// Define defines a new marker with the given name, target, and output type. +// It's a shortcut around +// r.Register(MakeDefinition(name, target, obj)) +func (r *Registry) Define(name string, target TargetType, obj interface{}) error { + def, err := MakeDefinition(name, target, obj) + if err != nil { + return err + } + return r.Register(def) +} + +// Register registers the given marker definition with this registry for later lookup. +func (r *Registry) Register(def *Definition) error { + r.init() + + r.mu.Lock() + defer r.mu.Unlock() + + switch def.Target { + case DescribesPackage: + r.forPkg[def.Name] = def + case DescribesType: + r.forType[def.Name] = def + case DescribesField: + r.forField[def.Name] = def + default: + return fmt.Errorf("unknown target type %v", def.Target) + } + return nil +} + +// AddHelp stores the given help in the registry, marking it as associated with +// the given definition. +func (r *Registry) AddHelp(def *Definition, help *DefinitionHelp) { + r.init() + + r.mu.Lock() + defer r.mu.Unlock() + + r.helpFor[def] = help +} + +// Lookup fetches the definition corresponding to the given name and target type. +func (r *Registry) Lookup(name string, target TargetType) *Definition { + r.init() + + r.mu.RLock() + defer r.mu.RUnlock() + + switch target { + case DescribesPackage: + return tryAnonLookup(name, r.forPkg) + case DescribesType: + return tryAnonLookup(name, r.forType) + case DescribesField: + return tryAnonLookup(name, r.forField) + default: + return nil + } +} + +// HelpFor fetches the help for a given definition, if present. +func (r *Registry) HelpFor(def *Definition) *DefinitionHelp { + r.init() + + r.mu.RLock() + defer r.mu.RUnlock() + + return r.helpFor[def] +} + +// AllDefinitions returns all marker definitions known to this registry. +func (r *Registry) AllDefinitions() []*Definition { + res := make([]*Definition, 0, len(r.forPkg)+len(r.forType)+len(r.forField)) + for _, def := range r.forPkg { + res = append(res, def) + } + for _, def := range r.forType { + res = append(res, def) + } + for _, def := range r.forField { + res = append(res, def) + } + return res +} + +// tryAnonLookup tries looking up the given marker as both an struct-based +// marker and an anonymous marker, returning whichever format matches first, +// preferring the longer (anonymous) name in case of conflicts. +func tryAnonLookup(name string, defs map[string]*Definition) *Definition { + // NB(directxman12): we look up anonymous names first to work with + // legacy style marker definitions that have a namespaced approach + // (e.g. deepcopy-gen, which uses `+k8s:deepcopy-gen=foo,bar` *and* + // `+k8s.io:deepcopy-gen:interfaces=foo`). + name, anonName, _ := splitMarker(name) + if def, exists := defs[anonName]; exists { + return def + } + + return defs[name] +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/markers/regutil.go b/vendor/sigs.k8s.io/controller-tools/pkg/markers/regutil.go new file mode 100644 index 0000000000..a9160c3c2f --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/markers/regutil.go @@ -0,0 +1,36 @@ +/* +Copyright 2019 The Kubernetes 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 markers + +// Must panics on errors creating definitions. +func Must(def *Definition, err error) *Definition { + if err != nil { + panic(err) + } + return def +} + +// RegisterAll attempts to register all definitions against the given registry, +// stopping and returning if an error occurs. +func RegisterAll(reg *Registry, defs ...*Definition) error { + for _, def := range defs { + if err := reg.Register(def); err != nil { + return err + } + } + return nil +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/markers/zip.go b/vendor/sigs.k8s.io/controller-tools/pkg/markers/zip.go new file mode 100644 index 0000000000..a9b4c98af5 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/markers/zip.go @@ -0,0 +1,191 @@ +/* +Copyright 2019 The Kubernetes 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 markers + +import ( + "go/ast" + "go/token" + "reflect" + "strings" + + "sigs.k8s.io/controller-tools/pkg/loader" +) + +// extractDoc extracts documentation from the given node, skipping markers +// in the godoc and falling back to the decl if necessary (for single-line decls). +func extractDoc(node ast.Node, decl *ast.GenDecl) string { + var docs *ast.CommentGroup + switch docced := node.(type) { + case *ast.Field: + docs = docced.Doc + case *ast.File: + docs = docced.Doc + case *ast.GenDecl: + docs = docced.Doc + case *ast.TypeSpec: + docs = docced.Doc + // type Ident expr expressions get docs attached to the decl, + // so check for that case (missing Lparen == single line type decl) + if docs == nil && decl.Lparen == token.NoPos { + docs = decl.Doc + } + } + + if docs == nil { + return "" + } + + // filter out markers + var outGroup ast.CommentGroup + outGroup.List = make([]*ast.Comment, 0, len(docs.List)) + for _, comment := range docs.List { + if isMarkerComment(comment.Text) { + continue + } + outGroup.List = append(outGroup.List, comment) + } + + // split lines, and re-join together as a single + // paragraph, respecting double-newlines as + // paragraph markers. + outLines := strings.Split(outGroup.Text(), "\n") + if outLines[len(outLines)-1] == "" { + // chop off the extraneous last part + outLines = outLines[:len(outLines)-1] + } + // respect double-newline meaning actual newline + for i, line := range outLines { + if line == "" { + outLines[i] = "\n" + } + } + return strings.Join(outLines, " ") +} + +// PackageMarkers collects all the package-level marker values for the given package. +func PackageMarkers(col *Collector, pkg *loader.Package) (MarkerValues, error) { + markers, err := col.MarkersInPackage(pkg) + if err != nil { + return nil, err + } + res := make(MarkerValues) + for _, file := range pkg.Syntax { + fileMarkers := markers[file] + for name, vals := range fileMarkers { + res[name] = append(res[name], vals...) + } + } + + return res, nil +} + +// FieldInfo contains marker values and commonly used information for a struct field. +type FieldInfo struct { + // Name is the name of the field (or "" for embedded fields) + Name string + // Doc is the Godoc of the field, pre-processed to remove markers and joine + // single newlines together. + Doc string + // Tag struct tag associated with this field (or "" if non existed). + Tag reflect.StructTag + + // Markers are all registered markers associated with this field. + Markers MarkerValues + + // RawField is the raw, underlying field AST object that this field represents. + RawField *ast.Field +} + +// TypeInfo contains marker values and commonly used information for a type declaration. +type TypeInfo struct { + // Name is the name of the type. + Name string + // Doc is the Godoc of the type, pre-processed to remove markers and joine + // single newlines together. + Doc string + + // Markers are all registered markers associated with the type. + Markers MarkerValues + + // Fields are all the fields associated with the type, if it's a struct. + // (if not, Fields will be nil). + Fields []FieldInfo + + // RawDecl contains the raw GenDecl that the type was declared as part of. + RawDecl *ast.GenDecl + // RawSpec contains the raw Spec that declared this type. + RawSpec *ast.TypeSpec + // RawFile contains the file in which this type was declared. + RawFile *ast.File +} + +// TypeCallback is a callback called for each type declaration in a package. +type TypeCallback func(info *TypeInfo) + +// EachType collects all markers, then calls the given callback for each type declaration in a package. +// Each individual spec is considered separate, so +// +// type ( +// Foo string +// Bar int +// Baz struct{} +// ) +// +// yields three calls to the callback. +func EachType(col *Collector, pkg *loader.Package, cb TypeCallback) error { + markers, err := col.MarkersInPackage(pkg) + if err != nil { + return err + } + + loader.EachType(pkg, func(file *ast.File, decl *ast.GenDecl, spec *ast.TypeSpec) { + var fields []FieldInfo + if structSpec, isStruct := spec.Type.(*ast.StructType); isStruct { + for _, field := range structSpec.Fields.List { + for _, name := range field.Names { + fields = append(fields, FieldInfo{ + Name: name.Name, + Doc: extractDoc(field, nil), + Tag: loader.ParseAstTag(field.Tag), + Markers: markers[field], + RawField: field, + }) + } + if field.Names == nil { + fields = append(fields, FieldInfo{ + Doc: extractDoc(field, nil), + Tag: loader.ParseAstTag(field.Tag), + Markers: markers[field], + RawField: field, + }) + } + } + } + + cb(&TypeInfo{ + Name: spec.Name.Name, + Markers: markers[spec], + Doc: extractDoc(spec, decl), + Fields: fields, + RawDecl: decl, + RawSpec: spec, + RawFile: file, + }) + }) + + return nil +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/rbac/parser.go b/vendor/sigs.k8s.io/controller-tools/pkg/rbac/parser.go new file mode 100644 index 0000000000..88ae805dd2 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/rbac/parser.go @@ -0,0 +1,258 @@ +/* +Copyright 2019 The Kubernetes 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 rbac contain libraries for generating RBAC manifests from RBAC +// markers in Go source files. +// +// The markers take the form: +// +// +kubebuilder:rbac:groups=,resources=,verbs=,urls= +package rbac + +import ( + "fmt" + "sort" + "strings" + + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "sigs.k8s.io/controller-tools/pkg/genall" + "sigs.k8s.io/controller-tools/pkg/markers" +) + +var ( + // RuleDefinition is a marker for defining RBAC rules. + // Call ToRule on the value to get a Kubernetes RBAC policy rule. + RuleDefinition = markers.Must(markers.MakeDefinition("kubebuilder:rbac", markers.DescribesPackage, Rule{})) +) + +// +controllertools:marker:generateHelp:category=RBAC + +// Rule specifies an RBAC rule to all access to some resources or non-resource URLs. +type Rule struct { + // Groups specifies the API groups that this rule encompasses. + Groups []string `marker:",optional"` + // Resources specifies the API resources that this rule encompasses. + Resources []string `marker:",optional"` + // Verbs specifies the (lowercase) kubernetes API verbs that this rule encompasses. + Verbs []string + // URL specifies the non-resource URLs that this rule encompasses. + URLs []string `marker:"urls,optional"` + // Namespace specifies the scope of the Rule. + // If not set, the Rule belongs to the generated ClusterRole. + // If set, the Rule belongs to a Role, whose namespace is specified by this field. + Namespace string `marker:",optional"` +} + +// ruleKey represents the resources and non-resources a Rule applies. +type ruleKey struct { + Groups string + Resources string + URLs string +} + +func (key ruleKey) String() string { + return fmt.Sprintf("%s + %s + %s", key.Groups, key.Resources, key.URLs) +} + +// ruleKeys implements sort.Interface +type ruleKeys []ruleKey + +func (keys ruleKeys) Len() int { return len(keys) } +func (keys ruleKeys) Swap(i, j int) { keys[i], keys[j] = keys[j], keys[i] } +func (keys ruleKeys) Less(i, j int) bool { return keys[i].String() < keys[j].String() } + +// key normalizes the Rule and returns a ruleKey object. +func (r *Rule) key() ruleKey { + r.normalize() + return ruleKey{ + Groups: strings.Join(r.Groups, "&"), + Resources: strings.Join(r.Resources, "&"), + URLs: strings.Join(r.URLs, "&"), + } +} + +// addVerbs adds new verbs into a Rule. +// The duplicates in `r.Verbs` will be removed, and then `r.Verbs` will be sorted. +func (r *Rule) addVerbs(verbs []string) { + r.Verbs = removeDupAndSort(append(r.Verbs, verbs...)) +} + +// normalize removes duplicates from each field of a Rule, and sorts each field. +func (r *Rule) normalize() { + r.Groups = removeDupAndSort(r.Groups) + r.Resources = removeDupAndSort(r.Resources) + r.Verbs = removeDupAndSort(r.Verbs) + r.URLs = removeDupAndSort(r.URLs) +} + +// removeDupAndSort removes duplicates in strs, sorts the items, and returns a +// new slice of strings. +func removeDupAndSort(strs []string) []string { + set := make(map[string]bool) + for _, str := range strs { + if _, ok := set[str]; !ok { + set[str] = true + } + } + + var result []string + for str := range set { + result = append(result, str) + } + sort.Strings(result) + return result +} + +// ToRule converts this rule to its Kubernetes API form. +func (r *Rule) ToRule() rbacv1.PolicyRule { + // fix the group names first, since letting people type "core" is nice + for i, group := range r.Groups { + if group == "core" { + r.Groups[i] = "" + } + } + return rbacv1.PolicyRule{ + APIGroups: r.Groups, + Verbs: r.Verbs, + Resources: r.Resources, + NonResourceURLs: r.URLs, + } +} + +// +controllertools:marker:generateHelp + +// Generator generates ClusterRole objects. +type Generator struct { + // RoleName sets the name of the generated ClusterRole. + RoleName string +} + +func (Generator) RegisterMarkers(into *markers.Registry) error { + if err := into.Register(RuleDefinition); err != nil { + return err + } + into.AddHelp(RuleDefinition, Rule{}.Help()) + return nil +} + +// GenerateRoles generate a slice of objs representing either a ClusterRole or a Role object +// The order of the objs in the returned slice is stable and determined by their namespaces. +func GenerateRoles(ctx *genall.GenerationContext, roleName string) ([]interface{}, error) { + rulesByNS := make(map[string][]*Rule) + for _, root := range ctx.Roots { + markerSet, err := markers.PackageMarkers(ctx.Collector, root) + if err != nil { + root.AddError(err) + } + + // group RBAC markers by namespace + for _, markerValue := range markerSet[RuleDefinition.Name] { + rule := markerValue.(Rule) + namespace := rule.Namespace + if _, ok := rulesByNS[namespace]; !ok { + rules := make([]*Rule, 0) + rulesByNS[namespace] = rules + } + rulesByNS[namespace] = append(rulesByNS[namespace], &rule) + } + } + + // NormalizeRules merge Rule with the same ruleKey and sort the Rules + NormalizeRules := func(rules []*Rule) []rbacv1.PolicyRule { + ruleMap := make(map[ruleKey]*Rule) + // all the Rules having the same ruleKey will be merged into the first Rule + for _, rule := range rules { + key := rule.key() + if _, ok := ruleMap[key]; !ok { + ruleMap[key] = rule + continue + } + ruleMap[key].addVerbs(rule.Verbs) + } + + // sort the Rules in rules according to their ruleKeys + keys := make([]ruleKey, 0, len(ruleMap)) + for key := range ruleMap { + keys = append(keys, key) + } + sort.Sort(ruleKeys(keys)) + + var policyRules []rbacv1.PolicyRule + for _, key := range keys { + policyRules = append(policyRules, ruleMap[key].ToRule()) + + } + return policyRules + } + + // collect all the namespaces and sort them + var namespaces []string + for ns := range rulesByNS { + namespaces = append(namespaces, ns) + } + sort.Strings(namespaces) + + // process the items in rulesByNS by the order specified in `namespaces` to make sure that the Role order is stable + var objs []interface{} + for _, ns := range namespaces { + rules := rulesByNS[ns] + policyRules := NormalizeRules(rules) + if len(policyRules) == 0 { + continue + } + if ns == "" { + objs = append(objs, rbacv1.ClusterRole{ + TypeMeta: metav1.TypeMeta{ + Kind: "ClusterRole", + APIVersion: rbacv1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: roleName, + }, + Rules: policyRules, + }) + } else { + objs = append(objs, rbacv1.Role{ + TypeMeta: metav1.TypeMeta{ + Kind: "Role", + APIVersion: rbacv1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: roleName, + Namespace: ns, + }, + Rules: policyRules, + }) + } + } + + return objs, nil +} + +func (g Generator) Generate(ctx *genall.GenerationContext) error { + objs, err := GenerateRoles(ctx, g.RoleName) + if err != nil { + return err + } + + if len(objs) == 0 { + return nil + } + + return ctx.WriteYAML("role.yaml", objs...) +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/rbac/zz_generated.markerhelp.go b/vendor/sigs.k8s.io/controller-tools/pkg/rbac/zz_generated.markerhelp.go new file mode 100644 index 0000000000..f58a9ed940 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/rbac/zz_generated.markerhelp.go @@ -0,0 +1,73 @@ +// +build !ignore_autogenerated + +/* +Copyright2019 The Kubernetes 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. +*/ + +// Code generated by helpgen. DO NOT EDIT. + +package rbac + +import ( + "sigs.k8s.io/controller-tools/pkg/markers" +) + +func (Generator) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "", + DetailedHelp: markers.DetailedHelp{ + Summary: "generates ClusterRole objects.", + Details: "", + }, + FieldHelp: map[string]markers.DetailedHelp{ + "RoleName": markers.DetailedHelp{ + Summary: "sets the name of the generated ClusterRole.", + Details: "", + }, + }, + } +} + +func (Rule) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "RBAC", + DetailedHelp: markers.DetailedHelp{ + Summary: "specifies an RBAC rule to all access to some resources or non-resource URLs.", + Details: "", + }, + FieldHelp: map[string]markers.DetailedHelp{ + "Groups": markers.DetailedHelp{ + Summary: "specifies the API groups that this rule encompasses.", + Details: "", + }, + "Resources": markers.DetailedHelp{ + Summary: "specifies the API resources that this rule encompasses.", + Details: "", + }, + "Verbs": markers.DetailedHelp{ + Summary: "specifies the (lowercase) kubernetes API verbs that this rule encompasses.", + Details: "", + }, + "URLs": markers.DetailedHelp{ + Summary: "URL specifies the non-resource URLs that this rule encompasses.", + Details: "", + }, + "Namespace": markers.DetailedHelp{ + Summary: "specifies the scope of the Rule. If not set, the Rule belongs to the generated ClusterRole. If set, the Rule belongs to a Role, whose namespace is specified by this field.", + Details: "", + }, + }, + } +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/schemapatcher/gen.go b/vendor/sigs.k8s.io/controller-tools/pkg/schemapatcher/gen.go new file mode 100644 index 0000000000..5217ee4b9d --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/schemapatcher/gen.go @@ -0,0 +1,508 @@ +/* +Copyright 2019 The Kubernetes 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 schemapatcher + +import ( + "fmt" + "io/ioutil" + "path/filepath" + + "gopkg.in/yaml.v3" + apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + apiextlegacy "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" + "k8s.io/apimachinery/pkg/api/equality" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + kyaml "sigs.k8s.io/yaml" + + crdgen "sigs.k8s.io/controller-tools/pkg/crd" + crdmarkers "sigs.k8s.io/controller-tools/pkg/crd/markers" + "sigs.k8s.io/controller-tools/pkg/genall" + "sigs.k8s.io/controller-tools/pkg/markers" + yamlop "sigs.k8s.io/controller-tools/pkg/schemapatcher/internal/yaml" +) + +// NB(directxman12): this code is quite fragile, but there are a sufficient +// number of corner cases that it's hard to decompose into separate tools. +// When in doubt, ping @sttts. +// +// Namely: +// - It needs to only update existing versions +// - It needs to make "stable" changes that don't mess with map key ordering +// (in order to facilitate validating that no change has occurred) +// - It needs to collapse identical schema versions into a top-level schema, +// if all versions are identical (this is a common requirement to all CRDs, +// but in this case it means simple jsonpatch wouldn't suffice) + +// TODO(directxman12): When CRD v1 rolls around, consider splitting this into a +// tool that generates a patch, and a separate tool for applying stable YAML +// patches. + +var ( + legacyAPIExtVersion = apiextlegacy.SchemeGroupVersion.String() + currentAPIExtVersion = apiext.SchemeGroupVersion.String() +) + +// +controllertools:marker:generateHelp + +// Generator patches existing CRDs with new schemata. +// +// For legacy (v1beta1) single-version CRDs, it will simply replace the global schema. +// +// For legacy (v1beta1) multi-version CRDs, and any v1 CRDs, it will replace +// schemata of existing versions and *clear the schema* from any versions not +// specified in the Go code. It will *not* add new versions, or remove old +// ones. +// +// For legacy multi-version CRDs with identical schemata, it will take care of +// lifting the per-version schema up to the global schema. +// +// It will generate output for each "CRD Version" (API version of the CRD type +// itself) , e.g. apiextensions/v1beta1 and apiextensions/v1) available. +type Generator struct { + // ManifestsPath contains the CustomResourceDefinition YAML files. + ManifestsPath string `marker:"manifests"` + + // MaxDescLen specifies the maximum description length for fields in CRD's OpenAPI schema. + // + // 0 indicates drop the description for all fields completely. + // n indicates limit the description to at most n characters and truncate the description to + // closest sentence boundary if it exceeds n characters. + MaxDescLen *int `marker:",optional"` +} + +var _ genall.Generator = &Generator{} + +func (Generator) RegisterMarkers(into *markers.Registry) error { + return crdmarkers.Register(into) +} + +func (g Generator) Generate(ctx *genall.GenerationContext) (result error) { + parser := &crdgen.Parser{ + Collector: ctx.Collector, + Checker: ctx.Checker, + } + + crdgen.AddKnownTypes(parser) + for _, root := range ctx.Roots { + parser.NeedPackage(root) + } + + metav1Pkg := crdgen.FindMetav1(ctx.Roots) + if metav1Pkg == nil { + // no objects in the roots, since nothing imported metav1 + return nil + } + + // load existing CRD manifests with group-kind and versions + partialCRDSets, err := crdsFromDirectory(ctx, g.ManifestsPath) + if err != nil { + return err + } + + // generate schemata for the types we care about, and save them to be written later. + for _, groupKind := range crdgen.FindKubeKinds(parser, metav1Pkg) { + existingSet, wanted := partialCRDSets[groupKind] + if !wanted { + continue + } + + for pkg, gv := range parser.GroupVersions { + if gv.Group != groupKind.Group { + continue + } + if _, wantedVersion := existingSet.Versions[gv.Version]; !wantedVersion { + continue + } + + typeIdent := crdgen.TypeIdent{Package: pkg, Name: groupKind.Kind} + parser.NeedFlattenedSchemaFor(typeIdent) + + fullSchema := parser.FlattenedSchemata[typeIdent] + if g.MaxDescLen != nil { + fullSchema = *fullSchema.DeepCopy() + crdgen.TruncateDescription(&fullSchema, *g.MaxDescLen) + } + existingSet.NewSchemata[gv.Version] = fullSchema + } + } + + // patch existing CRDs with new schemata + for _, existingSet := range partialCRDSets { + // first, figure out if we need to merge schemata together if they're *all* + // identical (meaning we also don't have any "unset" versions) + + if len(existingSet.NewSchemata) == 0 { + continue + } + + // copy over the new versions that we have, keeping old versions so + // that we can tell if a schema would be nil + var someVer string + for ver := range existingSet.NewSchemata { + someVer = ver + existingSet.Versions[ver] = struct{}{} + } + + allSame := true + firstSchema := existingSet.NewSchemata[someVer] + for ver := range existingSet.Versions { + otherSchema, hasSchema := existingSet.NewSchemata[ver] + if !hasSchema || !equality.Semantic.DeepEqual(firstSchema, otherSchema) { + allSame = false + break + } + } + + if allSame { + if err := existingSet.setGlobalSchema(); err != nil { + return fmt.Errorf("failed to set global firstSchema for %s: %v", existingSet.GroupKind, err) + } + } else { + if err := existingSet.setVersionedSchemata(); err != nil { + return fmt.Errorf("failed to set versioned schemas for %s: %v", existingSet.GroupKind, err) + } + } + } + + // write the final result out to the new location + for _, set := range partialCRDSets { + // We assume all CRD versions came from different files, since this + // is how controller-gen works. If they came from the same file, + // it'd be non-sensical, since you couldn't reasonably use kubectl + // with them against older servers. + for _, crd := range set.CRDVersions { + if err := func() error { + outWriter, err := ctx.OutputRule.Open(nil, crd.FileName) + if err != nil { + return err + } + defer outWriter.Close() + + enc := yaml.NewEncoder(outWriter) + // yaml.v2 defaults to indent=2, yaml.v3 defaults to indent=4, + // so be compatible with everything else in k8s and choose 2. + enc.SetIndent(2) + + return enc.Encode(crd.Yaml) + }(); err != nil { + return err + } + } + } + + return nil +} + +// partialCRDSet represents a set of CRDs of different apiext versions +// (v1beta1.CRD vs v1.CRD) that represent the same GroupKind. +// +// It tracks modifications to the schemata of those CRDs from this source file, +// plus some useful structured content, and keeps track of the raw YAML representation +// of the different apiext versions. +type partialCRDSet struct { + // GroupKind is the GroupKind represented by this CRD. + GroupKind schema.GroupKind + // NewSchemata are the new schemata generated from Go IDL by controller-gen. + NewSchemata map[string]apiext.JSONSchemaProps + // CRDVersions are the forms of this CRD across different apiextensions + // versions + CRDVersions []*partialCRD + // Versions are the versions of the given GroupKind in this set of CRDs. + Versions map[string]struct{} +} + +// partialCRD represents the raw YAML encoding of a given CRD instance, plus +// the versions contained therein for easy lookup. +type partialCRD struct { + // Yaml is the raw YAML structure of the CRD. + Yaml *yaml.Node + // FileName is the source name of the file that this was read from. + // + // This isn't on partialCRDSet because we could have different CRD versions + // stored in the same file (like controller-tools does by default) or in + // different files. + FileName string + + // CRDVersion is the version of the CRD object itself, from + // apiextensions (currently apiextensions/v1 or apiextensions/v1beta1). + CRDVersion string +} + +// setGlobalSchema sets the global schema for the v1beta1 apiext version in +// this set (if present, as per partialCRD.setGlobalSchema), and sets the +// versioned schemas (as per setVersionedSchemata) for the v1 version. +func (e *partialCRDSet) setGlobalSchema() error { + // there's no easy way to get a "random" key from a go map :-/ + var schema apiext.JSONSchemaProps + for ver := range e.NewSchemata { + schema = e.NewSchemata[ver] + break + } + for _, crdInfo := range e.CRDVersions { + switch crdInfo.CRDVersion { + case legacyAPIExtVersion: + if err := crdInfo.setGlobalSchema(schema); err != nil { + return err + } + case currentAPIExtVersion: + // just set the schemata as normal for non-legacy versions + if err := crdInfo.setVersionedSchemata(e.NewSchemata); err != nil { + return err + } + } + } + return nil +} + +// setGlobalSchema sets the global schema to one of the schemata +// for this CRD. All schemata must be identical for this to be a valid operation. +func (e *partialCRD) setGlobalSchema(newSchema apiext.JSONSchemaProps) error { + if e.CRDVersion != legacyAPIExtVersion { + // no global schema, nothing to do + return fmt.Errorf("cannot set global schema on non-legacy CRD versions") + } + schema, err := legacySchema(newSchema) + if err != nil { + return fmt.Errorf("failed to convert schema to legacy form: %v", err) + } + schemaNodeTree, err := yamlop.ToYAML(schema) + if err != nil { + return err + } + schemaNodeTree = schemaNodeTree.Content[0] // get rid of the document node + yamlop.SetStyle(schemaNodeTree, 0) // clear the style so it defaults to auto-style-choice + + if err := yamlop.SetNode(e.Yaml, *schemaNodeTree, "spec", "validation", "openAPIV3Schema"); err != nil { + return err + } + + versions, found, err := e.getVersionsNode() + if err != nil { + return err + } + if !found { + return nil + } + for i, verNode := range versions.Content { + if err := yamlop.DeleteNode(verNode, "schema"); err != nil { + return fmt.Errorf("spec.versions[%d]: %v", i, err) + } + } + + return nil +} + +// getVersionsNode gets the YAML node of .spec.versions YAML mapping, +// if returning the node, and whether or not it was present. +func (e *partialCRD) getVersionsNode() (*yaml.Node, bool, error) { + versions, found, err := yamlop.GetNode(e.Yaml, "spec", "versions") + if err != nil { + return nil, false, err + } + if !found { + return nil, false, nil + } + if versions.Kind != yaml.SequenceNode { + return nil, true, fmt.Errorf("unexpected non-sequence versions") + } + return versions, found, nil +} + +// setVersionedSchemata sets the versioned schemata on each encoding in this set as per +// setVersionedSchemata on partialCRD. +func (e *partialCRDSet) setVersionedSchemata() error { + for _, crdInfo := range e.CRDVersions { + if err := crdInfo.setVersionedSchemata(e.NewSchemata); err != nil { + return err + } + } + return nil +} + +// setVersionedSchemata populates all existing versions with new schemata, +// wiping the schema of any version that doesn't have a listed schema. +// Any "unknown" versions are ignored. +func (e *partialCRD) setVersionedSchemata(newSchemata map[string]apiext.JSONSchemaProps) error { + var err error + if err := yamlop.DeleteNode(e.Yaml, "spec", "validation"); err != nil { + return err + } + + versions, found, err := e.getVersionsNode() + if err != nil { + return err + } + if !found { + return fmt.Errorf("unexpected missing versions") + } + + for i, verNode := range versions.Content { + nameNode, _, _ := yamlop.GetNode(verNode, "name") + if nameNode.Kind != yaml.ScalarNode || nameNode.ShortTag() != "!!str" { + return fmt.Errorf("version name was not a string at spec.versions[%d]", i) + } + name := nameNode.Value + if name == "" { + return fmt.Errorf("unexpected empty name at spec.versions[%d]", i) + } + newSchema, found := newSchemata[name] + if !found { + if err := yamlop.DeleteNode(verNode, "schema"); err != nil { + return fmt.Errorf("spec.versions[%d]: %v", i, err) + } + } else { + // TODO(directxman12): if this gets to be more than 2 versions, use polymorphism to clean this up + var verSchema interface{} = newSchema + if e.CRDVersion == legacyAPIExtVersion { + verSchema, err = legacySchema(newSchema) + if err != nil { + return fmt.Errorf("failed to convert schema to legacy form: %v", err) + } + } + + schemaNodeTree, err := yamlop.ToYAML(verSchema) + if err != nil { + return fmt.Errorf("failed to convert schema to YAML: %v", err) + } + schemaNodeTree = schemaNodeTree.Content[0] // get rid of the document node + yamlop.SetStyle(schemaNodeTree, 0) // clear the style so it defaults to an auto-chosen one + if err := yamlop.SetNode(verNode, *schemaNodeTree, "schema", "openAPIV3Schema"); err != nil { + return fmt.Errorf("spec.versions[%d]: %v", i, err) + } + } + } + return nil +} + +// crdsFromDirectory returns loads all CRDs from the given directory in a +// manner that preserves ordering, comments, etc in order to make patching +// minimally invasive. Returned CRDs are mapped by group-kind. +func crdsFromDirectory(ctx *genall.GenerationContext, dir string) (map[schema.GroupKind]*partialCRDSet, error) { + res := map[schema.GroupKind]*partialCRDSet{} + dirEntries, err := ioutil.ReadDir(dir) + if err != nil { + return nil, err + } + for _, fileInfo := range dirEntries { + // find all files that are YAML + if fileInfo.IsDir() || filepath.Ext(fileInfo.Name()) != ".yaml" { + continue + } + + rawContent, err := ctx.ReadFile(filepath.Join(dir, fileInfo.Name())) + if err != nil { + return nil, err + } + + // NB(directxman12): we could use the universal deserializer for this, but it's + // really pretty clunky, and the alternative is actually kinda easier to understand + + // ensure that this is a CRD + var typeMeta metav1.TypeMeta + if err := kyaml.Unmarshal(rawContent, &typeMeta); err != nil { + continue + } + if !isSupportedAPIExtGroupVer(typeMeta.APIVersion) || typeMeta.Kind != "CustomResourceDefinition" { + continue + } + + // collect the group-kind and versions from the actual structured form + var actualCRD crdIsh + if err := kyaml.Unmarshal(rawContent, &actualCRD); err != nil { + continue + } + groupKind := schema.GroupKind{Group: actualCRD.Spec.Group, Kind: actualCRD.Spec.Names.Kind} + var versions map[string]struct{} + if len(actualCRD.Spec.Versions) == 0 { + versions = map[string]struct{}{actualCRD.Spec.Version: struct{}{}} + } else { + versions = make(map[string]struct{}, len(actualCRD.Spec.Versions)) + for _, ver := range actualCRD.Spec.Versions { + versions[ver.Name] = struct{}{} + } + } + + // then actually unmarshal in a manner that preserves ordering, etc + var yamlNodeTree yaml.Node + if err := yaml.Unmarshal(rawContent, &yamlNodeTree); err != nil { + continue + } + + // then store this CRDVersion of the CRD in a set, populating the set if necessary + if res[groupKind] == nil { + res[groupKind] = &partialCRDSet{ + GroupKind: groupKind, + NewSchemata: make(map[string]apiext.JSONSchemaProps), + Versions: make(map[string]struct{}), + } + } + for ver := range versions { + res[groupKind].Versions[ver] = struct{}{} + } + res[groupKind].CRDVersions = append(res[groupKind].CRDVersions, &partialCRD{ + Yaml: &yamlNodeTree, + FileName: fileInfo.Name(), + CRDVersion: typeMeta.APIVersion, + }) + } + return res, nil +} + +// isSupportedAPIExtGroupVer checks if the given string-form group-version +// is one of the known apiextensions versions (v1, v1beta1). +func isSupportedAPIExtGroupVer(groupVer string) bool { + return groupVer == currentAPIExtVersion || groupVer == legacyAPIExtVersion +} + +// crdIsh is a merged blob of CRD fields that looks enough like all versions of +// CRD to extract the relevant information for partialCRDSet and partialCRD. +// +// We keep this separate so it's clear what info we need, and so we don't break +// when we switch canonical internal versions and lose old fields while gaining +// new ones (like in v1beta1 --> v1). +// +// Its use is tied directly to crdsFromDirectory, and is mostly an implementation detail of that. +type crdIsh struct { + Spec struct { + Group string `json:"group"` + Names struct { + Kind string `json:"kind"` + } `json:"names"` + Versions []struct { + Name string `json:"name"` + } `json:"versions"` + Version string `json:"version"` + } `json:"spec"` +} + +// legacySchema jumps through some hoops to convert a v1 schema to a v1beta1 schema. +func legacySchema(origSchema apiext.JSONSchemaProps) (apiextlegacy.JSONSchemaProps, error) { + shellCRD := apiext.CustomResourceDefinition{} + shellCRD.APIVersion = currentAPIExtVersion + shellCRD.Kind = "CustomResourceDefinition" + shellCRD.Spec.Versions = []apiext.CustomResourceDefinitionVersion{ + {Schema: &apiext.CustomResourceValidation{OpenAPIV3Schema: origSchema.DeepCopy()}}, + } + + legacyCRD, err := crdgen.AsVersion(shellCRD, apiextlegacy.SchemeGroupVersion) + if err != nil { + return apiextlegacy.JSONSchemaProps{}, err + } + + return *legacyCRD.(*apiextlegacy.CustomResourceDefinition).Spec.Validation.OpenAPIV3Schema, nil +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/schemapatcher/internal/yaml/convert.go b/vendor/sigs.k8s.io/controller-tools/pkg/schemapatcher/internal/yaml/convert.go new file mode 100644 index 0000000000..ed47c0a49a --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/schemapatcher/internal/yaml/convert.go @@ -0,0 +1,61 @@ +/* +Copyright 2019 The Kubernetes 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 yaml + +import ( + "encoding/json" + "fmt" + + "gopkg.in/yaml.v3" +) + +// ToYAML converts some object that serializes to JSON into a YAML node tree. +// It's useful since it pays attention to JSON tags, unlike yaml.Unmarshal or +// yaml.Node.Decode. +func ToYAML(rawObj interface{}) (*yaml.Node, error) { + if rawObj == nil { + return &yaml.Node{Kind: yaml.ScalarNode, Value: "null", Tag: "!!null"}, nil + } + + rawJSON, err := json.Marshal(rawObj) + if err != nil { + return nil, fmt.Errorf("failed to marshal object: %v", err) + } + + var out yaml.Node + if err := yaml.Unmarshal(rawJSON, &out); err != nil { + return nil, fmt.Errorf("unable to unmarshal marshalled object: %v", err) + } + return &out, nil +} + +// changeAll calls the given callback for all nodes in +// the given YAML node tree. +func changeAll(root *yaml.Node, cb func(*yaml.Node)) { + cb(root) + for _, child := range root.Content { + changeAll(child, cb) + } +} + +// SetStyle sets the style for all nodes in the given +// node tree to the given style. +func SetStyle(root *yaml.Node, style yaml.Style) { + changeAll(root, func(node *yaml.Node) { + node.Style = style + }) +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/schemapatcher/internal/yaml/nested.go b/vendor/sigs.k8s.io/controller-tools/pkg/schemapatcher/internal/yaml/nested.go new file mode 100644 index 0000000000..e6bcbb71f0 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/schemapatcher/internal/yaml/nested.go @@ -0,0 +1,87 @@ +/* +Copyright 2019 The Kubernetes 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 yaml + +import ( + "fmt" + + "gopkg.in/yaml.v3" +) + +// ValueInMapping finds the value node with the corresponding string key +// in the given mapping node. If the given node is not a mapping, an +// error will be returned. +func ValueInMapping(root *yaml.Node, key string) (*yaml.Node, error) { + if root.Kind != yaml.MappingNode { + return nil, fmt.Errorf("unexpected non-mapping node") + } + + for i := 0; i < len(root.Content)/2; i++ { + keyNode := root.Content[i*2] + if keyNode.Value == key { + return root.Content[i*2+1], nil + } + } + return nil, nil +} + +// asCloseAsPossible goes as deep on the given path as possible, returning the +// last node that existed from the given path in the given tree of mapping +// nodes, as well as the rest of the path that could not be fetched, if any. +func asCloseAsPossible(root *yaml.Node, path ...string) (*yaml.Node, []string, error) { + if root == nil { + return nil, path, nil + } + if root.Kind == yaml.DocumentNode && len(root.Content) > 0 { + root = root.Content[0] + } + + currNode := root + for ; len(path) > 0; path = path[1:] { + if currNode.Kind != yaml.MappingNode { + return nil, nil, fmt.Errorf("unexpected non-mapping (%v) before path %v", currNode.Kind, path) + } + + nextNode, err := ValueInMapping(currNode, path[0]) + if err != nil { + return nil, nil, fmt.Errorf("unable to get next node in path %v: %v", path, err) + } + + if nextNode == nil { + // we're as close as possible + break + } + + currNode = nextNode + } + + return currNode, path, nil +} + +// GetNode gets the node at the given path in the given sequence of mapping +// nodes, or, if it doesn't exist, returning false. +func GetNode(root *yaml.Node, path ...string) (*yaml.Node, bool, error) { + resNode, restPath, err := asCloseAsPossible(root, path...) + if err != nil { + return nil, false, err + } + // more path means the node didn't exist + if len(restPath) != 0 { + return nil, false, nil + } + return resNode, true, nil +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/schemapatcher/internal/yaml/set.go b/vendor/sigs.k8s.io/controller-tools/pkg/schemapatcher/internal/yaml/set.go new file mode 100644 index 0000000000..ede417f1cf --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/schemapatcher/internal/yaml/set.go @@ -0,0 +1,80 @@ +/* +Copyright 2019 The Kubernetes 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 yaml + +import ( + "fmt" + + "gopkg.in/yaml.v3" +) + +// SetNode sets the given path to the given yaml Node, creating mapping nodes along the way. +func SetNode(root *yaml.Node, val yaml.Node, path ...string) error { + currNode, path, err := asCloseAsPossible(root, path...) + if err != nil { + return err + } + + if len(path) > 0 { + if currNode.Kind != yaml.MappingNode { + return fmt.Errorf("unexpected non-mapping before path %v", path) + } + + for ; len(path) > 0; path = path[1:] { + keyNode := yaml.Node{Kind: yaml.ScalarNode, Tag: "!!str", Style: yaml.DoubleQuotedStyle, Value: path[0]} + nextNode := &yaml.Node{Kind: yaml.MappingNode} + currNode.Content = append(currNode.Content, &keyNode, nextNode) + + currNode = nextNode + } + } + + *currNode = val + return nil +} + +// DeleteNode deletes the node at the given path in the given tree of mapping nodes. +// It's a noop if the path doesn't exist. +func DeleteNode(root *yaml.Node, path ...string) error { + if len(path) == 0 { + return fmt.Errorf("must specify a path to delete") + } + pathToParent, keyToDelete := path[:len(path)-1], path[len(path)-1] + parentNode, path, err := asCloseAsPossible(root, pathToParent...) + if err != nil { + return err + } + if len(path) > 0 { + // no-op, parent node doesn't exist + return nil + } + + if parentNode.Kind != yaml.MappingNode { + return fmt.Errorf("unexpected non-mapping node") + } + + for i := 0; i < len(parentNode.Content)/2; i++ { + keyNode := parentNode.Content[i*2] + if keyNode.Value == keyToDelete { + parentNode.Content = append(parentNode.Content[:i*2], parentNode.Content[i*2+2:]...) + return nil + } + } + + // no-op, key not found in parent node + return nil +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/schemapatcher/zz_generated.markerhelp.go b/vendor/sigs.k8s.io/controller-tools/pkg/schemapatcher/zz_generated.markerhelp.go new file mode 100644 index 0000000000..230cb9c066 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/schemapatcher/zz_generated.markerhelp.go @@ -0,0 +1,45 @@ +// +build !ignore_autogenerated + +/* +Copyright2019 The Kubernetes 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. +*/ + +// Code generated by helpgen. DO NOT EDIT. + +package schemapatcher + +import ( + "sigs.k8s.io/controller-tools/pkg/markers" +) + +func (Generator) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "", + DetailedHelp: markers.DetailedHelp{ + Summary: "patches existing CRDs with new schemata. ", + Details: "For legacy (v1beta1) single-version CRDs, it will simply replace the global schema. \n For legacy (v1beta1) multi-version CRDs, and any v1 CRDs, it will replace schemata of existing versions and *clear the schema* from any versions not specified in the Go code. It will *not* add new versions, or remove old ones. \n For legacy multi-version CRDs with identical schemata, it will take care of lifting the per-version schema up to the global schema. \n It will generate output for each \"CRD Version\" (API version of the CRD type itself) , e.g. apiextensions/v1beta1 and apiextensions/v1) available.", + }, + FieldHelp: map[string]markers.DetailedHelp{ + "ManifestsPath": markers.DetailedHelp{ + Summary: "contains the CustomResourceDefinition YAML files.", + Details: "", + }, + "MaxDescLen": markers.DetailedHelp{ + Summary: "specifies the maximum description length for fields in CRD's OpenAPI schema. ", + Details: "0 indicates drop the description for all fields completely. n indicates limit the description to at most n characters and truncate the description to closest sentence boundary if it exceeds n characters.", + }, + }, + } +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/version/version.go b/vendor/sigs.k8s.io/controller-tools/pkg/version/version.go new file mode 100644 index 0000000000..245826f692 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/version/version.go @@ -0,0 +1,49 @@ +/* +Copyright 2019 The Kubernetes 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 version + +import ( + "fmt" + "runtime/debug" +) + +// Version returns the version of the main module +func Version() string { + info, ok := debug.ReadBuildInfo() + if !ok { + // binary has not been built with module support + return "(unknown)" + } + return info.Main.Version +} + +// Print prints the main module version on stdout. +// +// Print will display either: +// +// - "Version: v0.2.1" when the program has been compiled with: +// +// $ go get github.com/controller-tools/cmd/controller-gen@v0.2.1 +// +// Note: go modules requires the usage of semver compatible tags starting with +// 'v' to have nice human-readable versions. +// +// - "Version: (devel)" when the program is compiled from a local git checkout. +// +// - "Version: (unknown)" when not using go modules. +func Print() { + fmt.Printf("Version: %s\n", Version()) +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/webhook/parser.go b/vendor/sigs.k8s.io/controller-tools/pkg/webhook/parser.go new file mode 100644 index 0000000000..1db5534962 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/webhook/parser.go @@ -0,0 +1,250 @@ +/* +Copyright 2018 The Kubernetes 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 webhook contains libraries for generating webhookconfig manifests +// from markers in Go source files. +// +// The markers take the form: +// +// +kubebuilder:webhook:failurePolicy=,groups=<[]string>,resources=<[]string>,verbs=<[]string>,versions=<[]string>,name=,path=,mutating= +package webhook + +import ( + "fmt" + "strings" + + admissionreg "k8s.io/api/admissionregistration/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "sigs.k8s.io/controller-tools/pkg/genall" + "sigs.k8s.io/controller-tools/pkg/markers" +) + +var ( + // ConfigDefinition s a marker for defining Webhook manifests. + // Call ToWebhook on the value to get a Kubernetes Webhook. + ConfigDefinition = markers.Must(markers.MakeDefinition("kubebuilder:webhook", markers.DescribesPackage, Config{})) +) + +// +controllertools:marker:generateHelp:category=Webhook + +// Config specifies how a webhook should be served. +// +// It specifies only the details that are intrinsic to the application serving +// it (e.g. the resources it can handle, or the path it serves on). +type Config struct { + // Mutating marks this as a mutating webhook (it's validating only if false) + // + // Mutating webhooks are allowed to change the object in their response, + // and are called *before* all validating webhooks. Mutating webhooks may + // choose to reject an object, similarly to a validating webhook. + Mutating bool + // FailurePolicy specifies what should happen if the API server cannot reach the webhook. + // + // It may be either "ignore" (to skip the webhook and continue on) or "fail" (to reject + // the object in question). + FailurePolicy string + + // Groups specifies the API groups that this webhook receives requests for. + Groups []string + // Resources specifies the API resources that this webhook receives requests for. + Resources []string + // Verbs specifies the Kubernetes API verbs that this webhook receives requests for. + // + // Only modification-like verbs may be specified. + // May be "create", "update", "delete", "connect", or "*" (for all). + Verbs []string + // Versions specifies the API versions that this webhook receives requests for. + Versions []string + + // Name indicates the name of this webhook configuration. + Name string + // Path specifies that path that the API server should connect to this webhook on. + Path string +} + +// verbToAPIVariant converts a marker's verb to the proper value for the API. +// Unrecognized verbs are passed through. +func verbToAPIVariant(verbRaw string) admissionreg.OperationType { + switch strings.ToLower(verbRaw) { + case strings.ToLower(string(admissionreg.Create)): + return admissionreg.Create + case strings.ToLower(string(admissionreg.Update)): + return admissionreg.Update + case strings.ToLower(string(admissionreg.Delete)): + return admissionreg.Delete + case strings.ToLower(string(admissionreg.Connect)): + return admissionreg.Connect + case strings.ToLower(string(admissionreg.OperationAll)): + return admissionreg.OperationAll + default: + return admissionreg.OperationType(verbRaw) + } +} + +// ToMutatingWebhook converts this rule to its Kubernetes API form. +func (c Config) ToMutatingWebhook() (admissionreg.MutatingWebhook, error) { + if !c.Mutating { + return admissionreg.MutatingWebhook{}, fmt.Errorf("%s is a validating webhook", c.Name) + } + + return admissionreg.MutatingWebhook{ + Name: c.Name, + Rules: c.rules(), + FailurePolicy: c.failurePolicy(), + ClientConfig: c.clientConfig(), + }, nil +} + +// ToValidatingWebhook converts this rule to its Kubernetes API form. +func (c Config) ToValidatingWebhook() (admissionreg.ValidatingWebhook, error) { + if c.Mutating { + return admissionreg.ValidatingWebhook{}, fmt.Errorf("%s is a mutating webhook", c.Name) + } + + return admissionreg.ValidatingWebhook{ + Name: c.Name, + Rules: c.rules(), + FailurePolicy: c.failurePolicy(), + ClientConfig: c.clientConfig(), + }, nil +} + +// rules returns the configuration of what operations on what +// resources/subresources a webhook should care about. +func (c Config) rules() []admissionreg.RuleWithOperations { + whConfig := admissionreg.RuleWithOperations{ + Rule: admissionreg.Rule{ + APIGroups: c.Groups, + APIVersions: c.Versions, + Resources: c.Resources, + }, + Operations: make([]admissionreg.OperationType, len(c.Verbs)), + } + + for i, verbRaw := range c.Verbs { + whConfig.Operations[i] = verbToAPIVariant(verbRaw) + } + + // fix the group names, since letting people type "core" is nice + for i, group := range whConfig.APIGroups { + if group == "core" { + whConfig.APIGroups[i] = "" + } + } + + return []admissionreg.RuleWithOperations{whConfig} +} + +// failurePolicy converts the string value to the proper value for the API. +// Unrecognized values are passed through. +func (c Config) failurePolicy() *admissionreg.FailurePolicyType { + var failurePolicy admissionreg.FailurePolicyType + switch strings.ToLower(c.FailurePolicy) { + case strings.ToLower(string(admissionreg.Ignore)): + failurePolicy = admissionreg.Ignore + case strings.ToLower(string(admissionreg.Fail)): + failurePolicy = admissionreg.Fail + default: + failurePolicy = admissionreg.FailurePolicyType(c.FailurePolicy) + } + return &failurePolicy +} + +// clientConfig returns the client config for a webhook. +func (c Config) clientConfig() admissionreg.WebhookClientConfig { + path := c.Path + return admissionreg.WebhookClientConfig{ + Service: &admissionreg.ServiceReference{ + Name: "webhook-service", + Namespace: "system", + Path: &path, + }, + // OpenAPI marks the field as required before 1.13 because of a bug that got fixed in + // https://github.com/kubernetes/api/commit/e7d9121e9ffd63cea0288b36a82bcc87b073bd1b + // Put "\n" as an placeholder as a workaround til 1.13+ is almost everywhere. + CABundle: []byte("\n"), + } +} + +// +controllertools:marker:generateHelp + +// Generator generates (partial) {Mutating,Validating}WebhookConfiguration objects. +type Generator struct{} + +func (Generator) RegisterMarkers(into *markers.Registry) error { + if err := into.Register(ConfigDefinition); err != nil { + return err + } + into.AddHelp(ConfigDefinition, Config{}.Help()) + return nil +} + +func (Generator) Generate(ctx *genall.GenerationContext) error { + var mutatingCfgs []admissionreg.MutatingWebhook + var validatingCfgs []admissionreg.ValidatingWebhook + for _, root := range ctx.Roots { + markerSet, err := markers.PackageMarkers(ctx.Collector, root) + if err != nil { + root.AddError(err) + } + + for _, cfg := range markerSet[ConfigDefinition.Name] { + cfg := cfg.(Config) + if cfg.Mutating { + w, _ := cfg.ToMutatingWebhook() + mutatingCfgs = append(mutatingCfgs, w) + } else { + w, _ := cfg.ToValidatingWebhook() + validatingCfgs = append(validatingCfgs, w) + } + } + } + + var objs []interface{} + if len(mutatingCfgs) > 0 { + objs = append(objs, &admissionreg.MutatingWebhookConfiguration{ + TypeMeta: metav1.TypeMeta{ + Kind: "MutatingWebhookConfiguration", + APIVersion: admissionreg.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "mutating-webhook-configuration", + }, + Webhooks: mutatingCfgs, + }) + } + + if len(validatingCfgs) > 0 { + objs = append(objs, &admissionreg.ValidatingWebhookConfiguration{ + TypeMeta: metav1.TypeMeta{ + Kind: "ValidatingWebhookConfiguration", + APIVersion: admissionreg.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "validating-webhook-configuration", + }, + Webhooks: validatingCfgs, + }) + + } + + if err := ctx.WriteYAML("manifests.yaml", objs...); err != nil { + return err + } + + return nil +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/webhook/zz_generated.markerhelp.go b/vendor/sigs.k8s.io/controller-tools/pkg/webhook/zz_generated.markerhelp.go new file mode 100644 index 0000000000..81d9437f40 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/webhook/zz_generated.markerhelp.go @@ -0,0 +1,80 @@ +// +build !ignore_autogenerated + +/* +Copyright2019 The Kubernetes 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. +*/ + +// Code generated by helpgen. DO NOT EDIT. + +package webhook + +import ( + "sigs.k8s.io/controller-tools/pkg/markers" +) + +func (Config) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "Webhook", + DetailedHelp: markers.DetailedHelp{ + Summary: "specifies how a webhook should be served. ", + Details: "It specifies only the details that are intrinsic to the application serving it (e.g. the resources it can handle, or the path it serves on).", + }, + FieldHelp: map[string]markers.DetailedHelp{ + "Mutating": markers.DetailedHelp{ + Summary: "marks this as a mutating webhook (it's validating only if false) ", + Details: "Mutating webhooks are allowed to change the object in their response, and are called *before* all validating webhooks. Mutating webhooks may choose to reject an object, similarly to a validating webhook.", + }, + "FailurePolicy": markers.DetailedHelp{ + Summary: "specifies what should happen if the API server cannot reach the webhook. ", + Details: "It may be either \"ignore\" (to skip the webhook and continue on) or \"fail\" (to reject the object in question).", + }, + "Groups": markers.DetailedHelp{ + Summary: "specifies the API groups that this webhook receives requests for.", + Details: "", + }, + "Resources": markers.DetailedHelp{ + Summary: "specifies the API resources that this webhook receives requests for.", + Details: "", + }, + "Verbs": markers.DetailedHelp{ + Summary: "specifies the Kubernetes API verbs that this webhook receives requests for. ", + Details: "Only modification-like verbs may be specified. May be \"create\", \"update\", \"delete\", \"connect\", or \"*\" (for all).", + }, + "Versions": markers.DetailedHelp{ + Summary: "specifies the API versions that this webhook receives requests for.", + Details: "", + }, + "Name": markers.DetailedHelp{ + Summary: "indicates the name of this webhook configuration.", + Details: "", + }, + "Path": markers.DetailedHelp{ + Summary: "specifies that path that the API server should connect to this webhook on.", + Details: "", + }, + }, + } +} + +func (Generator) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "", + DetailedHelp: markers.DetailedHelp{ + Summary: "generates (partial) {Mutating,Validating}WebhookConfiguration objects.", + Details: "", + }, + FieldHelp: map[string]markers.DetailedHelp{}, + } +} From bc1f231dc6c954974656a68755941a81031f22c3 Mon Sep 17 00:00:00 2001 From: awgreene Date: Tue, 3 Dec 2019 17:01:26 -0500 Subject: [PATCH 3/4] Update manifest CRDs This commit updates the CRDs defined in the manifest dir to include a Structural Schema. --- ...0_50_olm_03-clusterserviceversion.crd.yaml | 8706 +++++++++++++++- .../0000_50_olm_04-installplan.crd.yaml | 172 +- .../0000_50_olm_05-subscription.crd.yaml | 1628 +-- .../0000_50_olm_06-catalogsource.crd.yaml | 126 +- .../0000_50_olm_10-operatorgroup.crd.yaml | 184 +- ...0_50_olm_03-clusterserviceversion.crd.yaml | 8707 ++++++++++++++++- manifests/0000_50_olm_04-installplan.crd.yaml | 173 +- .../0000_50_olm_05-subscription.crd.yaml | 1629 +-- .../0000_50_olm_06-catalogsource.crd.yaml | 127 +- .../0000_50_olm_10-operatorgroup.crd.yaml | 185 +- 10 files changed, 19032 insertions(+), 2605 deletions(-) diff --git a/deploy/chart/templates/0000_50_olm_03-clusterserviceversion.crd.yaml b/deploy/chart/templates/0000_50_olm_03-clusterserviceversion.crd.yaml index 613893753f..a1b8553b20 100644 --- a/deploy/chart/templates/0000_50_olm_03-clusterserviceversion.crd.yaml +++ b/deploy/chart/templates/0000_50_olm_03-clusterserviceversion.crd.yaml @@ -4,7 +4,8 @@ metadata: name: clusterserviceversions.operators.coreos.com annotations: displayName: Operator Version - description: Represents an Operator that should be running on the cluster, including requirements and install strategy. + description: Represents an Operator that should be running on the cluster, including + requirements and install strategy. spec: names: plural: clusterserviceversions @@ -35,732 +36,8397 @@ spec: group: operators.coreos.com version: v1alpha1 versions: - - name: v1alpha1 - served: true - storage: true + - name: v1alpha1 + served: true + storage: true scope: Namespaced subresources: - # status enables the status subresource. status: {} validation: openAPIV3Schema: - description: Represents an Operator that should be running on the cluster, including requirements and install strategy. + description: ClusterServiceVersion is a Custom Resource of type `ClusterServiceVersionSpec`. + type: object + required: + - metadata + - spec properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object spec: + description: ClusterServiceVersionSpec declarations tell OLM how to install + an operator that can manage apps for a given version. type: object - description: Spec for a ClusterServiceVersion required: - displayName - install properties: - displayName: - type: string - description: Human readable name of the application that will be displayed in the ALM UI - - description: - type: string - description: Human readable description of what the application does - - minKubeVersion: - type: string - description: Minimum kubernetes version requirement on the server to deploy operator - pattern: ^\bv?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(-(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*)?(\+[0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*)?$ - - keywords: - type: array - description: List of keywords which will be used to discover and categorize app types - items: - type: string - - maintainers: - type: array - description: Those responsible for the creation of this specific app type - items: - type: object - description: Information for a single maintainer - required: - - name - - email - properties: - name: - type: string - description: Maintainer's name - email: - type: string - description: Maintainer's email address - format: email - optionalProperties: - type: string - description: "Any additional key-value metadata you wish to expose about the maintainer, e.g. github: " - - links: - type: array - description: Interesting links to find more information about the project, such as marketing page, documentation, or github page - items: - type: object - description: A single link to describe one aspect of the project - required: - - name - - url - properties: - name: - type: string - description: Name of the link type, e.g. homepage or github url - url: - type: string - description: URL to which the link should point - format: uri - - icon: - type: array - description: Icon which should be rendered with the application information - items: - type: object - required: - - base64data - - mediatype - properties: - base64data: - type: string - description: Base64 binary representation of the icon image - mediatype: - type: string - description: Mediatype for the binary data specified in the base64data property - enum: - - image/gif - - image/jpeg - - image/png - - image/svg+xml - version: - type: string - description: Version string, recommended that users use semantic versioning - pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(-(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*)?(\+[0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*)?$ - - replaces: - type: string - description: Name of the ClusterServiceVersion custom resource that this version replaces - - maturity: - type: string - description: What level of maturity the software has achieved at this version - enum: - - planning - - pre-alpha - - alpha - - beta - - stable - - mature - - inactive - - deprecated - labels: - type: object - description: Labels that will be applied to associated resources created by the operator. - selector: + annotations: + description: Annotations is an unstructured key value map stored with + a resource that may be set by external tools to store and retrieve + arbitrary metadata. type: object - description: Label selector to find resources associated with or managed by the operator - properties: - matchLabels: - type: object - description: Label key:value pairs to match directly - matchExpressions: - type: array - description: A set of expressions to match against the resource. - items: - allOf: - - type: object - required: - - key - - operator - - values - properties: - key: - type: string - description: the key to match - operator: - type: string - description: the operator for the expression - enum: - - In - - NotIn - - Exists - - DoesNotExist - values: - type: array - description: set of values for the expression - nativeAPIs: - type: array - description: What resources are required by the Operator, but must be provided by the underlying cluster and not as an extension. - items: - type: object - required: - - group - - version - - kind - properties: - group: - type: string - description: Group of the API resource - version: - type: string - description: Version of the API resource - kind: - type: string - description: Kind of the API resource + additionalProperties: + type: string apiservicedefinitions: + description: APIServiceDefinitions declares all of the extension apis + managed or required by an operator being ran by ClusterServiceVersion. type: object properties: owned: type: array - description: What resources this operator is responsible for managing. No two running operators should manage the same resource. items: + description: APIServiceDescription provides details to OLM about + apis provided via aggregation type: object required: - group - - version - kind - name - - deploymentName - - displayName - - description + - version properties: - group: - type: string - description: Group of the APIService (e.g. app.coreos.com) - name: + actionDescriptors: + type: array + items: + description: ActionDescriptor describes a declarative action + that can be performed on a custom resource instance + type: object + required: + - path + properties: + description: + type: string + displayName: + type: string + path: + type: string + value: + description: RawMessage is a raw encoded JSON value. + It implements Marshaler and Unmarshaler and can be + used to delay JSON decoding or precompute a JSON encoding. + type: string + format: byte + x-descriptors: + type: array + items: + type: string + containerPort: + type: integer + format: int32 + deploymentName: type: string - description: The plural name for the APIService provided - version: + description: type: string - description: The version field of the APIService - kind: + displayName: type: string - description: The kind field of the APIService - deploymentName: + group: type: string - description: Name of the extension api-server's deployment - containerPort: - type: number - description: Port where the extension api-server serves TLS traffic - displayName: + kind: type: string - description: A human-readable name for the APIService. - description: + name: type: string - description: A description of the APIService resources: type: array items: + description: APIResourceReference is a Kubernetes resource + type used by a custom resource type: object - description: A list of resources that should be displayed for the APIService required: - kind + - name - version properties: + kind: + type: string name: type: string - description: If a APIService, the fully qualified name of the APIService (e.g. my-resource-v1.app.coreos.com) version: type: string - description: The version of the resource kind - kind: - type: string - description: The kind field of the resource kind - statusDescriptors: + specDescriptors: type: array items: + description: SpecDescriptor describes a field in a spec + block of a CRD so that OLM can consume it type: object - description: A spec for a field in the status block of the API resource required: - path - - displayName - - description properties: - path: + description: type: string - description: A jsonpath indexing into the status object on the API resource where the the status value can be found. displayName: type: string - description: A human-readable name for the status entry. - description: - type: string - description: A description of the status entry. - x-descriptors: - type: array - description: A list of descriptors for the status entry that indicate the meaning of the field. - items: - type: string - value: - description: If present, the value of this status is the same for all instances of the API resource and can be found here instead of on the API resource. - specDescriptors: - type: array - items: - type: object - description: A spec for a field in the spec block of the APIService resource. - required: - - path - - displayName - - description - properties: path: type: string - description: A jsonpath indexing into the spec object on the API resource where the the spec value can be found. - displayName: - type: string - description: A human-readable name for the spec entry. - description: + value: + description: RawMessage is a raw encoded JSON value. + It implements Marshaler and Unmarshaler and can be + used to delay JSON decoding or precompute a JSON encoding. type: string - description: A description of the spec entry. + format: byte x-descriptors: type: array - description: A list of descriptors for the spec entry that indicate the meaning of the field. items: type: string - value: - description: If present, the value of this spec is the same for all instances of the API Resource and can be found here instead of on the API resource. - actionDescriptors: + statusDescriptors: type: array items: + description: StatusDescriptor describes a field in a status + block of a CRD so that OLM can consume it type: object - description: A spec for actions that can be performed on instances of the API resource required: - path - - displayName - - description properties: - path: + description: type: string - description: A jsonpath indexing into the spec object on the API resource where the the spec value can be found. displayName: type: string - description: A human-readable name for the action. - description: + path: type: string - description: A description of the action. + value: + description: RawMessage is a raw encoded JSON value. + It implements Marshaler and Unmarshaler and can be + used to delay JSON decoding or precompute a JSON encoding. + type: string + format: byte x-descriptors: type: array - description: A list of descriptors for the action that indicate the meaning of the action. items: type: string - value: - description: If present, the value of this action is the same for all instances of the API resource and can be found here instead of on the API resource. + version: + type: string required: type: array - description: What resources this operator is responsible for managing. No two running operators should manage the same resource. items: + description: APIServiceDescription provides details to OLM about + apis provided via aggregation type: object required: - group - - version - kind - name - - displayName - - description + - version properties: - group: - type: string - description: Group of the APIService (e.g. app.coreos.com) - version: - type: string - description: The version field of the APIService - kind: - type: string - description: The kind field of the APIService - name: - type: string - description: The plural name for the APIService provided - deploymentName: - type: string - description: Name of the extension api-server's deployment - containerPort: - type: number - description: Port where the extension api-server serves TLS traffic - displayName: - type: string - description: A human-readable name for the APIService. - description: - type: string - description: A description of the APIService - statusDescriptors: + actionDescriptors: type: array items: + description: ActionDescriptor describes a declarative action + that can be performed on a custom resource instance type: object - description: A spec for a field in the status block of the APIService required: - path - - displayName - - description properties: - path: + description: type: string - description: A jsonpath indexing into the status object on the API Resource where the the status value can be found. displayName: type: string - description: A human-readable name for the status entry. - description: + path: type: string - description: A description of the status entry. + value: + description: RawMessage is a raw encoded JSON value. + It implements Marshaler and Unmarshaler and can be + used to delay JSON decoding or precompute a JSON encoding. + type: string + format: byte x-descriptors: type: array - description: A list of descriptors for the status entry that indicate the meaning of the field. items: type: string - value: - description: If present, the value of this status is the same for all instances of the API Resource and can be found here instead of on the API Resource. - - customresourcedefinitions: - type: object - properties: - owned: - type: array - description: What resources this operator is responsible for managing. No two running operators should manage the same resource. - items: - type: object - required: - - name - - version - - kind - - displayName - - description - properties: - name: - type: string - description: Fully qualified name of the CustomResourceDefinition (e.g. my-resource-v1.app.coreos.com) - version: + containerPort: + type: integer + format: int32 + deploymentName: type: string - description: The version field of the CustomResourceDefinition - kind: + description: type: string - description: The kind field of the CustomResourceDefinition displayName: type: string - description: A human-readable name for the CRD. - description: + group: + type: string + kind: + type: string + name: type: string - description: A description of the CRD resources: type: array items: + description: APIResourceReference is a Kubernetes resource + type used by a custom resource type: object - description: A list of resources that should be displayed for the CRD required: - - kind - - version + - kind + - name + - version properties: + kind: + type: string name: type: string - description: If a CRD, the fully qualified name of the CustomResourceDefinition (e.g. my-resource-v1.app.coreos.com) version: type: string - description: The version of the resource kind - kind: - type: string - description: The kind field of the resource kind - statusDescriptors: + specDescriptors: type: array items: + description: SpecDescriptor describes a field in a spec + block of a CRD so that OLM can consume it type: object - description: A spec for a field in the status block of the CRD required: - - path - - displayName - - description + - path properties: - path: + description: type: string - description: A jsonpath indexing into the status object on the CR where the the status value can be found. displayName: type: string - description: A human-readable name for the status entry. - description: + path: type: string - description: A description of the status entry. + value: + description: RawMessage is a raw encoded JSON value. + It implements Marshaler and Unmarshaler and can be + used to delay JSON decoding or precompute a JSON encoding. + type: string + format: byte x-descriptors: type: array - description: A list of descriptors for the status entry that indicate the meaning of the field. items: type: string - value: - description: If present, the value of this status is the same for all instances of the CRD and can be found here instead of on the CR. - specDescriptors: + statusDescriptors: type: array items: + description: StatusDescriptor describes a field in a status + block of a CRD so that OLM can consume it type: object - description: A spec for a field in the spec block of the CRD required: - - path - - displayName - - description + - path properties: - path: + description: type: string - description: A jsonpath indexing into the spec object on the CR where the the spec value can be found. displayName: type: string - description: A human-readable name for the spec entry. - description: + path: + type: string + value: + description: RawMessage is a raw encoded JSON value. + It implements Marshaler and Unmarshaler and can be + used to delay JSON decoding or precompute a JSON encoding. type: string - description: A description of the spec entry. + format: byte x-descriptors: type: array - description: A list of descriptors for the spec entry that indicate the meaning of the field. items: type: string - value: - description: If present, the value of this spec is the same for all instances of the CRD and can be found here instead of on the CR. + version: + type: string + customresourcedefinitions: + description: "CustomResourceDefinitions declares all of the CRDs managed + or required by an operator being ran by ClusterServiceVersion. \n + If the CRD is present in the Owned list, it is implicitly required." + type: object + properties: + owned: + type: array + items: + description: CRDDescription provides details to OLM about the + CRDs + type: object + required: + - kind + - name + - version + properties: actionDescriptors: type: array items: + description: ActionDescriptor describes a declarative action + that can be performed on a custom resource instance type: object - description: A spec for actions that can be performed on instances of the CRD required: - - path - - displayName - - description + - path properties: - path: + description: type: string - description: A jsonpath indexing into the spec object on the CR where the the spec value can be found. displayName: type: string - description: A human-readable name for the action. - description: + path: type: string - description: A description of the action. + value: + description: RawMessage is a raw encoded JSON value. + It implements Marshaler and Unmarshaler and can be + used to delay JSON decoding or precompute a JSON encoding. + type: string + format: byte x-descriptors: type: array - description: A list of descriptors for the action that indicate the meaning of the action. items: type: string - value: - description: If present, the value of this action is the same for all instances of the CRD and can be found here instead of on the CR. - required: - type: array - description: What resources this operator is responsible for managing. No two running operators should manage the same resource. - items: - type: object - required: - - name - - version - - kind - - displayName - - description - properties: - name: + description: type: string - description: Fully qualified name of the CustomResourceDefinition (e.g. my-resource-v1.app.coreos.com) - version: + displayName: type: string - description: The version field of the CustomResourceDefinition kind: type: string - description: The kind field of the CustomResourceDefinition - displayName: - type: string - description: A human-readable name for the CRD. - description: + name: type: string - description: A description of the CRD - statusDescriptors: + resources: + type: array + items: + description: APIResourceReference is a Kubernetes resource + type used by a custom resource + type: object + required: + - kind + - name + - version + properties: + kind: + type: string + name: + type: string + version: + type: string + specDescriptors: type: array items: + description: SpecDescriptor describes a field in a spec + block of a CRD so that OLM can consume it type: object - description: A spec for a field in the status block of the CRD required: - - path - - displayName - - description + - path properties: - path: + description: type: string - description: A jsonpath indexing into the status object on the CR where the the status value can be found. displayName: type: string - description: A human-readable name for the status entry. - description: + path: + type: string + value: + description: RawMessage is a raw encoded JSON value. + It implements Marshaler and Unmarshaler and can be + used to delay JSON decoding or precompute a JSON encoding. type: string - description: A description of the status entry. + format: byte x-descriptors: type: array - description: A list of descriptors for the status entry that indicate the meaning of the field. items: type: string + statusDescriptors: + type: array + items: + description: StatusDescriptor describes a field in a status + block of a CRD so that OLM can consume it + type: object + required: + - path + properties: + description: + type: string + displayName: + type: string + path: + type: string value: - description: If present, the value of this status is the same for all instances of the CRD and can be found here instead of on the CR. - - - install: - type: object - description: Information required to install this specific version of the operator software - oneOf: - - type: object - required: - - strategy - - spec - properties: - strategy: - type: string - enum: ['image'] - spec: - type: object - required: - - image - properties: - image: + description: RawMessage is a raw encoded JSON value. + It implements Marshaler and Unmarshaler and can be + used to delay JSON decoding or precompute a JSON encoding. + type: string + format: byte + x-descriptors: + type: array + items: + type: string + version: type: string - - type: object required: - - strategy - - spec - properties: - strategy: - type: string - enum: ['deployment'] - spec: + type: array + items: + description: CRDDescription provides details to OLM about the + CRDs type: object required: - - deployments + - kind + - name + - version properties: - installModes: + actionDescriptors: type: array - description: List of supported install modes for the operator items: + description: ActionDescriptor describes a declarative action + that can be performed on a custom resource instance type: object - description: A tuple representing a mode of installation and whether the operator supports it required: - - type - - supported + - path properties: - type: - type: string - description: A type of install mode - enum: - - OwnNamespace - - SingleNamespace - - MultiNamespace - - AllNamespaces - supported: - type: boolean - description: Represents if the install mode type is supported - deployments: + description: + type: string + displayName: + type: string + path: + type: string + value: + description: RawMessage is a raw encoded JSON value. + It implements Marshaler and Unmarshaler and can be + used to delay JSON decoding or precompute a JSON encoding. + type: string + format: byte + x-descriptors: + type: array + items: + type: string + description: + type: string + displayName: + type: string + kind: + type: string + name: + type: string + resources: type: array - description: List of deployments to create items: + description: APIResourceReference is a Kubernetes resource + type used by a custom resource type: object - description: A name and deployment to create in the cluster required: - - name - - spec + - kind + - name + - version properties: + kind: + type: string name: type: string - description: the consistent name of the deployment - spec: - type: object - description: The deployment spec to create in the cluster - permissions: + version: + type: string + specDescriptors: type: array - description: Permissions needed by the deployement to run correctly items: + description: SpecDescriptor describes a field in a spec + block of a CRD so that OLM can consume it type: object required: - - serviceAccountName - - rules + - path properties: - serviceAccountName: + description: type: string - description: The service account name to create for the deployment - rules: + displayName: + type: string + path: + type: string + value: + description: RawMessage is a raw encoded JSON value. + It implements Marshaler and Unmarshaler and can be + used to delay JSON decoding or precompute a JSON encoding. + type: string + format: byte + x-descriptors: type: array items: - type: object - description: a rule required by the service account - properties: - apiGroups: - type: array - description: apiGroups the rule applies to - items: - type: string - resources: - type: array - items: - type: string - resourceNames: - type: array - items: - type: string - verbs: - type: array - items: - type: string - enum: - - "*" - - assign - - get - - list - - watch - - create - - update - - patch - - delete - - deletecollection - - initialize - - use - clusterPermissions: + type: string + statusDescriptors: type: array - description: Cluster permissions needed by the deployement to run correctly items: + description: StatusDescriptor describes a field in a status + block of a CRD so that OLM can consume it type: object required: - - serviceAccountName - - rules + - path properties: - serviceAccountName: + description: + type: string + displayName: + type: string + path: + type: string + value: + description: RawMessage is a raw encoded JSON value. + It implements Marshaler and Unmarshaler and can be + used to delay JSON decoding or precompute a JSON encoding. type: string - description: The service account name to create for the deployment - rules: + format: byte + x-descriptors: type: array items: + type: string + version: + type: string + description: + type: string + displayName: + type: string + icon: + type: array + items: + type: object + required: + - base64data + - mediatype + properties: + base64data: + type: string + mediatype: + type: string + install: + description: NamedInstallStrategy represents the block of an ClusterServiceVersion + resource where the install strategy is specified. + type: object + required: + - strategy + properties: + spec: + description: StrategyDetailsDeployment represents the parsed details + of a Deployment InstallStrategy. + type: object + required: + - deployments + properties: + clusterPermissions: + type: array + items: + description: StrategyDeploymentPermissions describe the rbac + rules and service account needed by the install strategy + type: object + required: + - rules + - serviceAccountName + properties: + rules: + type: array + items: + description: PolicyRule holds information that describes + a policy rule, but does not contain information about + who the rule applies to or which namespace the rule + applies to. + type: object + required: + - verbs + properties: + apiGroups: + description: APIGroups is the name of the APIGroup + that contains the resources. If multiple API + groups are specified, any action requested against + one of the enumerated resources in any API group + will be allowed. + type: array + items: + type: string + nonResourceURLs: + description: NonResourceURLs is a set of partial + urls that a user should have access to. *s are + allowed, but only as the full, final step in the + path Since non-resource URLs are not namespaced, + this field is only applicable for ClusterRoles + referenced from a ClusterRoleBinding. Rules can + either apply to API resources (such as "pods" + or "secrets") or non-resource URL paths (such + as "/api"), but not both. + type: array + items: + type: string + resourceNames: + description: ResourceNames is an optional white + list of names that the rule applies to. An empty + set means that everything is allowed. + type: array + items: + type: string + resources: + description: Resources is a list of resources this + rule applies to. ResourceAll represents all resources. + type: array + items: + type: string + verbs: + description: Verbs is a list of Verbs that apply + to ALL the ResourceKinds and AttributeRestrictions + contained in this rule. VerbAll represents all + kinds. + type: array + items: + type: string + serviceAccountName: + type: string + deployments: + type: array + items: + description: StrategyDeploymentSpec contains the name and + spec for the deployment ALM should create + type: object + required: + - name + - spec + properties: + name: + type: string + spec: + description: DeploymentSpec is the specification of the + desired behavior of the Deployment. + type: object + required: + - selector + - template + properties: + minReadySeconds: + description: Minimum number of seconds for which a + newly created pod should be ready without any of + its container crashing, for it to be considered + available. Defaults to 0 (pod will be considered + available as soon as it is ready) + type: integer + format: int32 + paused: + description: Indicates that the deployment is paused. + type: boolean + progressDeadlineSeconds: + description: The maximum time in seconds for a deployment + to make progress before it is considered to be failed. + The deployment controller will continue to process + failed deployments and a condition with a ProgressDeadlineExceeded + reason will be surfaced in the deployment status. + Note that progress will not be estimated during + the time a deployment is paused. Defaults to 600s. + type: integer + format: int32 + replicas: + description: Number of desired pods. This is a pointer + to distinguish between explicit zero and not specified. + Defaults to 1. + type: integer + format: int32 + revisionHistoryLimit: + description: The number of old ReplicaSets to retain + to allow rollback. This is a pointer to distinguish + between explicit zero and not specified. Defaults + to 10. + type: integer + format: int32 + selector: + description: Label selector for pods. Existing ReplicaSets + whose pods are selected by this will be the ones + affected by this deployment. It must match the pod + template's labels. type: object - required: - - verbs - description: a rule required by the service account properties: - apiGroups: - type: array - description: apiGroups the rule applies to - items: - type: string - resources: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. type: array items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + type: object + required: + - key + - operator + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + type: array + items: + type: string + matchLabels: + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + additionalProperties: type: string - resourceNames: - type: array - items: - type: string - nonResourceURLs: - type: array - items: - type: string - verbs: - type: array - items: - type: string - enum: - - "*" - - assign - - get - - list - - watch - - create - - update - - patch - - put - - post - - delete - - deletecollection - - initialize - - use + strategy: + description: The deployment strategy to use to replace + existing pods with new ones. + type: object + properties: + rollingUpdate: + description: 'Rolling update config params. Present + only if DeploymentStrategyType = RollingUpdate. + --- TODO: Update this to follow our convention + for oneOf, whatever we decide it to be.' + type: object + properties: + maxSurge: + description: 'The maximum number of pods that + can be scheduled above the desired number + of pods. Value can be an absolute number + (ex: 5) or a percentage of desired pods + (ex: 10%). This can not be 0 if MaxUnavailable + is 0. Absolute number is calculated from + percentage by rounding up. Defaults to 25%. + Example: when this is set to 30%, the new + ReplicaSet can be scaled up immediately + when the rolling update starts, such that + the total number of old and new pods do + not exceed 130% of desired pods. Once old + pods have been killed, new ReplicaSet can + be scaled up further, ensuring that total + number of pods running at any time during + the update is at most 130% of desired pods.' + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + maxUnavailable: + description: 'The maximum number of pods that + can be unavailable during the update. Value + can be an absolute number (ex: 5) or a percentage + of desired pods (ex: 10%). Absolute number + is calculated from percentage by rounding + down. This can not be 0 if MaxSurge is 0. + Defaults to 25%. Example: when this is set + to 30%, the old ReplicaSet can be scaled + down to 70% of desired pods immediately + when the rolling update starts. Once new + pods are ready, old ReplicaSet can be scaled + down further, followed by scaling up the + new ReplicaSet, ensuring that the total + number of pods available at all times during + the update is at least 70% of desired pods.' + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + type: + description: Type of deployment. Can be "Recreate" + or "RollingUpdate". Default is RollingUpdate. + type: string + template: + description: Template describes the pods that will + be created. + type: object + properties: + metadata: + description: 'Standard object''s metadata. More + info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' + type: object + x-kubernetes-preserve-unknown-fields: true + spec: + description: 'Specification of the desired behavior + of the pod. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' + type: object + required: + - containers + properties: + activeDeadlineSeconds: + description: Optional duration in seconds + the pod may be active on the node relative + to StartTime before the system will actively + try to mark it failed and kill associated + containers. Value must be a positive integer. + type: integer + format: int64 + affinity: + description: If specified, the pod's scheduling + constraints + type: object + properties: + nodeAffinity: + description: Describes node affinity scheduling + rules for the pod. + type: object + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer + to schedule pods to nodes that satisfy + the affinity expressions specified + by this field, but it may choose + a node that violates one or more + of the expressions. The node that + is most preferred is the one with + the greatest sum of weights, i.e. + for each node that meets all of + the scheduling requirements (resource + request, requiredDuringScheduling + affinity expressions, etc.), compute + a sum by iterating through the elements + of this field and adding "weight" + to the sum if the node matches the + corresponding matchExpressions; + the node(s) with the highest sum + are the most preferred. + type: array + items: + description: An empty preferred + scheduling term matches all objects + with implicit weight 0 (i.e. it's + a no-op). A null preferred scheduling + term matches no objects (i.e. + is also a no-op). + type: object + required: + - preference + - weight + properties: + preference: + description: A node selector + term, associated with the + corresponding weight. + type: object + properties: + matchExpressions: + description: A list of node + selector requirements + by node's labels. + type: array + items: + description: A node selector + requirement is a selector + that contains values, + a key, and an operator + that relates the key + and values. + type: object + required: + - key + - operator + properties: + key: + description: The label + key that the selector + applies to. + type: string + operator: + description: Represents + a key's relationship + to a set of values. + Valid operators + are In, NotIn, Exists, + DoesNotExist. Gt, + and Lt. + type: string + values: + description: An array + of string values. + If the operator + is In or NotIn, + the values array + must be non-empty. + If the operator + is Exists or DoesNotExist, + the values array + must be empty. If + the operator is + Gt or Lt, the values + array must have + a single element, + which will be interpreted + as an integer. This + array is replaced + during a strategic + merge patch. + type: array + items: + type: string + matchFields: + description: A list of node + selector requirements + by node's fields. + type: array + items: + description: A node selector + requirement is a selector + that contains values, + a key, and an operator + that relates the key + and values. + type: object + required: + - key + - operator + properties: + key: + description: The label + key that the selector + applies to. + type: string + operator: + description: Represents + a key's relationship + to a set of values. + Valid operators + are In, NotIn, Exists, + DoesNotExist. Gt, + and Lt. + type: string + values: + description: An array + of string values. + If the operator + is In or NotIn, + the values array + must be non-empty. + If the operator + is Exists or DoesNotExist, + the values array + must be empty. If + the operator is + Gt or Lt, the values + array must have + a single element, + which will be interpreted + as an integer. This + array is replaced + during a strategic + merge patch. + type: array + items: + type: string + weight: + description: Weight associated + with matching the corresponding + nodeSelectorTerm, in the range + 1-100. + type: integer + format: int32 + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements + specified by this field are not + met at scheduling time, the pod + will not be scheduled onto the node. + If the affinity requirements specified + by this field cease to be met at + some point during pod execution + (e.g. due to an update), the system + may or may not try to eventually + evict the pod from its node. + type: object + required: + - nodeSelectorTerms + properties: + nodeSelectorTerms: + description: Required. A list + of node selector terms. The + terms are ORed. + type: array + items: + description: A null or empty + node selector term matches + no objects. The requirements + of them are ANDed. The TopologySelectorTerm + type implements a subset of + the NodeSelectorTerm. + type: object + properties: + matchExpressions: + description: A list of node + selector requirements + by node's labels. + type: array + items: + description: A node selector + requirement is a selector + that contains values, + a key, and an operator + that relates the key + and values. + type: object + required: + - key + - operator + properties: + key: + description: The label + key that the selector + applies to. + type: string + operator: + description: Represents + a key's relationship + to a set of values. + Valid operators + are In, NotIn, Exists, + DoesNotExist. Gt, + and Lt. + type: string + values: + description: An array + of string values. + If the operator + is In or NotIn, + the values array + must be non-empty. + If the operator + is Exists or DoesNotExist, + the values array + must be empty. If + the operator is + Gt or Lt, the values + array must have + a single element, + which will be interpreted + as an integer. This + array is replaced + during a strategic + merge patch. + type: array + items: + type: string + matchFields: + description: A list of node + selector requirements + by node's fields. + type: array + items: + description: A node selector + requirement is a selector + that contains values, + a key, and an operator + that relates the key + and values. + type: object + required: + - key + - operator + properties: + key: + description: The label + key that the selector + applies to. + type: string + operator: + description: Represents + a key's relationship + to a set of values. + Valid operators + are In, NotIn, Exists, + DoesNotExist. Gt, + and Lt. + type: string + values: + description: An array + of string values. + If the operator + is In or NotIn, + the values array + must be non-empty. + If the operator + is Exists or DoesNotExist, + the values array + must be empty. If + the operator is + Gt or Lt, the values + array must have + a single element, + which will be interpreted + as an integer. This + array is replaced + during a strategic + merge patch. + type: array + items: + type: string + podAffinity: + description: Describes pod affinity scheduling + rules (e.g. co-locate this pod in the + same node, zone, etc. as some other + pod(s)). + type: object + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer + to schedule pods to nodes that satisfy + the affinity expressions specified + by this field, but it may choose + a node that violates one or more + of the expressions. The node that + is most preferred is the one with + the greatest sum of weights, i.e. + for each node that meets all of + the scheduling requirements (resource + request, requiredDuringScheduling + affinity expressions, etc.), compute + a sum by iterating through the elements + of this field and adding "weight" + to the sum if the node has pods + which matches the corresponding + podAffinityTerm; the node(s) with + the highest sum are the most preferred. + type: array + items: + description: The weights of all + of the matched WeightedPodAffinityTerm + fields are added per-node to find + the most preferred node(s) + type: object + required: + - podAffinityTerm + - weight + properties: + podAffinityTerm: + description: Required. A pod + affinity term, associated + with the corresponding weight. + type: object + required: + - topologyKey + properties: + labelSelector: + description: A label query + over a set of resources, + in this case pods. + type: object + properties: + matchExpressions: + description: matchExpressions + is a list of label + selector requirements. + The requirements are + ANDed. + type: array + items: + description: A label + selector requirement + is a selector that + contains values, + a key, and an operator + that relates the + key and values. + type: object + required: + - key + - operator + properties: + key: + description: key + is the label + key that the + selector applies + to. + type: string + operator: + description: operator + represents a + key's relationship + to a set of + values. Valid + operators are + In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values + is an array + of string values. + If the operator + is In or NotIn, + the values array + must be non-empty. + If the operator + is Exists or + DoesNotExist, + the values array + must be empty. + This array is + replaced during + a strategic + merge patch. + type: array + items: + type: string + matchLabels: + description: matchLabels + is a map of {key,value} + pairs. A single {key,value} + in the matchLabels + map is equivalent + to an element of matchExpressions, + whose key field is + "key", the operator + is "In", and the values + array contains only + "value". The requirements + are ANDed. + type: object + additionalProperties: + type: string + namespaces: + description: namespaces + specifies which namespaces + the labelSelector applies + to (matches against); + null or empty list means + "this pod's namespace" + type: array + items: + type: string + topologyKey: + description: This pod should + be co-located (affinity) + or not co-located (anti-affinity) + with the pods matching + the labelSelector in the + specified namespaces, + where co-located is defined + as running on a node whose + value of the label with + key topologyKey matches + that of any node on which + any of the selected pods + is running. Empty topologyKey + is not allowed. + type: string + weight: + description: weight associated + with matching the corresponding + podAffinityTerm, in the range + 1-100. + type: integer + format: int32 + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements + specified by this field are not + met at scheduling time, the pod + will not be scheduled onto the node. + If the affinity requirements specified + by this field cease to be met at + some point during pod execution + (e.g. due to a pod label update), + the system may or may not try to + eventually evict the pod from its + node. When there are multiple elements, + the lists of nodes corresponding + to each podAffinityTerm are intersected, + i.e. all terms must be satisfied. + type: array + items: + description: Defines a set of pods + (namely those matching the labelSelector + relative to the given namespace(s)) + that this pod should be co-located + (affinity) or not co-located (anti-affinity) + with, where co-located is defined + as running on a node whose value + of the label with key + matches that of any node on which + a pod of the set of pods is running + type: object + required: + - topologyKey + properties: + labelSelector: + description: A label query over + a set of resources, in this + case pods. + type: object + properties: + matchExpressions: + description: matchExpressions + is a list of label selector + requirements. The requirements + are ANDed. + type: array + items: + description: A label selector + requirement is a selector + that contains values, + a key, and an operator + that relates the key + and values. + type: object + required: + - key + - operator + properties: + key: + description: key is + the label key that + the selector applies + to. + type: string + operator: + description: operator + represents a key's + relationship to + a set of values. + Valid operators + are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values + is an array of string + values. If the operator + is In or NotIn, + the values array + must be non-empty. + If the operator + is Exists or DoesNotExist, + the values array + must be empty. This + array is replaced + during a strategic + merge patch. + type: array + items: + type: string + matchLabels: + description: matchLabels + is a map of {key,value} + pairs. A single {key,value} + in the matchLabels map + is equivalent to an element + of matchExpressions, whose + key field is "key", the + operator is "In", and + the values array contains + only "value". The requirements + are ANDed. + type: object + additionalProperties: + type: string + namespaces: + description: namespaces specifies + which namespaces the labelSelector + applies to (matches against); + null or empty list means "this + pod's namespace" + type: array + items: + type: string + topologyKey: + description: This pod should + be co-located (affinity) or + not co-located (anti-affinity) + with the pods matching the + labelSelector in the specified + namespaces, where co-located + is defined as running on a + node whose value of the label + with key topologyKey matches + that of any node on which + any of the selected pods is + running. Empty topologyKey + is not allowed. + type: string + podAntiAffinity: + description: Describes pod anti-affinity + scheduling rules (e.g. avoid putting + this pod in the same node, zone, etc. + as some other pod(s)). + type: object + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer + to schedule pods to nodes that satisfy + the anti-affinity expressions specified + by this field, but it may choose + a node that violates one or more + of the expressions. The node that + is most preferred is the one with + the greatest sum of weights, i.e. + for each node that meets all of + the scheduling requirements (resource + request, requiredDuringScheduling + anti-affinity expressions, etc.), + compute a sum by iterating through + the elements of this field and adding + "weight" to the sum if the node + has pods which matches the corresponding + podAffinityTerm; the node(s) with + the highest sum are the most preferred. + type: array + items: + description: The weights of all + of the matched WeightedPodAffinityTerm + fields are added per-node to find + the most preferred node(s) + type: object + required: + - podAffinityTerm + - weight + properties: + podAffinityTerm: + description: Required. A pod + affinity term, associated + with the corresponding weight. + type: object + required: + - topologyKey + properties: + labelSelector: + description: A label query + over a set of resources, + in this case pods. + type: object + properties: + matchExpressions: + description: matchExpressions + is a list of label + selector requirements. + The requirements are + ANDed. + type: array + items: + description: A label + selector requirement + is a selector that + contains values, + a key, and an operator + that relates the + key and values. + type: object + required: + - key + - operator + properties: + key: + description: key + is the label + key that the + selector applies + to. + type: string + operator: + description: operator + represents a + key's relationship + to a set of + values. Valid + operators are + In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values + is an array + of string values. + If the operator + is In or NotIn, + the values array + must be non-empty. + If the operator + is Exists or + DoesNotExist, + the values array + must be empty. + This array is + replaced during + a strategic + merge patch. + type: array + items: + type: string + matchLabels: + description: matchLabels + is a map of {key,value} + pairs. A single {key,value} + in the matchLabels + map is equivalent + to an element of matchExpressions, + whose key field is + "key", the operator + is "In", and the values + array contains only + "value". The requirements + are ANDed. + type: object + additionalProperties: + type: string + namespaces: + description: namespaces + specifies which namespaces + the labelSelector applies + to (matches against); + null or empty list means + "this pod's namespace" + type: array + items: + type: string + topologyKey: + description: This pod should + be co-located (affinity) + or not co-located (anti-affinity) + with the pods matching + the labelSelector in the + specified namespaces, + where co-located is defined + as running on a node whose + value of the label with + key topologyKey matches + that of any node on which + any of the selected pods + is running. Empty topologyKey + is not allowed. + type: string + weight: + description: weight associated + with matching the corresponding + podAffinityTerm, in the range + 1-100. + type: integer + format: int32 + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity + requirements specified by this field + are not met at scheduling time, + the pod will not be scheduled onto + the node. If the anti-affinity requirements + specified by this field cease to + be met at some point during pod + execution (e.g. due to a pod label + update), the system may or may not + try to eventually evict the pod + from its node. When there are multiple + elements, the lists of nodes corresponding + to each podAffinityTerm are intersected, + i.e. all terms must be satisfied. + type: array + items: + description: Defines a set of pods + (namely those matching the labelSelector + relative to the given namespace(s)) + that this pod should be co-located + (affinity) or not co-located (anti-affinity) + with, where co-located is defined + as running on a node whose value + of the label with key + matches that of any node on which + a pod of the set of pods is running + type: object + required: + - topologyKey + properties: + labelSelector: + description: A label query over + a set of resources, in this + case pods. + type: object + properties: + matchExpressions: + description: matchExpressions + is a list of label selector + requirements. The requirements + are ANDed. + type: array + items: + description: A label selector + requirement is a selector + that contains values, + a key, and an operator + that relates the key + and values. + type: object + required: + - key + - operator + properties: + key: + description: key is + the label key that + the selector applies + to. + type: string + operator: + description: operator + represents a key's + relationship to + a set of values. + Valid operators + are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values + is an array of string + values. If the operator + is In or NotIn, + the values array + must be non-empty. + If the operator + is Exists or DoesNotExist, + the values array + must be empty. This + array is replaced + during a strategic + merge patch. + type: array + items: + type: string + matchLabels: + description: matchLabels + is a map of {key,value} + pairs. A single {key,value} + in the matchLabels map + is equivalent to an element + of matchExpressions, whose + key field is "key", the + operator is "In", and + the values array contains + only "value". The requirements + are ANDed. + type: object + additionalProperties: + type: string + namespaces: + description: namespaces specifies + which namespaces the labelSelector + applies to (matches against); + null or empty list means "this + pod's namespace" + type: array + items: + type: string + topologyKey: + description: This pod should + be co-located (affinity) or + not co-located (anti-affinity) + with the pods matching the + labelSelector in the specified + namespaces, where co-located + is defined as running on a + node whose value of the label + with key topologyKey matches + that of any node on which + any of the selected pods is + running. Empty topologyKey + is not allowed. + type: string + automountServiceAccountToken: + description: AutomountServiceAccountToken + indicates whether a service account token + should be automatically mounted. + type: boolean + containers: + description: List of containers belonging + to the pod. Containers cannot currently + be added or removed. There must be at least + one container in a Pod. Cannot be updated. + type: array + items: + description: A single application container + that you want to run within a pod. + type: object + required: + - name + properties: + args: + description: 'Arguments to the entrypoint. + The docker image''s CMD is used if + this is not provided. Variable references + $(VAR_NAME) are expanded using the + container''s environment. If a variable + cannot be resolved, the reference + in the input string will be unchanged. + The $(VAR_NAME) syntax can be escaped + with a double $$, ie: $$(VAR_NAME). + Escaped references will never be expanded, + regardless of whether the variable + exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + type: array + items: + type: string + command: + description: 'Entrypoint array. Not + executed within a shell. The docker + image''s ENTRYPOINT is used if this + is not provided. Variable references + $(VAR_NAME) are expanded using the + container''s environment. If a variable + cannot be resolved, the reference + in the input string will be unchanged. + The $(VAR_NAME) syntax can be escaped + with a double $$, ie: $$(VAR_NAME). + Escaped references will never be expanded, + regardless of whether the variable + exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + type: array + items: + type: string + env: + description: List of environment variables + to set in the container. Cannot be + updated. + type: array + items: + description: EnvVar represents an + environment variable present in + a Container. + type: object + required: + - name + properties: + name: + description: Name of the environment + variable. Must be a C_IDENTIFIER. + type: string + value: + description: 'Variable references + $(VAR_NAME) are expanded using + the previous defined environment + variables in the container and + any service environment variables. + If a variable cannot be resolved, + the reference in the input string + will be unchanged. The $(VAR_NAME) + syntax can be escaped with a + double $$, ie: $$(VAR_NAME). + Escaped references will never + be expanded, regardless of whether + the variable exists or not. + Defaults to "".' + type: string + valueFrom: + description: Source for the environment + variable's value. Cannot be + used if value is not empty. + type: object + properties: + configMapKeyRef: + description: Selects a key + of a ConfigMap. + type: object + required: + - key + properties: + key: + description: The key to + select. + type: string + name: + description: 'Name of + the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful + fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether + the ConfigMap or its + key must be defined + type: boolean + fieldRef: + description: 'Selects a field + of the pod: supports metadata.name, + metadata.namespace, metadata.labels, + metadata.annotations, spec.nodeName, + spec.serviceAccountName, + status.hostIP, status.podIP.' + type: object + required: + - fieldPath + properties: + apiVersion: + description: Version of + the schema the FieldPath + is written in terms + of, defaults to "v1". + type: string + fieldPath: + description: Path of the + field to select in the + specified API version. + type: string + resourceFieldRef: + description: 'Selects a resource + of the container: only resources + limits and requests (limits.cpu, + limits.memory, limits.ephemeral-storage, + requests.cpu, requests.memory + and requests.ephemeral-storage) + are currently supported.' + type: object + required: + - resource + properties: + containerName: + description: 'Container + name: required for volumes, + optional for env vars' + type: string + divisor: + description: Specifies + the output format of + the exposed resources, + defaults to "1" + type: string + resource: + description: 'Required: + resource to select' + type: string + secretKeyRef: + description: Selects a key + of a secret in the pod's + namespace + type: object + required: + - key + properties: + key: + description: The key of + the secret to select + from. Must be a valid + secret key. + type: string + name: + description: 'Name of + the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful + fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether + the Secret or its key + must be defined + type: boolean + envFrom: + description: List of sources to populate + environment variables in the container. + The keys defined within a source must + be a C_IDENTIFIER. All invalid keys + will be reported as an event when + the container is starting. When a + key exists in multiple sources, the + value associated with the last source + will take precedence. Values defined + by an Env with a duplicate key will + take precedence. Cannot be updated. + type: array + items: + description: EnvFromSource represents + the source of a set of ConfigMaps + type: object + properties: + configMapRef: + description: The ConfigMap to + select from + type: object + properties: + name: + description: 'Name of the + referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + optional: + description: Specify whether + the ConfigMap must be defined + type: boolean + prefix: + description: An optional identifier + to prepend to each key in the + ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select + from + type: object + properties: + name: + description: 'Name of the + referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + optional: + description: Specify whether + the Secret must be defined + type: boolean + image: + description: 'Docker image name. More + info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher + level config management to default + or override container images in workload + controllers like Deployments and StatefulSets.' + type: string + imagePullPolicy: + description: 'Image pull policy. One + of Always, Never, IfNotPresent. Defaults + to Always if :latest tag is specified, + or IfNotPresent otherwise. Cannot + be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' + type: string + lifecycle: + description: Actions that the management + system should take in response to + container lifecycle events. Cannot + be updated. + type: object + properties: + postStart: + description: 'PostStart is called + immediately after a container + is created. If the handler fails, + the container is terminated and + restarted according to its restart + policy. Other management of the + container blocks until the hook + completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + type: object + properties: + exec: + description: One and only one + of the following should be + specified. Exec specifies + the action to take. + type: object + properties: + command: + description: Command is + the command line to execute + inside the container, + the working directory + for the command is root + ('/') in the container's + filesystem. The command + is simply exec'd, it is + not run inside a shell, + so traditional shell instructions + ('|', etc) won't work. + To use a shell, you need + to explicitly call out + to that shell. Exit status + of 0 is treated as live/healthy + and non-zero is unhealthy. + type: array + items: + type: string + httpGet: + description: HTTPGet specifies + the http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to + connect to, defaults to + the pod IP. You probably + want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers + to set in the request. + HTTP allows repeated headers. + type: array + items: + description: HTTPHeader + describes a custom header + to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + path: + description: Path to access + on the HTTP server. + type: string + port: + description: Name or number + of the port to access + on the container. Number + must be in the range 1 + to 65535. Name must be + an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use + for connecting to the + host. Defaults to HTTP. + type: string + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP + port. TCP hooks not yet supported + TODO: implement a realistic + TCP lifecycle hook' + type: object + required: + - port + properties: + host: + description: 'Optional: + Host name to connect to, + defaults to the pod IP.' + type: string + port: + description: Number or name + of the port to access + on the container. Number + must be in the range 1 + to 65535. Name must be + an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + preStop: + description: 'PreStop is called + immediately before a container + is terminated due to an API request + or management event such as liveness/startup + probe failure, preemption, resource + contention, etc. The handler is + not called if the container crashes + or exits. The reason for termination + is passed to the handler. The + Pod''s termination grace period + countdown begins before the PreStop + hooked is executed. Regardless + of the outcome of the handler, + the container will eventually + terminate within the Pod''s termination + grace period. Other management + of the container blocks until + the hook completes or until the + termination grace period is reached. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + type: object + properties: + exec: + description: One and only one + of the following should be + specified. Exec specifies + the action to take. + type: object + properties: + command: + description: Command is + the command line to execute + inside the container, + the working directory + for the command is root + ('/') in the container's + filesystem. The command + is simply exec'd, it is + not run inside a shell, + so traditional shell instructions + ('|', etc) won't work. + To use a shell, you need + to explicitly call out + to that shell. Exit status + of 0 is treated as live/healthy + and non-zero is unhealthy. + type: array + items: + type: string + httpGet: + description: HTTPGet specifies + the http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to + connect to, defaults to + the pod IP. You probably + want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers + to set in the request. + HTTP allows repeated headers. + type: array + items: + description: HTTPHeader + describes a custom header + to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + path: + description: Path to access + on the HTTP server. + type: string + port: + description: Name or number + of the port to access + on the container. Number + must be in the range 1 + to 65535. Name must be + an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use + for connecting to the + host. Defaults to HTTP. + type: string + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP + port. TCP hooks not yet supported + TODO: implement a realistic + TCP lifecycle hook' + type: object + required: + - port + properties: + host: + description: 'Optional: + Host name to connect to, + defaults to the pod IP.' + type: string + port: + description: Number or name + of the port to access + on the container. Number + must be in the range 1 + to 65535. Name must be + an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + livenessProbe: + description: 'Periodic probe of container + liveness. Container will be restarted + if the probe fails. Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: object + properties: + exec: + description: One and only one of + the following should be specified. + Exec specifies the action to take. + type: object + properties: + command: + description: Command is the + command line to execute inside + the container, the working + directory for the command is + root ('/') in the container's + filesystem. The command is + simply exec'd, it is not run + inside a shell, so traditional + shell instructions ('|', etc) + won't work. To use a shell, + you need to explicitly call + out to that shell. Exit status + of 0 is treated as live/healthy + and non-zero is unhealthy. + type: array + items: + type: string + failureThreshold: + description: Minimum consecutive + failures for the probe to be considered + failed after having succeeded. + Defaults to 3. Minimum value is + 1. + type: integer + format: int32 + httpGet: + description: HTTPGet specifies the + http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to connect + to, defaults to the pod IP. + You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers + to set in the request. HTTP + allows repeated headers. + type: array + items: + description: HTTPHeader describes + a custom header to be used + in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + path: + description: Path to access + on the HTTP server. + type: string + port: + description: Name or number + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for + connecting to the host. Defaults + to HTTP. + type: string + initialDelaySeconds: + description: 'Number of seconds + after the container has started + before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + periodSeconds: + description: How often (in seconds) + to perform the probe. Default + to 10 seconds. Minimum value is + 1. + type: integer + format: int32 + successThreshold: + description: Minimum consecutive + successes for the probe to be + considered successful after having + failed. Defaults to 1. Must be + 1 for liveness and startup. Minimum + value is 1. + type: integer + format: int32 + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP port. + TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle + hook' + type: object + required: + - port + properties: + host: + description: 'Optional: Host + name to connect to, defaults + to the pod IP.' + type: string + port: + description: Number or name + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: 'Number of seconds + after which the probe times out. + Defaults to 1 second. Minimum + value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + name: + description: Name of the container specified + as a DNS_LABEL. Each container in + a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: List of ports to expose + from the container. Exposing a port + here gives the system additional information + about the network connections a container + uses, but is primarily informational. + Not specifying a port here DOES NOT + prevent that port from being exposed. + Any port which is listening on the + default "0.0.0.0" address inside a + container will be accessible from + the network. Cannot be updated. + type: array + items: + description: ContainerPort represents + a network port in a single container. + type: object + required: + - containerPort + properties: + containerPort: + description: Number of port to + expose on the pod's IP address. + This must be a valid port number, + 0 < x < 65536. + type: integer + format: int32 + hostIP: + description: What host IP to bind + the external port to. + type: string + hostPort: + description: Number of port to + expose on the host. If specified, + this must be a valid port number, + 0 < x < 65536. If HostNetwork + is specified, this must match + ContainerPort. Most containers + do not need this. + type: integer + format: int32 + name: + description: If specified, this + must be an IANA_SVC_NAME and + unique within the pod. Each + named port in a pod must have + a unique name. Name for the + port that can be referred to + by services. + type: string + protocol: + description: Protocol for port. + Must be UDP, TCP, or SCTP. Defaults + to "TCP". + type: string + readinessProbe: + description: 'Periodic probe of container + service readiness. Container will + be removed from service endpoints + if the probe fails. Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: object + properties: + exec: + description: One and only one of + the following should be specified. + Exec specifies the action to take. + type: object + properties: + command: + description: Command is the + command line to execute inside + the container, the working + directory for the command is + root ('/') in the container's + filesystem. The command is + simply exec'd, it is not run + inside a shell, so traditional + shell instructions ('|', etc) + won't work. To use a shell, + you need to explicitly call + out to that shell. Exit status + of 0 is treated as live/healthy + and non-zero is unhealthy. + type: array + items: + type: string + failureThreshold: + description: Minimum consecutive + failures for the probe to be considered + failed after having succeeded. + Defaults to 3. Minimum value is + 1. + type: integer + format: int32 + httpGet: + description: HTTPGet specifies the + http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to connect + to, defaults to the pod IP. + You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers + to set in the request. HTTP + allows repeated headers. + type: array + items: + description: HTTPHeader describes + a custom header to be used + in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + path: + description: Path to access + on the HTTP server. + type: string + port: + description: Name or number + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for + connecting to the host. Defaults + to HTTP. + type: string + initialDelaySeconds: + description: 'Number of seconds + after the container has started + before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + periodSeconds: + description: How often (in seconds) + to perform the probe. Default + to 10 seconds. Minimum value is + 1. + type: integer + format: int32 + successThreshold: + description: Minimum consecutive + successes for the probe to be + considered successful after having + failed. Defaults to 1. Must be + 1 for liveness and startup. Minimum + value is 1. + type: integer + format: int32 + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP port. + TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle + hook' + type: object + required: + - port + properties: + host: + description: 'Optional: Host + name to connect to, defaults + to the pod IP.' + type: string + port: + description: Number or name + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: 'Number of seconds + after which the probe times out. + Defaults to 1 second. Minimum + value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + resources: + description: 'Compute Resources required + by this container. Cannot be updated. + More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + properties: + limits: + description: 'Limits describes the + maximum amount of compute resources + allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + additionalProperties: + type: string + requests: + description: 'Requests describes + the minimum amount of compute + resources required. If Requests + is omitted for a container, it + defaults to Limits if that is + explicitly specified, otherwise + to an implementation-defined value. + More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + additionalProperties: + type: string + securityContext: + description: 'Security options the pod + should run with. More info: https://kubernetes.io/docs/concepts/policy/security-context/ + More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' + type: object + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation + controls whether a process can + gain more privileges than its + parent process. This bool directly + controls if the no_new_privs flag + will be set on the container process. + AllowPrivilegeEscalation is true + always when the container is: + 1) run as Privileged 2) has CAP_SYS_ADMIN' + type: boolean + capabilities: + description: The capabilities to + add/drop when running containers. + Defaults to the default set of + capabilities granted by the container + runtime. + type: object + properties: + add: + description: Added capabilities + type: array + items: + description: Capability represent + POSIX capabilities type + type: string + drop: + description: Removed capabilities + type: array + items: + description: Capability represent + POSIX capabilities type + type: string + privileged: + description: Run container in privileged + mode. Processes in privileged + containers are essentially equivalent + to root on the host. Defaults + to false. + type: boolean + procMount: + description: procMount denotes the + type of proc mount to use for + the containers. The default is + DefaultProcMount which uses the + container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType + feature flag to be enabled. + type: string + readOnlyRootFilesystem: + description: Whether this container + has a read-only root filesystem. + Default is false. + type: boolean + runAsGroup: + description: The GID to run the + entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If + set in both SecurityContext and + PodSecurityContext, the value + specified in SecurityContext takes + precedence. + type: integer + format: int64 + runAsNonRoot: + description: Indicates that the + container must run as a non-root + user. If true, the Kubelet will + validate the image at runtime + to ensure that it does not run + as UID 0 (root) and fail to start + the container if it does. If unset + or false, no such validation will + be performed. May also be set + in PodSecurityContext. If set + in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext + takes precedence. + type: boolean + runAsUser: + description: The UID to run the + entrypoint of the container process. + Defaults to user specified in + image metadata if unspecified. + May also be set in PodSecurityContext. If + set in both SecurityContext and + PodSecurityContext, the value + specified in SecurityContext takes + precedence. + type: integer + format: int64 + seLinuxOptions: + description: The SELinux context + to be applied to the container. + If unspecified, the container + runtime will allocate a random + SELinux context for each container. May + also be set in PodSecurityContext. If + set in both SecurityContext and + PodSecurityContext, the value + specified in SecurityContext takes + precedence. + type: object + properties: + level: + description: Level is SELinux + level label that applies to + the container. + type: string + role: + description: Role is a SELinux + role label that applies to + the container. + type: string + type: + description: Type is a SELinux + type label that applies to + the container. + type: string + user: + description: User is a SELinux + user label that applies to + the container. + type: string + windowsOptions: + description: The Windows specific + settings applied to all containers. + If unspecified, the options from + the PodSecurityContext will be + used. If set in both SecurityContext + and PodSecurityContext, the value + specified in SecurityContext takes + precedence. + type: object + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec + is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the + GMSA credential spec named + by the GMSACredentialSpecName + field. This field is alpha-level + and is only honored by servers + that enable the WindowsGMSA + feature flag. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName + is the name of the GMSA credential + spec to use. This field is + alpha-level and is only honored + by servers that enable the + WindowsGMSA feature flag. + type: string + runAsUserName: + description: The UserName in + Windows to run the entrypoint + of the container process. + Defaults to the user specified + in image metadata if unspecified. + May also be set in PodSecurityContext. + If set in both SecurityContext + and PodSecurityContext, the + value specified in SecurityContext + takes precedence. This field + is alpha-level and it is only + honored by servers that enable + the WindowsRunAsUserName feature + flag. + type: string + startupProbe: + description: 'StartupProbe indicates + that the Pod has successfully initialized. + If specified, no other probes are + executed until this completes successfully. + If this probe fails, the Pod will + be restarted, just as if the livenessProbe + failed. This can be used to provide + different probe parameters at the + beginning of a Pod''s lifecycle, when + it might take a long time to load + data or warm a cache, than during + steady-state operation. This cannot + be updated. This is an alpha feature + enabled by the StartupProbe feature + flag. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: object + properties: + exec: + description: One and only one of + the following should be specified. + Exec specifies the action to take. + type: object + properties: + command: + description: Command is the + command line to execute inside + the container, the working + directory for the command is + root ('/') in the container's + filesystem. The command is + simply exec'd, it is not run + inside a shell, so traditional + shell instructions ('|', etc) + won't work. To use a shell, + you need to explicitly call + out to that shell. Exit status + of 0 is treated as live/healthy + and non-zero is unhealthy. + type: array + items: + type: string + failureThreshold: + description: Minimum consecutive + failures for the probe to be considered + failed after having succeeded. + Defaults to 3. Minimum value is + 1. + type: integer + format: int32 + httpGet: + description: HTTPGet specifies the + http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to connect + to, defaults to the pod IP. + You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers + to set in the request. HTTP + allows repeated headers. + type: array + items: + description: HTTPHeader describes + a custom header to be used + in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + path: + description: Path to access + on the HTTP server. + type: string + port: + description: Name or number + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for + connecting to the host. Defaults + to HTTP. + type: string + initialDelaySeconds: + description: 'Number of seconds + after the container has started + before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + periodSeconds: + description: How often (in seconds) + to perform the probe. Default + to 10 seconds. Minimum value is + 1. + type: integer + format: int32 + successThreshold: + description: Minimum consecutive + successes for the probe to be + considered successful after having + failed. Defaults to 1. Must be + 1 for liveness and startup. Minimum + value is 1. + type: integer + format: int32 + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP port. + TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle + hook' + type: object + required: + - port + properties: + host: + description: 'Optional: Host + name to connect to, defaults + to the pod IP.' + type: string + port: + description: Number or name + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: 'Number of seconds + after which the probe times out. + Defaults to 1 second. Minimum + value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + stdin: + description: Whether this container + should allocate a buffer for stdin + in the container runtime. If this + is not set, reads from stdin in the + container will always result in EOF. + Default is false. + type: boolean + stdinOnce: + description: Whether the container runtime + should close the stdin channel after + it has been opened by a single attach. + When stdin is true the stdin stream + will remain open across multiple attach + sessions. If stdinOnce is set to true, + stdin is opened on container start, + is empty until the first client attaches + to stdin, and then remains open and + accepts data until the client disconnects, + at which time stdin is closed and + remains closed until the container + is restarted. If this flag is false, + a container processes that reads from + stdin will never receive an EOF. Default + is false + type: boolean + terminationMessagePath: + description: 'Optional: Path at which + the file to which the container''s + termination message will be written + is mounted into the container''s filesystem. + Message written is intended to be + brief final status, such as an assertion + failure message. Will be truncated + by the node if greater than 4096 bytes. + The total message length across all + containers will be limited to 12kb. + Defaults to /dev/termination-log. + Cannot be updated.' + type: string + terminationMessagePolicy: + description: Indicate how the termination + message should be populated. File + will use the contents of terminationMessagePath + to populate the container status message + on both success and failure. FallbackToLogsOnError + will use the last chunk of container + log output if the termination message + file is empty and the container exited + with an error. The log output is limited + to 2048 bytes or 80 lines, whichever + is smaller. Defaults to File. Cannot + be updated. + type: string + tty: + description: Whether this container + should allocate a TTY for itself, + also requires 'stdin' to be true. + Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list + of block devices to be used by the + container. This is a beta feature. + type: array + items: + description: volumeDevice describes + a mapping of a raw block device + within a container. + type: object + required: + - devicePath + - name + properties: + devicePath: + description: devicePath is the + path inside of the container + that the device will be mapped + to. + type: string + name: + description: name must match the + name of a persistentVolumeClaim + in the pod + type: string + volumeMounts: + description: Pod volumes to mount into + the container's filesystem. Cannot + be updated. + type: array + items: + description: VolumeMount describes + a mounting of a Volume within a + container. + type: object + required: + - mountPath + - name + properties: + mountPath: + description: Path within the container + at which the volume should be + mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation + determines how mounts are propagated + from the host to container and + the other way around. When not + set, MountPropagationNone is + used. This field is beta in + 1.10. + type: string + name: + description: This must match the + Name of a Volume. + type: string + readOnly: + description: Mounted read-only + if true, read-write otherwise + (false or unspecified). Defaults + to false. + type: boolean + subPath: + description: Path within the volume + from which the container's volume + should be mounted. Defaults + to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within + the volume from which the container's + volume should be mounted. Behaves + similarly to SubPath but environment + variable references $(VAR_NAME) + are expanded using the container's + environment. Defaults to "" + (volume's root). SubPathExpr + and SubPath are mutually exclusive. + This field is beta in 1.15. + type: string + workingDir: + description: Container's working directory. + If not specified, the container runtime's + default will be used, which might + be configured in the container image. + Cannot be updated. + type: string + dnsConfig: + description: Specifies the DNS parameters + of a pod. Parameters specified here will + be merged to the generated DNS configuration + based on DNSPolicy. + type: object + properties: + nameservers: + description: A list of DNS name server + IP addresses. This will be appended + to the base nameservers generated from + DNSPolicy. Duplicated nameservers will + be removed. + type: array + items: + type: string + options: + description: A list of DNS resolver options. + This will be merged with the base options + generated from DNSPolicy. Duplicated + entries will be removed. Resolution + options given in Options will override + those that appear in the base DNSPolicy. + type: array + items: + description: PodDNSConfigOption defines + DNS resolver options of a pod. + type: object + properties: + name: + description: Required. + type: string + value: + type: string + searches: + description: A list of DNS search domains + for host-name lookup. This will be appended + to the base search paths generated from + DNSPolicy. Duplicated search paths will + be removed. + type: array + items: + type: string + dnsPolicy: + description: Set DNS policy for the pod. Defaults + to "ClusterFirst". Valid values are 'ClusterFirstWithHostNet', + 'ClusterFirst', 'Default' or 'None'. DNS + parameters given in DNSConfig will be merged + with the policy selected with DNSPolicy. + To have DNS options set along with hostNetwork, + you have to specify DNS policy explicitly + to 'ClusterFirstWithHostNet'. + type: string + enableServiceLinks: + description: 'EnableServiceLinks indicates + whether information about services should + be injected into pod''s environment variables, + matching the syntax of Docker links. Optional: + Defaults to true.' + type: boolean + ephemeralContainers: + description: List of ephemeral containers + run in this pod. Ephemeral containers may + be run in an existing pod to perform user-initiated + actions such as debugging. This list cannot + be specified when creating a pod, and it + cannot be modified by updating the pod spec. + In order to add an ephemeral container to + an existing pod, use the pod's ephemeralcontainers + subresource. This field is alpha-level and + is only honored by servers that enable the + EphemeralContainers feature. + type: array + items: + description: An EphemeralContainer is a + container that may be added temporarily + to an existing pod for user-initiated + activities such as debugging. Ephemeral + containers have no resource or scheduling + guarantees, and they will not be restarted + when they exit or when a pod is removed + or restarted. If an ephemeral container + causes a pod to exceed its resource allocation, + the pod may be evicted. Ephemeral containers + may not be added by directly updating + the pod spec. They must be added via the + pod's ephemeralcontainers subresource, + and they will appear in the pod spec once + added. This is an alpha feature enabled + by the EphemeralContainers feature flag. + type: object + required: + - name + properties: + args: + description: 'Arguments to the entrypoint. + The docker image''s CMD is used if + this is not provided. Variable references + $(VAR_NAME) are expanded using the + container''s environment. If a variable + cannot be resolved, the reference + in the input string will be unchanged. + The $(VAR_NAME) syntax can be escaped + with a double $$, ie: $$(VAR_NAME). + Escaped references will never be expanded, + regardless of whether the variable + exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + type: array + items: + type: string + command: + description: 'Entrypoint array. Not + executed within a shell. The docker + image''s ENTRYPOINT is used if this + is not provided. Variable references + $(VAR_NAME) are expanded using the + container''s environment. If a variable + cannot be resolved, the reference + in the input string will be unchanged. + The $(VAR_NAME) syntax can be escaped + with a double $$, ie: $$(VAR_NAME). + Escaped references will never be expanded, + regardless of whether the variable + exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + type: array + items: + type: string + env: + description: List of environment variables + to set in the container. Cannot be + updated. + type: array + items: + description: EnvVar represents an + environment variable present in + a Container. + type: object + required: + - name + properties: + name: + description: Name of the environment + variable. Must be a C_IDENTIFIER. + type: string + value: + description: 'Variable references + $(VAR_NAME) are expanded using + the previous defined environment + variables in the container and + any service environment variables. + If a variable cannot be resolved, + the reference in the input string + will be unchanged. The $(VAR_NAME) + syntax can be escaped with a + double $$, ie: $$(VAR_NAME). + Escaped references will never + be expanded, regardless of whether + the variable exists or not. + Defaults to "".' + type: string + valueFrom: + description: Source for the environment + variable's value. Cannot be + used if value is not empty. + type: object + properties: + configMapKeyRef: + description: Selects a key + of a ConfigMap. + type: object + required: + - key + properties: + key: + description: The key to + select. + type: string + name: + description: 'Name of + the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful + fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether + the ConfigMap or its + key must be defined + type: boolean + fieldRef: + description: 'Selects a field + of the pod: supports metadata.name, + metadata.namespace, metadata.labels, + metadata.annotations, spec.nodeName, + spec.serviceAccountName, + status.hostIP, status.podIP.' + type: object + required: + - fieldPath + properties: + apiVersion: + description: Version of + the schema the FieldPath + is written in terms + of, defaults to "v1". + type: string + fieldPath: + description: Path of the + field to select in the + specified API version. + type: string + resourceFieldRef: + description: 'Selects a resource + of the container: only resources + limits and requests (limits.cpu, + limits.memory, limits.ephemeral-storage, + requests.cpu, requests.memory + and requests.ephemeral-storage) + are currently supported.' + type: object + required: + - resource + properties: + containerName: + description: 'Container + name: required for volumes, + optional for env vars' + type: string + divisor: + description: Specifies + the output format of + the exposed resources, + defaults to "1" + type: string + resource: + description: 'Required: + resource to select' + type: string + secretKeyRef: + description: Selects a key + of a secret in the pod's + namespace + type: object + required: + - key + properties: + key: + description: The key of + the secret to select + from. Must be a valid + secret key. + type: string + name: + description: 'Name of + the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful + fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether + the Secret or its key + must be defined + type: boolean + envFrom: + description: List of sources to populate + environment variables in the container. + The keys defined within a source must + be a C_IDENTIFIER. All invalid keys + will be reported as an event when + the container is starting. When a + key exists in multiple sources, the + value associated with the last source + will take precedence. Values defined + by an Env with a duplicate key will + take precedence. Cannot be updated. + type: array + items: + description: EnvFromSource represents + the source of a set of ConfigMaps + type: object + properties: + configMapRef: + description: The ConfigMap to + select from + type: object + properties: + name: + description: 'Name of the + referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + optional: + description: Specify whether + the ConfigMap must be defined + type: boolean + prefix: + description: An optional identifier + to prepend to each key in the + ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select + from + type: object + properties: + name: + description: 'Name of the + referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + optional: + description: Specify whether + the Secret must be defined + type: boolean + image: + description: 'Docker image name. More + info: https://kubernetes.io/docs/concepts/containers/images' + type: string + imagePullPolicy: + description: 'Image pull policy. One + of Always, Never, IfNotPresent. Defaults + to Always if :latest tag is specified, + or IfNotPresent otherwise. Cannot + be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' + type: string + lifecycle: + description: Lifecycle is not allowed + for ephemeral containers. + type: object + properties: + postStart: + description: 'PostStart is called + immediately after a container + is created. If the handler fails, + the container is terminated and + restarted according to its restart + policy. Other management of the + container blocks until the hook + completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + type: object + properties: + exec: + description: One and only one + of the following should be + specified. Exec specifies + the action to take. + type: object + properties: + command: + description: Command is + the command line to execute + inside the container, + the working directory + for the command is root + ('/') in the container's + filesystem. The command + is simply exec'd, it is + not run inside a shell, + so traditional shell instructions + ('|', etc) won't work. + To use a shell, you need + to explicitly call out + to that shell. Exit status + of 0 is treated as live/healthy + and non-zero is unhealthy. + type: array + items: + type: string + httpGet: + description: HTTPGet specifies + the http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to + connect to, defaults to + the pod IP. You probably + want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers + to set in the request. + HTTP allows repeated headers. + type: array + items: + description: HTTPHeader + describes a custom header + to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + path: + description: Path to access + on the HTTP server. + type: string + port: + description: Name or number + of the port to access + on the container. Number + must be in the range 1 + to 65535. Name must be + an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use + for connecting to the + host. Defaults to HTTP. + type: string + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP + port. TCP hooks not yet supported + TODO: implement a realistic + TCP lifecycle hook' + type: object + required: + - port + properties: + host: + description: 'Optional: + Host name to connect to, + defaults to the pod IP.' + type: string + port: + description: Number or name + of the port to access + on the container. Number + must be in the range 1 + to 65535. Name must be + an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + preStop: + description: 'PreStop is called + immediately before a container + is terminated due to an API request + or management event such as liveness/startup + probe failure, preemption, resource + contention, etc. The handler is + not called if the container crashes + or exits. The reason for termination + is passed to the handler. The + Pod''s termination grace period + countdown begins before the PreStop + hooked is executed. Regardless + of the outcome of the handler, + the container will eventually + terminate within the Pod''s termination + grace period. Other management + of the container blocks until + the hook completes or until the + termination grace period is reached. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + type: object + properties: + exec: + description: One and only one + of the following should be + specified. Exec specifies + the action to take. + type: object + properties: + command: + description: Command is + the command line to execute + inside the container, + the working directory + for the command is root + ('/') in the container's + filesystem. The command + is simply exec'd, it is + not run inside a shell, + so traditional shell instructions + ('|', etc) won't work. + To use a shell, you need + to explicitly call out + to that shell. Exit status + of 0 is treated as live/healthy + and non-zero is unhealthy. + type: array + items: + type: string + httpGet: + description: HTTPGet specifies + the http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to + connect to, defaults to + the pod IP. You probably + want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers + to set in the request. + HTTP allows repeated headers. + type: array + items: + description: HTTPHeader + describes a custom header + to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + path: + description: Path to access + on the HTTP server. + type: string + port: + description: Name or number + of the port to access + on the container. Number + must be in the range 1 + to 65535. Name must be + an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use + for connecting to the + host. Defaults to HTTP. + type: string + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP + port. TCP hooks not yet supported + TODO: implement a realistic + TCP lifecycle hook' + type: object + required: + - port + properties: + host: + description: 'Optional: + Host name to connect to, + defaults to the pod IP.' + type: string + port: + description: Number or name + of the port to access + on the container. Number + must be in the range 1 + to 65535. Name must be + an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + livenessProbe: + description: Probes are not allowed + for ephemeral containers. + type: object + properties: + exec: + description: One and only one of + the following should be specified. + Exec specifies the action to take. + type: object + properties: + command: + description: Command is the + command line to execute inside + the container, the working + directory for the command is + root ('/') in the container's + filesystem. The command is + simply exec'd, it is not run + inside a shell, so traditional + shell instructions ('|', etc) + won't work. To use a shell, + you need to explicitly call + out to that shell. Exit status + of 0 is treated as live/healthy + and non-zero is unhealthy. + type: array + items: + type: string + failureThreshold: + description: Minimum consecutive + failures for the probe to be considered + failed after having succeeded. + Defaults to 3. Minimum value is + 1. + type: integer + format: int32 + httpGet: + description: HTTPGet specifies the + http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to connect + to, defaults to the pod IP. + You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers + to set in the request. HTTP + allows repeated headers. + type: array + items: + description: HTTPHeader describes + a custom header to be used + in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + path: + description: Path to access + on the HTTP server. + type: string + port: + description: Name or number + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for + connecting to the host. Defaults + to HTTP. + type: string + initialDelaySeconds: + description: 'Number of seconds + after the container has started + before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + periodSeconds: + description: How often (in seconds) + to perform the probe. Default + to 10 seconds. Minimum value is + 1. + type: integer + format: int32 + successThreshold: + description: Minimum consecutive + successes for the probe to be + considered successful after having + failed. Defaults to 1. Must be + 1 for liveness and startup. Minimum + value is 1. + type: integer + format: int32 + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP port. + TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle + hook' + type: object + required: + - port + properties: + host: + description: 'Optional: Host + name to connect to, defaults + to the pod IP.' + type: string + port: + description: Number or name + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: 'Number of seconds + after which the probe times out. + Defaults to 1 second. Minimum + value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + name: + description: Name of the ephemeral container + specified as a DNS_LABEL. This name + must be unique among all containers, + init containers and ephemeral containers. + type: string + ports: + description: Ports are not allowed for + ephemeral containers. + type: array + items: + description: ContainerPort represents + a network port in a single container. + type: object + required: + - containerPort + properties: + containerPort: + description: Number of port to + expose on the pod's IP address. + This must be a valid port number, + 0 < x < 65536. + type: integer + format: int32 + hostIP: + description: What host IP to bind + the external port to. + type: string + hostPort: + description: Number of port to + expose on the host. If specified, + this must be a valid port number, + 0 < x < 65536. If HostNetwork + is specified, this must match + ContainerPort. Most containers + do not need this. + type: integer + format: int32 + name: + description: If specified, this + must be an IANA_SVC_NAME and + unique within the pod. Each + named port in a pod must have + a unique name. Name for the + port that can be referred to + by services. + type: string + protocol: + description: Protocol for port. + Must be UDP, TCP, or SCTP. Defaults + to "TCP". + type: string + readinessProbe: + description: Probes are not allowed + for ephemeral containers. + type: object + properties: + exec: + description: One and only one of + the following should be specified. + Exec specifies the action to take. + type: object + properties: + command: + description: Command is the + command line to execute inside + the container, the working + directory for the command is + root ('/') in the container's + filesystem. The command is + simply exec'd, it is not run + inside a shell, so traditional + shell instructions ('|', etc) + won't work. To use a shell, + you need to explicitly call + out to that shell. Exit status + of 0 is treated as live/healthy + and non-zero is unhealthy. + type: array + items: + type: string + failureThreshold: + description: Minimum consecutive + failures for the probe to be considered + failed after having succeeded. + Defaults to 3. Minimum value is + 1. + type: integer + format: int32 + httpGet: + description: HTTPGet specifies the + http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to connect + to, defaults to the pod IP. + You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers + to set in the request. HTTP + allows repeated headers. + type: array + items: + description: HTTPHeader describes + a custom header to be used + in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + path: + description: Path to access + on the HTTP server. + type: string + port: + description: Name or number + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for + connecting to the host. Defaults + to HTTP. + type: string + initialDelaySeconds: + description: 'Number of seconds + after the container has started + before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + periodSeconds: + description: How often (in seconds) + to perform the probe. Default + to 10 seconds. Minimum value is + 1. + type: integer + format: int32 + successThreshold: + description: Minimum consecutive + successes for the probe to be + considered successful after having + failed. Defaults to 1. Must be + 1 for liveness and startup. Minimum + value is 1. + type: integer + format: int32 + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP port. + TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle + hook' + type: object + required: + - port + properties: + host: + description: 'Optional: Host + name to connect to, defaults + to the pod IP.' + type: string + port: + description: Number or name + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: 'Number of seconds + after which the probe times out. + Defaults to 1 second. Minimum + value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + resources: + description: Resources are not allowed + for ephemeral containers. Ephemeral + containers use spare resources already + allocated to the pod. + type: object + properties: + limits: + description: 'Limits describes the + maximum amount of compute resources + allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + additionalProperties: + type: string + requests: + description: 'Requests describes + the minimum amount of compute + resources required. If Requests + is omitted for a container, it + defaults to Limits if that is + explicitly specified, otherwise + to an implementation-defined value. + More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + additionalProperties: + type: string + securityContext: + description: SecurityContext is not + allowed for ephemeral containers. + type: object + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation + controls whether a process can + gain more privileges than its + parent process. This bool directly + controls if the no_new_privs flag + will be set on the container process. + AllowPrivilegeEscalation is true + always when the container is: + 1) run as Privileged 2) has CAP_SYS_ADMIN' + type: boolean + capabilities: + description: The capabilities to + add/drop when running containers. + Defaults to the default set of + capabilities granted by the container + runtime. + type: object + properties: + add: + description: Added capabilities + type: array + items: + description: Capability represent + POSIX capabilities type + type: string + drop: + description: Removed capabilities + type: array + items: + description: Capability represent + POSIX capabilities type + type: string + privileged: + description: Run container in privileged + mode. Processes in privileged + containers are essentially equivalent + to root on the host. Defaults + to false. + type: boolean + procMount: + description: procMount denotes the + type of proc mount to use for + the containers. The default is + DefaultProcMount which uses the + container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType + feature flag to be enabled. + type: string + readOnlyRootFilesystem: + description: Whether this container + has a read-only root filesystem. + Default is false. + type: boolean + runAsGroup: + description: The GID to run the + entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If + set in both SecurityContext and + PodSecurityContext, the value + specified in SecurityContext takes + precedence. + type: integer + format: int64 + runAsNonRoot: + description: Indicates that the + container must run as a non-root + user. If true, the Kubelet will + validate the image at runtime + to ensure that it does not run + as UID 0 (root) and fail to start + the container if it does. If unset + or false, no such validation will + be performed. May also be set + in PodSecurityContext. If set + in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext + takes precedence. + type: boolean + runAsUser: + description: The UID to run the + entrypoint of the container process. + Defaults to user specified in + image metadata if unspecified. + May also be set in PodSecurityContext. If + set in both SecurityContext and + PodSecurityContext, the value + specified in SecurityContext takes + precedence. + type: integer + format: int64 + seLinuxOptions: + description: The SELinux context + to be applied to the container. + If unspecified, the container + runtime will allocate a random + SELinux context for each container. May + also be set in PodSecurityContext. If + set in both SecurityContext and + PodSecurityContext, the value + specified in SecurityContext takes + precedence. + type: object + properties: + level: + description: Level is SELinux + level label that applies to + the container. + type: string + role: + description: Role is a SELinux + role label that applies to + the container. + type: string + type: + description: Type is a SELinux + type label that applies to + the container. + type: string + user: + description: User is a SELinux + user label that applies to + the container. + type: string + windowsOptions: + description: The Windows specific + settings applied to all containers. + If unspecified, the options from + the PodSecurityContext will be + used. If set in both SecurityContext + and PodSecurityContext, the value + specified in SecurityContext takes + precedence. + type: object + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec + is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the + GMSA credential spec named + by the GMSACredentialSpecName + field. This field is alpha-level + and is only honored by servers + that enable the WindowsGMSA + feature flag. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName + is the name of the GMSA credential + spec to use. This field is + alpha-level and is only honored + by servers that enable the + WindowsGMSA feature flag. + type: string + runAsUserName: + description: The UserName in + Windows to run the entrypoint + of the container process. + Defaults to the user specified + in image metadata if unspecified. + May also be set in PodSecurityContext. + If set in both SecurityContext + and PodSecurityContext, the + value specified in SecurityContext + takes precedence. This field + is alpha-level and it is only + honored by servers that enable + the WindowsRunAsUserName feature + flag. + type: string + startupProbe: + description: Probes are not allowed + for ephemeral containers. + type: object + properties: + exec: + description: One and only one of + the following should be specified. + Exec specifies the action to take. + type: object + properties: + command: + description: Command is the + command line to execute inside + the container, the working + directory for the command is + root ('/') in the container's + filesystem. The command is + simply exec'd, it is not run + inside a shell, so traditional + shell instructions ('|', etc) + won't work. To use a shell, + you need to explicitly call + out to that shell. Exit status + of 0 is treated as live/healthy + and non-zero is unhealthy. + type: array + items: + type: string + failureThreshold: + description: Minimum consecutive + failures for the probe to be considered + failed after having succeeded. + Defaults to 3. Minimum value is + 1. + type: integer + format: int32 + httpGet: + description: HTTPGet specifies the + http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to connect + to, defaults to the pod IP. + You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers + to set in the request. HTTP + allows repeated headers. + type: array + items: + description: HTTPHeader describes + a custom header to be used + in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + path: + description: Path to access + on the HTTP server. + type: string + port: + description: Name or number + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for + connecting to the host. Defaults + to HTTP. + type: string + initialDelaySeconds: + description: 'Number of seconds + after the container has started + before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + periodSeconds: + description: How often (in seconds) + to perform the probe. Default + to 10 seconds. Minimum value is + 1. + type: integer + format: int32 + successThreshold: + description: Minimum consecutive + successes for the probe to be + considered successful after having + failed. Defaults to 1. Must be + 1 for liveness and startup. Minimum + value is 1. + type: integer + format: int32 + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP port. + TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle + hook' + type: object + required: + - port + properties: + host: + description: 'Optional: Host + name to connect to, defaults + to the pod IP.' + type: string + port: + description: Number or name + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: 'Number of seconds + after which the probe times out. + Defaults to 1 second. Minimum + value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + stdin: + description: Whether this container + should allocate a buffer for stdin + in the container runtime. If this + is not set, reads from stdin in the + container will always result in EOF. + Default is false. + type: boolean + stdinOnce: + description: Whether the container runtime + should close the stdin channel after + it has been opened by a single attach. + When stdin is true the stdin stream + will remain open across multiple attach + sessions. If stdinOnce is set to true, + stdin is opened on container start, + is empty until the first client attaches + to stdin, and then remains open and + accepts data until the client disconnects, + at which time stdin is closed and + remains closed until the container + is restarted. If this flag is false, + a container processes that reads from + stdin will never receive an EOF. Default + is false + type: boolean + targetContainerName: + description: If set, the name of the + container from PodSpec that this ephemeral + container targets. The ephemeral container + will be run in the namespaces (IPC, + PID, etc) of this container. If not + set then the ephemeral container is + run in whatever namespaces are shared + for the pod. Note that the container + runtime must support this feature. + type: string + terminationMessagePath: + description: 'Optional: Path at which + the file to which the container''s + termination message will be written + is mounted into the container''s filesystem. + Message written is intended to be + brief final status, such as an assertion + failure message. Will be truncated + by the node if greater than 4096 bytes. + The total message length across all + containers will be limited to 12kb. + Defaults to /dev/termination-log. + Cannot be updated.' + type: string + terminationMessagePolicy: + description: Indicate how the termination + message should be populated. File + will use the contents of terminationMessagePath + to populate the container status message + on both success and failure. FallbackToLogsOnError + will use the last chunk of container + log output if the termination message + file is empty and the container exited + with an error. The log output is limited + to 2048 bytes or 80 lines, whichever + is smaller. Defaults to File. Cannot + be updated. + type: string + tty: + description: Whether this container + should allocate a TTY for itself, + also requires 'stdin' to be true. + Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list + of block devices to be used by the + container. This is a beta feature. + type: array + items: + description: volumeDevice describes + a mapping of a raw block device + within a container. + type: object + required: + - devicePath + - name + properties: + devicePath: + description: devicePath is the + path inside of the container + that the device will be mapped + to. + type: string + name: + description: name must match the + name of a persistentVolumeClaim + in the pod + type: string + volumeMounts: + description: Pod volumes to mount into + the container's filesystem. Cannot + be updated. + type: array + items: + description: VolumeMount describes + a mounting of a Volume within a + container. + type: object + required: + - mountPath + - name + properties: + mountPath: + description: Path within the container + at which the volume should be + mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation + determines how mounts are propagated + from the host to container and + the other way around. When not + set, MountPropagationNone is + used. This field is beta in + 1.10. + type: string + name: + description: This must match the + Name of a Volume. + type: string + readOnly: + description: Mounted read-only + if true, read-write otherwise + (false or unspecified). Defaults + to false. + type: boolean + subPath: + description: Path within the volume + from which the container's volume + should be mounted. Defaults + to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within + the volume from which the container's + volume should be mounted. Behaves + similarly to SubPath but environment + variable references $(VAR_NAME) + are expanded using the container's + environment. Defaults to "" + (volume's root). SubPathExpr + and SubPath are mutually exclusive. + This field is beta in 1.15. + type: string + workingDir: + description: Container's working directory. + If not specified, the container runtime's + default will be used, which might + be configured in the container image. + Cannot be updated. + type: string + hostAliases: + description: HostAliases is an optional list + of hosts and IPs that will be injected into + the pod's hosts file if specified. This + is only valid for non-hostNetwork pods. + type: array + items: + description: HostAlias holds the mapping + between IP and hostnames that will be + injected as an entry in the pod's hosts + file. + type: object + properties: + hostnames: + description: Hostnames for the above + IP address. + type: array + items: + type: string + ip: + description: IP address of the host + file entry. + type: string + hostIPC: + description: 'Use the host''s ipc namespace. + Optional: Default to false.' + type: boolean + hostNetwork: + description: Host networking requested for + this pod. Use the host's network namespace. + If this option is set, the ports that will + be used must be specified. Default to false. + type: boolean + hostPID: + description: 'Use the host''s pid namespace. + Optional: Default to false.' + type: boolean + hostname: + description: Specifies the hostname of the + Pod If not specified, the pod's hostname + will be set to a system-defined value. + type: string + imagePullSecrets: + description: 'ImagePullSecrets is an optional + list of references to secrets in the same + namespace to use for pulling any of the + images used by this PodSpec. If specified, + these secrets will be passed to individual + puller implementations for them to use. + For example, in the case of docker, only + DockerConfig type secrets are honored. More + info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod' + type: array + items: + description: LocalObjectReference contains + enough information to let you locate the + referenced object inside the same namespace. + type: object + properties: + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + initContainers: + description: 'List of initialization containers + belonging to the pod. Init containers are + executed in order prior to containers being + started. If any init container fails, the + pod is considered to have failed and is + handled according to its restartPolicy. + The name for an init container or normal + container must be unique among all containers. + Init containers may not have Lifecycle actions, + Readiness probes, Liveness probes, or Startup + probes. The resourceRequirements of an init + container are taken into account during + scheduling by finding the highest request/limit + for each resource type, and then using the + max of of that value or the sum of the normal + containers. Limits are applied to init containers + in a similar fashion. Init containers cannot + currently be added or removed. Cannot be + updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/' + type: array + items: + description: A single application container + that you want to run within a pod. + type: object + required: + - name + properties: + args: + description: 'Arguments to the entrypoint. + The docker image''s CMD is used if + this is not provided. Variable references + $(VAR_NAME) are expanded using the + container''s environment. If a variable + cannot be resolved, the reference + in the input string will be unchanged. + The $(VAR_NAME) syntax can be escaped + with a double $$, ie: $$(VAR_NAME). + Escaped references will never be expanded, + regardless of whether the variable + exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + type: array + items: + type: string + command: + description: 'Entrypoint array. Not + executed within a shell. The docker + image''s ENTRYPOINT is used if this + is not provided. Variable references + $(VAR_NAME) are expanded using the + container''s environment. If a variable + cannot be resolved, the reference + in the input string will be unchanged. + The $(VAR_NAME) syntax can be escaped + with a double $$, ie: $$(VAR_NAME). + Escaped references will never be expanded, + regardless of whether the variable + exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + type: array + items: + type: string + env: + description: List of environment variables + to set in the container. Cannot be + updated. + type: array + items: + description: EnvVar represents an + environment variable present in + a Container. + type: object + required: + - name + properties: + name: + description: Name of the environment + variable. Must be a C_IDENTIFIER. + type: string + value: + description: 'Variable references + $(VAR_NAME) are expanded using + the previous defined environment + variables in the container and + any service environment variables. + If a variable cannot be resolved, + the reference in the input string + will be unchanged. The $(VAR_NAME) + syntax can be escaped with a + double $$, ie: $$(VAR_NAME). + Escaped references will never + be expanded, regardless of whether + the variable exists or not. + Defaults to "".' + type: string + valueFrom: + description: Source for the environment + variable's value. Cannot be + used if value is not empty. + type: object + properties: + configMapKeyRef: + description: Selects a key + of a ConfigMap. + type: object + required: + - key + properties: + key: + description: The key to + select. + type: string + name: + description: 'Name of + the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful + fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether + the ConfigMap or its + key must be defined + type: boolean + fieldRef: + description: 'Selects a field + of the pod: supports metadata.name, + metadata.namespace, metadata.labels, + metadata.annotations, spec.nodeName, + spec.serviceAccountName, + status.hostIP, status.podIP.' + type: object + required: + - fieldPath + properties: + apiVersion: + description: Version of + the schema the FieldPath + is written in terms + of, defaults to "v1". + type: string + fieldPath: + description: Path of the + field to select in the + specified API version. + type: string + resourceFieldRef: + description: 'Selects a resource + of the container: only resources + limits and requests (limits.cpu, + limits.memory, limits.ephemeral-storage, + requests.cpu, requests.memory + and requests.ephemeral-storage) + are currently supported.' + type: object + required: + - resource + properties: + containerName: + description: 'Container + name: required for volumes, + optional for env vars' + type: string + divisor: + description: Specifies + the output format of + the exposed resources, + defaults to "1" + type: string + resource: + description: 'Required: + resource to select' + type: string + secretKeyRef: + description: Selects a key + of a secret in the pod's + namespace + type: object + required: + - key + properties: + key: + description: The key of + the secret to select + from. Must be a valid + secret key. + type: string + name: + description: 'Name of + the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful + fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether + the Secret or its key + must be defined + type: boolean + envFrom: + description: List of sources to populate + environment variables in the container. + The keys defined within a source must + be a C_IDENTIFIER. All invalid keys + will be reported as an event when + the container is starting. When a + key exists in multiple sources, the + value associated with the last source + will take precedence. Values defined + by an Env with a duplicate key will + take precedence. Cannot be updated. + type: array + items: + description: EnvFromSource represents + the source of a set of ConfigMaps + type: object + properties: + configMapRef: + description: The ConfigMap to + select from + type: object + properties: + name: + description: 'Name of the + referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + optional: + description: Specify whether + the ConfigMap must be defined + type: boolean + prefix: + description: An optional identifier + to prepend to each key in the + ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select + from + type: object + properties: + name: + description: 'Name of the + referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + optional: + description: Specify whether + the Secret must be defined + type: boolean + image: + description: 'Docker image name. More + info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher + level config management to default + or override container images in workload + controllers like Deployments and StatefulSets.' + type: string + imagePullPolicy: + description: 'Image pull policy. One + of Always, Never, IfNotPresent. Defaults + to Always if :latest tag is specified, + or IfNotPresent otherwise. Cannot + be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' + type: string + lifecycle: + description: Actions that the management + system should take in response to + container lifecycle events. Cannot + be updated. + type: object + properties: + postStart: + description: 'PostStart is called + immediately after a container + is created. If the handler fails, + the container is terminated and + restarted according to its restart + policy. Other management of the + container blocks until the hook + completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + type: object + properties: + exec: + description: One and only one + of the following should be + specified. Exec specifies + the action to take. + type: object + properties: + command: + description: Command is + the command line to execute + inside the container, + the working directory + for the command is root + ('/') in the container's + filesystem. The command + is simply exec'd, it is + not run inside a shell, + so traditional shell instructions + ('|', etc) won't work. + To use a shell, you need + to explicitly call out + to that shell. Exit status + of 0 is treated as live/healthy + and non-zero is unhealthy. + type: array + items: + type: string + httpGet: + description: HTTPGet specifies + the http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to + connect to, defaults to + the pod IP. You probably + want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers + to set in the request. + HTTP allows repeated headers. + type: array + items: + description: HTTPHeader + describes a custom header + to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + path: + description: Path to access + on the HTTP server. + type: string + port: + description: Name or number + of the port to access + on the container. Number + must be in the range 1 + to 65535. Name must be + an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use + for connecting to the + host. Defaults to HTTP. + type: string + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP + port. TCP hooks not yet supported + TODO: implement a realistic + TCP lifecycle hook' + type: object + required: + - port + properties: + host: + description: 'Optional: + Host name to connect to, + defaults to the pod IP.' + type: string + port: + description: Number or name + of the port to access + on the container. Number + must be in the range 1 + to 65535. Name must be + an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + preStop: + description: 'PreStop is called + immediately before a container + is terminated due to an API request + or management event such as liveness/startup + probe failure, preemption, resource + contention, etc. The handler is + not called if the container crashes + or exits. The reason for termination + is passed to the handler. The + Pod''s termination grace period + countdown begins before the PreStop + hooked is executed. Regardless + of the outcome of the handler, + the container will eventually + terminate within the Pod''s termination + grace period. Other management + of the container blocks until + the hook completes or until the + termination grace period is reached. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + type: object + properties: + exec: + description: One and only one + of the following should be + specified. Exec specifies + the action to take. + type: object + properties: + command: + description: Command is + the command line to execute + inside the container, + the working directory + for the command is root + ('/') in the container's + filesystem. The command + is simply exec'd, it is + not run inside a shell, + so traditional shell instructions + ('|', etc) won't work. + To use a shell, you need + to explicitly call out + to that shell. Exit status + of 0 is treated as live/healthy + and non-zero is unhealthy. + type: array + items: + type: string + httpGet: + description: HTTPGet specifies + the http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to + connect to, defaults to + the pod IP. You probably + want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers + to set in the request. + HTTP allows repeated headers. + type: array + items: + description: HTTPHeader + describes a custom header + to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + path: + description: Path to access + on the HTTP server. + type: string + port: + description: Name or number + of the port to access + on the container. Number + must be in the range 1 + to 65535. Name must be + an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use + for connecting to the + host. Defaults to HTTP. + type: string + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP + port. TCP hooks not yet supported + TODO: implement a realistic + TCP lifecycle hook' + type: object + required: + - port + properties: + host: + description: 'Optional: + Host name to connect to, + defaults to the pod IP.' + type: string + port: + description: Number or name + of the port to access + on the container. Number + must be in the range 1 + to 65535. Name must be + an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + livenessProbe: + description: 'Periodic probe of container + liveness. Container will be restarted + if the probe fails. Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: object + properties: + exec: + description: One and only one of + the following should be specified. + Exec specifies the action to take. + type: object + properties: + command: + description: Command is the + command line to execute inside + the container, the working + directory for the command is + root ('/') in the container's + filesystem. The command is + simply exec'd, it is not run + inside a shell, so traditional + shell instructions ('|', etc) + won't work. To use a shell, + you need to explicitly call + out to that shell. Exit status + of 0 is treated as live/healthy + and non-zero is unhealthy. + type: array + items: + type: string + failureThreshold: + description: Minimum consecutive + failures for the probe to be considered + failed after having succeeded. + Defaults to 3. Minimum value is + 1. + type: integer + format: int32 + httpGet: + description: HTTPGet specifies the + http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to connect + to, defaults to the pod IP. + You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers + to set in the request. HTTP + allows repeated headers. + type: array + items: + description: HTTPHeader describes + a custom header to be used + in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + path: + description: Path to access + on the HTTP server. + type: string + port: + description: Name or number + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for + connecting to the host. Defaults + to HTTP. + type: string + initialDelaySeconds: + description: 'Number of seconds + after the container has started + before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + periodSeconds: + description: How often (in seconds) + to perform the probe. Default + to 10 seconds. Minimum value is + 1. + type: integer + format: int32 + successThreshold: + description: Minimum consecutive + successes for the probe to be + considered successful after having + failed. Defaults to 1. Must be + 1 for liveness and startup. Minimum + value is 1. + type: integer + format: int32 + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP port. + TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle + hook' + type: object + required: + - port + properties: + host: + description: 'Optional: Host + name to connect to, defaults + to the pod IP.' + type: string + port: + description: Number or name + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: 'Number of seconds + after which the probe times out. + Defaults to 1 second. Minimum + value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + name: + description: Name of the container specified + as a DNS_LABEL. Each container in + a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: List of ports to expose + from the container. Exposing a port + here gives the system additional information + about the network connections a container + uses, but is primarily informational. + Not specifying a port here DOES NOT + prevent that port from being exposed. + Any port which is listening on the + default "0.0.0.0" address inside a + container will be accessible from + the network. Cannot be updated. + type: array + items: + description: ContainerPort represents + a network port in a single container. + type: object + required: + - containerPort + properties: + containerPort: + description: Number of port to + expose on the pod's IP address. + This must be a valid port number, + 0 < x < 65536. + type: integer + format: int32 + hostIP: + description: What host IP to bind + the external port to. + type: string + hostPort: + description: Number of port to + expose on the host. If specified, + this must be a valid port number, + 0 < x < 65536. If HostNetwork + is specified, this must match + ContainerPort. Most containers + do not need this. + type: integer + format: int32 + name: + description: If specified, this + must be an IANA_SVC_NAME and + unique within the pod. Each + named port in a pod must have + a unique name. Name for the + port that can be referred to + by services. + type: string + protocol: + description: Protocol for port. + Must be UDP, TCP, or SCTP. Defaults + to "TCP". + type: string + readinessProbe: + description: 'Periodic probe of container + service readiness. Container will + be removed from service endpoints + if the probe fails. Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: object + properties: + exec: + description: One and only one of + the following should be specified. + Exec specifies the action to take. + type: object + properties: + command: + description: Command is the + command line to execute inside + the container, the working + directory for the command is + root ('/') in the container's + filesystem. The command is + simply exec'd, it is not run + inside a shell, so traditional + shell instructions ('|', etc) + won't work. To use a shell, + you need to explicitly call + out to that shell. Exit status + of 0 is treated as live/healthy + and non-zero is unhealthy. + type: array + items: + type: string + failureThreshold: + description: Minimum consecutive + failures for the probe to be considered + failed after having succeeded. + Defaults to 3. Minimum value is + 1. + type: integer + format: int32 + httpGet: + description: HTTPGet specifies the + http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to connect + to, defaults to the pod IP. + You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers + to set in the request. HTTP + allows repeated headers. + type: array + items: + description: HTTPHeader describes + a custom header to be used + in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + path: + description: Path to access + on the HTTP server. + type: string + port: + description: Name or number + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for + connecting to the host. Defaults + to HTTP. + type: string + initialDelaySeconds: + description: 'Number of seconds + after the container has started + before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + periodSeconds: + description: How often (in seconds) + to perform the probe. Default + to 10 seconds. Minimum value is + 1. + type: integer + format: int32 + successThreshold: + description: Minimum consecutive + successes for the probe to be + considered successful after having + failed. Defaults to 1. Must be + 1 for liveness and startup. Minimum + value is 1. + type: integer + format: int32 + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP port. + TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle + hook' + type: object + required: + - port + properties: + host: + description: 'Optional: Host + name to connect to, defaults + to the pod IP.' + type: string + port: + description: Number or name + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: 'Number of seconds + after which the probe times out. + Defaults to 1 second. Minimum + value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + resources: + description: 'Compute Resources required + by this container. Cannot be updated. + More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + properties: + limits: + description: 'Limits describes the + maximum amount of compute resources + allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + additionalProperties: + type: string + requests: + description: 'Requests describes + the minimum amount of compute + resources required. If Requests + is omitted for a container, it + defaults to Limits if that is + explicitly specified, otherwise + to an implementation-defined value. + More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + additionalProperties: + type: string + securityContext: + description: 'Security options the pod + should run with. More info: https://kubernetes.io/docs/concepts/policy/security-context/ + More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' + type: object + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation + controls whether a process can + gain more privileges than its + parent process. This bool directly + controls if the no_new_privs flag + will be set on the container process. + AllowPrivilegeEscalation is true + always when the container is: + 1) run as Privileged 2) has CAP_SYS_ADMIN' + type: boolean + capabilities: + description: The capabilities to + add/drop when running containers. + Defaults to the default set of + capabilities granted by the container + runtime. + type: object + properties: + add: + description: Added capabilities + type: array + items: + description: Capability represent + POSIX capabilities type + type: string + drop: + description: Removed capabilities + type: array + items: + description: Capability represent + POSIX capabilities type + type: string + privileged: + description: Run container in privileged + mode. Processes in privileged + containers are essentially equivalent + to root on the host. Defaults + to false. + type: boolean + procMount: + description: procMount denotes the + type of proc mount to use for + the containers. The default is + DefaultProcMount which uses the + container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType + feature flag to be enabled. + type: string + readOnlyRootFilesystem: + description: Whether this container + has a read-only root filesystem. + Default is false. + type: boolean + runAsGroup: + description: The GID to run the + entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If + set in both SecurityContext and + PodSecurityContext, the value + specified in SecurityContext takes + precedence. + type: integer + format: int64 + runAsNonRoot: + description: Indicates that the + container must run as a non-root + user. If true, the Kubelet will + validate the image at runtime + to ensure that it does not run + as UID 0 (root) and fail to start + the container if it does. If unset + or false, no such validation will + be performed. May also be set + in PodSecurityContext. If set + in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext + takes precedence. + type: boolean + runAsUser: + description: The UID to run the + entrypoint of the container process. + Defaults to user specified in + image metadata if unspecified. + May also be set in PodSecurityContext. If + set in both SecurityContext and + PodSecurityContext, the value + specified in SecurityContext takes + precedence. + type: integer + format: int64 + seLinuxOptions: + description: The SELinux context + to be applied to the container. + If unspecified, the container + runtime will allocate a random + SELinux context for each container. May + also be set in PodSecurityContext. If + set in both SecurityContext and + PodSecurityContext, the value + specified in SecurityContext takes + precedence. + type: object + properties: + level: + description: Level is SELinux + level label that applies to + the container. + type: string + role: + description: Role is a SELinux + role label that applies to + the container. + type: string + type: + description: Type is a SELinux + type label that applies to + the container. + type: string + user: + description: User is a SELinux + user label that applies to + the container. + type: string + windowsOptions: + description: The Windows specific + settings applied to all containers. + If unspecified, the options from + the PodSecurityContext will be + used. If set in both SecurityContext + and PodSecurityContext, the value + specified in SecurityContext takes + precedence. + type: object + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec + is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the + GMSA credential spec named + by the GMSACredentialSpecName + field. This field is alpha-level + and is only honored by servers + that enable the WindowsGMSA + feature flag. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName + is the name of the GMSA credential + spec to use. This field is + alpha-level and is only honored + by servers that enable the + WindowsGMSA feature flag. + type: string + runAsUserName: + description: The UserName in + Windows to run the entrypoint + of the container process. + Defaults to the user specified + in image metadata if unspecified. + May also be set in PodSecurityContext. + If set in both SecurityContext + and PodSecurityContext, the + value specified in SecurityContext + takes precedence. This field + is alpha-level and it is only + honored by servers that enable + the WindowsRunAsUserName feature + flag. + type: string + startupProbe: + description: 'StartupProbe indicates + that the Pod has successfully initialized. + If specified, no other probes are + executed until this completes successfully. + If this probe fails, the Pod will + be restarted, just as if the livenessProbe + failed. This can be used to provide + different probe parameters at the + beginning of a Pod''s lifecycle, when + it might take a long time to load + data or warm a cache, than during + steady-state operation. This cannot + be updated. This is an alpha feature + enabled by the StartupProbe feature + flag. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: object + properties: + exec: + description: One and only one of + the following should be specified. + Exec specifies the action to take. + type: object + properties: + command: + description: Command is the + command line to execute inside + the container, the working + directory for the command is + root ('/') in the container's + filesystem. The command is + simply exec'd, it is not run + inside a shell, so traditional + shell instructions ('|', etc) + won't work. To use a shell, + you need to explicitly call + out to that shell. Exit status + of 0 is treated as live/healthy + and non-zero is unhealthy. + type: array + items: + type: string + failureThreshold: + description: Minimum consecutive + failures for the probe to be considered + failed after having succeeded. + Defaults to 3. Minimum value is + 1. + type: integer + format: int32 + httpGet: + description: HTTPGet specifies the + http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to connect + to, defaults to the pod IP. + You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers + to set in the request. HTTP + allows repeated headers. + type: array + items: + description: HTTPHeader describes + a custom header to be used + in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + path: + description: Path to access + on the HTTP server. + type: string + port: + description: Name or number + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for + connecting to the host. Defaults + to HTTP. + type: string + initialDelaySeconds: + description: 'Number of seconds + after the container has started + before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + periodSeconds: + description: How often (in seconds) + to perform the probe. Default + to 10 seconds. Minimum value is + 1. + type: integer + format: int32 + successThreshold: + description: Minimum consecutive + successes for the probe to be + considered successful after having + failed. Defaults to 1. Must be + 1 for liveness and startup. Minimum + value is 1. + type: integer + format: int32 + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP port. + TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle + hook' + type: object + required: + - port + properties: + host: + description: 'Optional: Host + name to connect to, defaults + to the pod IP.' + type: string + port: + description: Number or name + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: 'Number of seconds + after which the probe times out. + Defaults to 1 second. Minimum + value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + stdin: + description: Whether this container + should allocate a buffer for stdin + in the container runtime. If this + is not set, reads from stdin in the + container will always result in EOF. + Default is false. + type: boolean + stdinOnce: + description: Whether the container runtime + should close the stdin channel after + it has been opened by a single attach. + When stdin is true the stdin stream + will remain open across multiple attach + sessions. If stdinOnce is set to true, + stdin is opened on container start, + is empty until the first client attaches + to stdin, and then remains open and + accepts data until the client disconnects, + at which time stdin is closed and + remains closed until the container + is restarted. If this flag is false, + a container processes that reads from + stdin will never receive an EOF. Default + is false + type: boolean + terminationMessagePath: + description: 'Optional: Path at which + the file to which the container''s + termination message will be written + is mounted into the container''s filesystem. + Message written is intended to be + brief final status, such as an assertion + failure message. Will be truncated + by the node if greater than 4096 bytes. + The total message length across all + containers will be limited to 12kb. + Defaults to /dev/termination-log. + Cannot be updated.' + type: string + terminationMessagePolicy: + description: Indicate how the termination + message should be populated. File + will use the contents of terminationMessagePath + to populate the container status message + on both success and failure. FallbackToLogsOnError + will use the last chunk of container + log output if the termination message + file is empty and the container exited + with an error. The log output is limited + to 2048 bytes or 80 lines, whichever + is smaller. Defaults to File. Cannot + be updated. + type: string + tty: + description: Whether this container + should allocate a TTY for itself, + also requires 'stdin' to be true. + Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list + of block devices to be used by the + container. This is a beta feature. + type: array + items: + description: volumeDevice describes + a mapping of a raw block device + within a container. + type: object + required: + - devicePath + - name + properties: + devicePath: + description: devicePath is the + path inside of the container + that the device will be mapped + to. + type: string + name: + description: name must match the + name of a persistentVolumeClaim + in the pod + type: string + volumeMounts: + description: Pod volumes to mount into + the container's filesystem. Cannot + be updated. + type: array + items: + description: VolumeMount describes + a mounting of a Volume within a + container. + type: object + required: + - mountPath + - name + properties: + mountPath: + description: Path within the container + at which the volume should be + mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation + determines how mounts are propagated + from the host to container and + the other way around. When not + set, MountPropagationNone is + used. This field is beta in + 1.10. + type: string + name: + description: This must match the + Name of a Volume. + type: string + readOnly: + description: Mounted read-only + if true, read-write otherwise + (false or unspecified). Defaults + to false. + type: boolean + subPath: + description: Path within the volume + from which the container's volume + should be mounted. Defaults + to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within + the volume from which the container's + volume should be mounted. Behaves + similarly to SubPath but environment + variable references $(VAR_NAME) + are expanded using the container's + environment. Defaults to "" + (volume's root). SubPathExpr + and SubPath are mutually exclusive. + This field is beta in 1.15. + type: string + workingDir: + description: Container's working directory. + If not specified, the container runtime's + default will be used, which might + be configured in the container image. + Cannot be updated. + type: string + nodeName: + description: NodeName is a request to schedule + this pod onto a specific node. If it is + non-empty, the scheduler simply schedules + this pod onto that node, assuming that it + fits resource requirements. + type: string + nodeSelector: + description: 'NodeSelector is a selector which + must be true for the pod to fit on a node. + Selector which must match a node''s labels + for the pod to be scheduled on that node. + More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/' + type: object + additionalProperties: + type: string + overhead: + description: 'Overhead represents the resource + overhead associated with running a pod for + a given RuntimeClass. This field will be + autopopulated at admission time by the RuntimeClass + admission controller. If the RuntimeClass + admission controller is enabled, overhead + must not be set in Pod create requests. + The RuntimeClass admission controller will + reject Pod create requests which have the + overhead already set. If RuntimeClass is + configured and selected in the PodSpec, + Overhead will be set to the value defined + in the corresponding RuntimeClass, otherwise + it will remain unset and treated as zero. + More info: https://git.k8s.io/enhancements/keps/sig-node/20190226-pod-overhead.md + This field is alpha-level as of Kubernetes + v1.16, and is only honored by servers that + enable the PodOverhead feature.' + type: object + additionalProperties: + type: string + preemptionPolicy: + description: PreemptionPolicy is the Policy + for preempting pods with lower priority. + One of Never, PreemptLowerPriority. Defaults + to PreemptLowerPriority if unset. This field + is alpha-level and is only honored by servers + that enable the NonPreemptingPriority feature. + type: string + priority: + description: The priority value. Various system + components use this field to find the priority + of the pod. When Priority Admission Controller + is enabled, it prevents users from setting + this field. The admission controller populates + this field from PriorityClassName. The higher + the value, the higher the priority. + type: integer + format: int32 + priorityClassName: + description: If specified, indicates the pod's + priority. "system-node-critical" and "system-cluster-critical" + are two special keywords which indicate + the highest priorities with the former being + the highest priority. Any other name must + be defined by creating a PriorityClass object + with that name. If not specified, the pod + priority will be default or zero if there + is no default. + type: string + readinessGates: + description: 'If specified, all readiness + gates will be evaluated for pod readiness. + A pod is ready when all its containers are + ready AND all conditions specified in the + readiness gates have status equal to "True" + More info: https://git.k8s.io/enhancements/keps/sig-network/0007-pod-ready%2B%2B.md' + type: array + items: + description: PodReadinessGate contains the + reference to a pod condition + type: object + required: + - conditionType + properties: + conditionType: + description: ConditionType refers to + a condition in the pod's condition + list with matching type. + type: string + restartPolicy: + description: 'Restart policy for all containers + within the pod. One of Always, OnFailure, + Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy' + type: string + runtimeClassName: + description: 'RuntimeClassName refers to a + RuntimeClass object in the node.k8s.io group, + which should be used to run this pod. If + no RuntimeClass resource matches the named + class, the pod will not be run. If unset + or empty, the "legacy" RuntimeClass will + be used, which is an implicit class with + an empty definition that uses the default + runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/runtime-class.md + This is a beta feature as of Kubernetes + v1.14.' + type: string + schedulerName: + description: If specified, the pod will be + dispatched by specified scheduler. If not + specified, the pod will be dispatched by + default scheduler. + type: string + securityContext: + description: 'SecurityContext holds pod-level + security attributes and common container + settings. Optional: Defaults to empty. See + type description for default values of each + field.' + type: object + properties: + fsGroup: + description: "A special supplemental group + that applies to all containers in a + pod. Some volume types allow the Kubelet + to change the ownership of that volume + to be owned by the pod: \n 1. The owning + GID will be the FSGroup 2. The setgid + bit is set (new files created in the + volume will be owned by FSGroup) 3. + The permission bits are OR'd with rw-rw---- + \n If unset, the Kubelet will not modify + the ownership and permissions of any + volume." + type: integer + format: int64 + runAsGroup: + description: The GID to run the entrypoint + of the container process. Uses runtime + default if unset. May also be set in + SecurityContext. If set in both SecurityContext + and PodSecurityContext, the value specified + in SecurityContext takes precedence + for that container. + type: integer + format: int64 + runAsNonRoot: + description: Indicates that the container + must run as a non-root user. If true, + the Kubelet will validate the image + at runtime to ensure that it does not + run as UID 0 (root) and fail to start + the container if it does. If unset or + false, no such validation will be performed. + May also be set in SecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext + takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint + of the container process. Defaults to + user specified in image metadata if + unspecified. May also be set in SecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext + takes precedence for that container. + type: integer + format: int64 + seLinuxOptions: + description: The SELinux context to be + applied to all containers. If unspecified, + the container runtime will allocate + a random SELinux context for each container. May + also be set in SecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext + takes precedence for that container. + type: object + properties: + level: + description: Level is SELinux level + label that applies to the container. + type: string + role: + description: Role is a SELinux role + label that applies to the container. + type: string + type: + description: Type is a SELinux type + label that applies to the container. + type: string + user: + description: User is a SELinux user + label that applies to the container. + type: string + supplementalGroups: + description: A list of groups applied + to the first process run in each container, + in addition to the container's primary + GID. If unspecified, no groups will + be added to any container. + type: array + items: + type: integer + format: int64 + sysctls: + description: Sysctls hold a list of namespaced + sysctls used for the pod. Pods with + unsupported sysctls (by the container + runtime) might fail to launch. + type: array + items: + description: Sysctl defines a kernel + parameter to be set + type: object + required: + - name + - value + properties: + name: + description: Name of a property + to set + type: string + value: + description: Value of a property + to set + type: string + windowsOptions: + description: The Windows specific settings + applied to all containers. If unspecified, + the options within a container's SecurityContext + will be used. If set in both SecurityContext + and PodSecurityContext, the value specified + in SecurityContext takes precedence. + type: object + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is + where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA + credential spec named by the GMSACredentialSpecName + field. This field is alpha-level + and is only honored by servers that + enable the WindowsGMSA feature flag. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName + is the name of the GMSA credential + spec to use. This field is alpha-level + and is only honored by servers that + enable the WindowsGMSA feature flag. + type: string + runAsUserName: + description: The UserName in Windows + to run the entrypoint of the container + process. Defaults to the user specified + in image metadata if unspecified. + May also be set in PodSecurityContext. + If set in both SecurityContext and + PodSecurityContext, the value specified + in SecurityContext takes precedence. + This field is alpha-level and it + is only honored by servers that + enable the WindowsRunAsUserName + feature flag. + type: string + serviceAccount: + description: 'DeprecatedServiceAccount is + a depreciated alias for ServiceAccountName. + Deprecated: Use serviceAccountName instead.' + type: string + serviceAccountName: + description: 'ServiceAccountName is the name + of the ServiceAccount to use to run this + pod. More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/' + type: string + shareProcessNamespace: + description: 'Share a single process namespace + between all of the containers in a pod. + When this is set containers will be able + to view and signal processes from other + containers in the same pod, and the first + process in each container will not be assigned + PID 1. HostPID and ShareProcessNamespace + cannot both be set. Optional: Default to + false. This field is beta-level and may + be disabled with the PodShareProcessNamespace + feature.' + type: boolean + subdomain: + description: If specified, the fully qualified + Pod hostname will be "...svc.". If not + specified, the pod will not have a domainname + at all. + type: string + terminationGracePeriodSeconds: + description: Optional duration in seconds + the pod needs to terminate gracefully. May + be decreased in delete request. Value must + be non-negative integer. The value zero + indicates delete immediately. If this value + is nil, the default grace period will be + used instead. The grace period is the duration + in seconds after the processes running in + the pod are sent a termination signal and + the time when the processes are forcibly + halted with a kill signal. Set this value + longer than the expected cleanup time for + your process. Defaults to 30 seconds. + type: integer + format: int64 + tolerations: + description: If specified, the pod's tolerations. + type: array + items: + description: The pod this Toleration is + attached to tolerates any taint that matches + the triple using the + matching operator . + type: object + properties: + effect: + description: Effect indicates the taint + effect to match. Empty means match + all taint effects. When specified, + allowed values are NoSchedule, PreferNoSchedule + and NoExecute. + type: string + key: + description: Key is the taint key that + the toleration applies to. Empty means + match all taint keys. If the key is + empty, operator must be Exists; this + combination means to match all values + and all keys. + type: string + operator: + description: Operator represents a key's + relationship to the value. Valid operators + are Exists and Equal. Defaults to + Equal. Exists is equivalent to wildcard + for value, so that a pod can tolerate + all taints of a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents + the period of time the toleration + (which must be of effect NoExecute, + otherwise this field is ignored) tolerates + the taint. By default, it is not set, + which means tolerate the taint forever + (do not evict). Zero and negative + values will be treated as 0 (evict + immediately) by the system. + type: integer + format: int64 + value: + description: Value is the taint value + the toleration matches to. If the + operator is Exists, the value should + be empty, otherwise just a regular + string. + type: string + topologySpreadConstraints: + description: TopologySpreadConstraints describes + how a group of pods ought to spread across + topology domains. Scheduler will schedule + pods in a way which abides by the constraints. + This field is alpha-level and is only honored + by clusters that enables the EvenPodsSpread + feature. All topologySpreadConstraints are + ANDed. + type: array + items: + description: TopologySpreadConstraint specifies + how to spread matching pods among the + given topology. + type: object + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + properties: + labelSelector: + description: LabelSelector is used to + find matching pods. Pods that match + this label selector are counted to + determine the number of pods in their + corresponding topology domain. + type: object + properties: + matchExpressions: + description: matchExpressions is + a list of label selector requirements. + The requirements are ANDed. + type: array + items: + description: A label selector + requirement is a selector that + contains values, a key, and + an operator that relates the + key and values. + type: object + required: + - key + - operator + properties: + key: + description: key is the label + key that the selector applies + to. + type: string + operator: + description: operator represents + a key's relationship to + a set of values. Valid operators + are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an + array of string values. + If the operator is In or + NotIn, the values array + must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be + empty. This array is replaced + during a strategic merge + patch. + type: array + items: + type: string + matchLabels: + description: matchLabels is a map + of {key,value} pairs. A single + {key,value} in the matchLabels + map is equivalent to an element + of matchExpressions, whose key + field is "key", the operator is + "In", and the values array contains + only "value". The requirements + are ANDed. + type: object + additionalProperties: + type: string + maxSkew: + description: 'MaxSkew describes the + degree to which pods may be unevenly + distributed. It''s the maximum permitted + difference between the number of matching + pods in any two topology domains of + a given topology type. For example, + in a 3-zone cluster, MaxSkew is set + to 1, and pods with the same labelSelector + spread as 1/1/0: | zone1 | zone2 | + zone3 | | P | P | | + - if MaxSkew is 1, incoming pod can + only be scheduled to zone3 to become + 1/1/1; scheduling it onto zone1(zone2) + would make the ActualSkew(2-0) on + zone1(zone2) violate MaxSkew(1). - + if MaxSkew is 2, incoming pod can + be scheduled onto any zone. It''s + a required field. Default value is + 1 and 0 is not allowed.' + type: integer + format: int32 + topologyKey: + description: TopologyKey is the key + of node labels. Nodes that have a + label with this key and identical + values are considered to be in the + same topology. We consider each as a "bucket", and try to put + balanced number of pods into each + bucket. It's a required field. + type: string + whenUnsatisfiable: + description: 'WhenUnsatisfiable indicates + how to deal with a pod if it doesn''t + satisfy the spread constraint. - DoNotSchedule + (default) tells the scheduler not + to schedule it - ScheduleAnyway tells + the scheduler to still schedule it + It''s considered as "Unsatisfiable" + if and only if placing incoming pod + on any topology violates "MaxSkew". + For example, in a 3-zone cluster, + MaxSkew is set to 1, and pods with + the same labelSelector spread as 3/1/1: + | zone1 | zone2 | zone3 | | P P P + | P | P | If WhenUnsatisfiable + is set to DoNotSchedule, incoming + pod can only be scheduled to zone2(zone3) + to become 3/2/1(3/1/2) as ActualSkew(2-1) + on zone2(zone3) satisfies MaxSkew(1). + In other words, the cluster can still + be imbalanced, but scheduler won''t + make it *more* imbalanced. It''s a + required field.' + type: string + volumes: + description: 'List of volumes that can be + mounted by containers belonging to the pod. + More info: https://kubernetes.io/docs/concepts/storage/volumes' + type: array + items: + description: Volume represents a named volume + in a pod that may be accessed by any container + in the pod. + type: object + required: + - name + properties: + awsElasticBlockStore: + description: 'AWSElasticBlockStore represents + an AWS Disk resource that is attached + to a kubelet''s host machine and then + exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: object + required: + - volumeID + properties: + fsType: + description: 'Filesystem type of + the volume that you want to mount. + Tip: Ensure that the filesystem + type is supported by the host + operating system. Examples: "ext4", + "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. More + info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors + in the filesystem from compromising + the machine' + type: string + partition: + description: 'The partition in the + volume that you want to mount. + If omitted, the default is to + mount by volume name. Examples: + For volume /dev/sda1, you specify + the partition as "1". Similarly, + the volume partition for /dev/sda + is "0" (or you can leave the property + empty).' + type: integer + format: int32 + readOnly: + description: 'Specify "true" to + force and set the ReadOnly property + in VolumeMounts to "true". If + omitted, the default is "false". + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'Unique ID of the persistent + disk resource in AWS (Amazon EBS + volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + azureDisk: + description: AzureDisk represents an + Azure Data Disk mount on the host + and bind mount to the pod. + type: object + required: + - diskName + - diskURI + properties: + cachingMode: + description: 'Host Caching mode: + None, Read Only, Read Write.' + type: string + diskName: + description: The Name of the data + disk in the blob storage + type: string + diskURI: + description: The URI the data disk + in the blob storage + type: string + fsType: + description: Filesystem type to + mount. Must be a filesystem type + supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" + if unspecified. + type: string + kind: + description: 'Expected values Shared: + multiple blob disks per storage + account Dedicated: single blob + disk per storage account Managed: + azure managed data disk (only + in managed availability set). + defaults to shared' + type: string + readOnly: + description: Defaults to false (read/write). + ReadOnly here will force the ReadOnly + setting in VolumeMounts. + type: boolean + azureFile: + description: AzureFile represents an + Azure File Service mount on the host + and bind mount to the pod. + type: object + required: + - secretName + - shareName + properties: + readOnly: + description: Defaults to false (read/write). + ReadOnly here will force the ReadOnly + setting in VolumeMounts. + type: boolean + secretName: + description: the name of secret + that contains Azure Storage Account + Name and Key + type: string + shareName: + description: Share Name + type: string + cephfs: + description: CephFS represents a Ceph + FS mount on the host that shares a + pod's lifetime + type: object + required: + - monitors + properties: + monitors: + description: 'Required: Monitors + is a collection of Ceph monitors + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: array + items: + type: string + path: + description: 'Optional: Used as + the mounted root, rather than + the full Ceph tree, default is + /' + type: string + readOnly: + description: 'Optional: Defaults + to false (read/write). ReadOnly + here will force the ReadOnly setting + in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'Optional: SecretFile + is the path to key ring for User, + default is /etc/ceph/user.secret + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'Optional: SecretRef + is reference to the authentication + secret for User, default is empty. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: object + properties: + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + user: + description: 'Optional: User is + the rados user name, default is + admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + cinder: + description: 'Cinder represents a cinder + volume attached and mounted on kubelets + host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: object + required: + - volumeID + properties: + fsType: + description: 'Filesystem type to + mount. Must be a filesystem type + supported by the host operating + system. Examples: "ext4", "xfs", + "ntfs". Implicitly inferred to + be "ext4" if unspecified. More + info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + readOnly: + description: 'Optional: Defaults + to false (read/write). ReadOnly + here will force the ReadOnly setting + in VolumeMounts. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'Optional: points to + a secret object containing parameters + used to connect to OpenStack.' + type: object + properties: + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + volumeID: + description: 'volume id used to + identify the volume in cinder. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + configMap: + description: ConfigMap represents a + configMap that should populate this + volume + type: object + properties: + defaultMode: + description: 'Optional: mode bits + to use on created files by default. + Must be a value between 0 and + 0777. Defaults to 0644. Directories + within the path are not affected + by this setting. This might be + in conflict with other options + that affect the file mode, like + fsGroup, and the result can be + other mode bits set.' + type: integer + format: int32 + items: + description: If unspecified, each + key-value pair in the Data field + of the referenced ConfigMap will + be projected into the volume as + a file whose name is the key and + content is the value. If specified, + the listed keys will be projected + into the specified paths, and + unlisted keys will not be present. + If a key is specified which is + not present in the ConfigMap, + the volume setup will error unless + it is marked optional. Paths must + be relative and may not contain + the '..' path or start with '..'. + type: array + items: + description: Maps a string key + to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode + bits to use on this file, + must be a value between + 0 and 0777. If not specified, + the volume defaultMode will + be used. This might be in + conflict with other options + that affect the file mode, + like fsGroup, and the result + can be other mode bits set.' + type: integer + format: int32 + path: + description: The relative + path of the file to map + the key to. May not be an + absolute path. May not contain + the path element '..'. May + not start with the string + '..'. + type: string + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the + ConfigMap or its keys must be + defined + type: boolean + csi: + description: CSI (Container Storage + Interface) represents storage that + is handled by an external CSI driver + (Alpha feature). + type: object + required: + - driver + properties: + driver: + description: Driver is the name + of the CSI driver that handles + this volume. Consult with your + admin for the correct name as + registered in the cluster. + type: string + fsType: + description: Filesystem type to + mount. Ex. "ext4", "xfs", "ntfs". + If not provided, the empty value + is passed to the associated CSI + driver which will determine the + default filesystem to apply. + type: string + nodePublishSecretRef: + description: NodePublishSecretRef + is a reference to the secret object + containing sensitive information + to pass to the CSI driver to complete + the CSI NodePublishVolume and + NodeUnpublishVolume calls. This + field is optional, and may be + empty if no secret is required. + If the secret object contains + more than one secret, all secret + references are passed. + type: object + properties: + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + readOnly: + description: Specifies a read-only + configuration for the volume. + Defaults to false (read/write). + type: boolean + volumeAttributes: + description: VolumeAttributes stores + driver-specific properties that + are passed to the CSI driver. + Consult your driver's documentation + for supported values. + type: object + additionalProperties: + type: string + downwardAPI: + description: DownwardAPI represents + downward API about the pod that should + populate this volume + type: object + properties: + defaultMode: + description: 'Optional: mode bits + to use on created files by default. + Must be a value between 0 and + 0777. Defaults to 0644. Directories + within the path are not affected + by this setting. This might be + in conflict with other options + that affect the file mode, like + fsGroup, and the result can be + other mode bits set.' + type: integer + format: int32 + items: + description: Items is a list of + downward API volume file + type: array + items: + description: DownwardAPIVolumeFile + represents information to create + the file containing the pod + field + type: object + required: + - path + properties: + fieldRef: + description: 'Required: Selects + a field of the pod: only + annotations, labels, name + and namespace are supported.' + type: object + required: + - fieldPath + properties: + apiVersion: + description: Version of + the schema the FieldPath + is written in terms + of, defaults to "v1". + type: string + fieldPath: + description: Path of the + field to select in the + specified API version. + type: string + mode: + description: 'Optional: mode + bits to use on this file, + must be a value between + 0 and 0777. If not specified, + the volume defaultMode will + be used. This might be in + conflict with other options + that affect the file mode, + like fsGroup, and the result + can be other mode bits set.' + type: integer + format: int32 + path: + description: 'Required: Path + is the relative path name + of the file to be created. + Must not be absolute or + contain the ''..'' path. + Must be utf-8 encoded. The + first item of the relative + path must not start with + ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource + of the container: only resources + limits and requests (limits.cpu, + limits.memory, requests.cpu + and requests.memory) are + currently supported.' + type: object + required: + - resource + properties: + containerName: + description: 'Container + name: required for volumes, + optional for env vars' + type: string + divisor: + description: Specifies + the output format of + the exposed resources, + defaults to "1" + type: string + resource: + description: 'Required: + resource to select' + type: string + emptyDir: + description: 'EmptyDir represents a + temporary directory that shares a + pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: object + properties: + medium: + description: 'What type of storage + medium should back this directory. + The default is "" which means + to use the node''s default medium. + Must be an empty string (default) + or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: string + sizeLimit: + description: 'Total amount of local + storage required for this EmptyDir + volume. The size limit is also + applicable for memory medium. + The maximum usage on memory medium + EmptyDir would be the minimum + value between the SizeLimit specified + here and the sum of memory limits + of all containers in a pod. The + default is nil which means that + the limit is undefined. More info: + http://kubernetes.io/docs/user-guide/volumes#emptydir' + type: string + fc: + description: FC represents a Fibre Channel + resource that is attached to a kubelet's + host machine and then exposed to the + pod. + type: object + properties: + fsType: + description: 'Filesystem type to + mount. Must be a filesystem type + supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" + if unspecified. TODO: how do we + prevent errors in the filesystem + from compromising the machine' + type: string + lun: + description: 'Optional: FC target + lun number' + type: integer + format: int32 + readOnly: + description: 'Optional: Defaults + to false (read/write). ReadOnly + here will force the ReadOnly setting + in VolumeMounts.' + type: boolean + targetWWNs: + description: 'Optional: FC target + worldwide names (WWNs)' + type: array + items: + type: string + wwids: + description: 'Optional: FC volume + world wide identifiers (wwids) + Either wwids or combination of + targetWWNs and lun must be set, + but not both simultaneously.' + type: array + items: + type: string + flexVolume: + description: FlexVolume represents a + generic volume resource that is provisioned/attached + using an exec based plugin. + type: object + required: + - driver + properties: + driver: + description: Driver is the name + of the driver to use for this + volume. + type: string + fsType: + description: Filesystem type to + mount. Must be a filesystem type + supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". + The default filesystem depends + on FlexVolume script. + type: string + options: + description: 'Optional: Extra command + options if any.' + type: object + additionalProperties: + type: string + readOnly: + description: 'Optional: Defaults + to false (read/write). ReadOnly + here will force the ReadOnly setting + in VolumeMounts.' + type: boolean + secretRef: + description: 'Optional: SecretRef + is reference to the secret object + containing sensitive information + to pass to the plugin scripts. + This may be empty if no secret + object is specified. If the secret + object contains more than one + secret, all secrets are passed + to the plugin scripts.' + type: object + properties: + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + flocker: + description: Flocker represents a Flocker + volume attached to a kubelet's host + machine. This depends on the Flocker + control service being running + type: object + properties: + datasetName: + description: Name of the dataset + stored as metadata -> name on + the dataset for Flocker should + be considered as deprecated + type: string + datasetUUID: + description: UUID of the dataset. + This is unique identifier of a + Flocker dataset + type: string + gcePersistentDisk: + description: 'GCEPersistentDisk represents + a GCE Disk resource that is attached + to a kubelet''s host machine and then + exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: object + required: + - pdName + properties: + fsType: + description: 'Filesystem type of + the volume that you want to mount. + Tip: Ensure that the filesystem + type is supported by the host + operating system. Examples: "ext4", + "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. More + info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors + in the filesystem from compromising + the machine' + type: string + partition: + description: 'The partition in the + volume that you want to mount. + If omitted, the default is to + mount by volume name. Examples: + For volume /dev/sda1, you specify + the partition as "1". Similarly, + the volume partition for /dev/sda + is "0" (or you can leave the property + empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: integer + format: int32 + pdName: + description: 'Unique name of the + PD resource in GCE. Used to identify + the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'ReadOnly here will + force the ReadOnly setting in + VolumeMounts. Defaults to false. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + gitRepo: + description: 'GitRepo represents a git + repository at a particular revision. + DEPRECATED: GitRepo is deprecated. + To provision a container with a git + repo, mount an EmptyDir into an InitContainer + that clones the repo using git, then + mount the EmptyDir into the Pod''s + container.' + type: object + required: + - repository + properties: + directory: + description: Target directory name. + Must not contain or start with + '..'. If '.' is supplied, the + volume directory will be the git + repository. Otherwise, if specified, + the volume will contain the git + repository in the subdirectory + with the given name. + type: string + repository: + description: Repository URL + type: string + revision: + description: Commit hash for the + specified revision. + type: string + glusterfs: + description: 'Glusterfs represents a + Glusterfs mount on the host that shares + a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' + type: object + required: + - endpoints + - path + properties: + endpoints: + description: 'EndpointsName is the + endpoint name that details Glusterfs + topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'Path is the Glusterfs + volume path. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'ReadOnly here will + force the Glusterfs volume to + be mounted with read-only permissions. + Defaults to false. More info: + https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + hostPath: + description: 'HostPath represents a + pre-existing file or directory on + the host machine that is directly + exposed to the container. This is + generally used for system agents or + other privileged things that are allowed + to see the host machine. Most containers + will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + --- TODO(jonesdl) We need to restrict + who can use host directory mounts + and who can/can not mount host directories + as read/write.' + type: object + required: + - path + properties: + path: + description: 'Path of the directory + on the host. If the path is a + symlink, it will follow the link + to the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'Type for HostPath + Volume Defaults to "" More info: + https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + iscsi: + description: 'ISCSI represents an ISCSI + Disk resource that is attached to + a kubelet''s host machine and then + exposed to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + type: object + required: + - iqn + - lun + - targetPortal + properties: + chapAuthDiscovery: + description: whether support iSCSI + Discovery CHAP authentication + type: boolean + chapAuthSession: + description: whether support iSCSI + Session CHAP authentication + type: boolean + fsType: + description: 'Filesystem type of + the volume that you want to mount. + Tip: Ensure that the filesystem + type is supported by the host + operating system. Examples: "ext4", + "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. More + info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors + in the filesystem from compromising + the machine' + type: string + initiatorName: + description: Custom iSCSI Initiator + Name. If initiatorName is specified + with iscsiInterface simultaneously, + new iSCSI interface : will be created for the + connection. + type: string + iqn: + description: Target iSCSI Qualified + Name. + type: string + iscsiInterface: + description: iSCSI Interface Name + that uses an iSCSI transport. + Defaults to 'default' (tcp). + type: string + lun: + description: iSCSI Target Lun number. + type: integer + format: int32 + portals: + description: iSCSI Target Portal + List. The portal is either an + IP or ip_addr:port if the port + is other than default (typically + TCP ports 860 and 3260). + type: array + items: + type: string + readOnly: + description: ReadOnly here will + force the ReadOnly setting in + VolumeMounts. Defaults to false. + type: boolean + secretRef: + description: CHAP Secret for iSCSI + target and initiator authentication + type: object + properties: + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + targetPortal: + description: iSCSI Target Portal. + The Portal is either an IP or + ip_addr:port if the port is other + than default (typically TCP ports + 860 and 3260). + type: string + name: + description: 'Volume''s name. Must be + a DNS_LABEL and unique within the + pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + nfs: + description: 'NFS represents an NFS + mount on the host that shares a pod''s + lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: object + required: + - path + - server + properties: + path: + description: 'Path that is exported + by the NFS server. More info: + https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'ReadOnly here will + force the NFS export to be mounted + with read-only permissions. Defaults + to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'Server is the hostname + or IP address of the NFS server. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + persistentVolumeClaim: + description: 'PersistentVolumeClaimVolumeSource + represents a reference to a PersistentVolumeClaim + in the same namespace. More info: + https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: object + required: + - claimName + properties: + claimName: + description: 'ClaimName is the name + of a PersistentVolumeClaim in + the same namespace as the pod + using this volume. More info: + https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: Will force the ReadOnly + setting in VolumeMounts. Default + false. + type: boolean + photonPersistentDisk: + description: PhotonPersistentDisk represents + a PhotonController persistent disk + attached and mounted on kubelets host + machine + type: object + required: + - pdID + properties: + fsType: + description: Filesystem type to + mount. Must be a filesystem type + supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" + if unspecified. + type: string + pdID: + description: ID that identifies + Photon Controller persistent disk + type: string + portworxVolume: + description: PortworxVolume represents + a portworx volume attached and mounted + on kubelets host machine + type: object + required: + - volumeID + properties: + fsType: + description: FSType represents the + filesystem type to mount Must + be a filesystem type supported + by the host operating system. + Ex. "ext4", "xfs". Implicitly + inferred to be "ext4" if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). + ReadOnly here will force the ReadOnly + setting in VolumeMounts. + type: boolean + volumeID: + description: VolumeID uniquely identifies + a Portworx volume + type: string + projected: + description: Items for all in one resources + secrets, configmaps, and downward + API + type: object + required: + - sources + properties: + defaultMode: + description: Mode bits to use on + created files by default. Must + be a value between 0 and 0777. + Directories within the path are + not affected by this setting. + This might be in conflict with + other options that affect the + file mode, like fsGroup, and the + result can be other mode bits + set. + type: integer + format: int32 + sources: + description: list of volume projections + type: array + items: + description: Projection that may + be projected along with other + supported volume types + type: object + properties: + configMap: + description: information about + the configMap data to project + type: object + properties: + items: + description: If unspecified, + each key-value pair + in the Data field of + the referenced ConfigMap + will be projected into + the volume as a file + whose name is the key + and content is the value. + If specified, the listed + keys will be projected + into the specified paths, + and unlisted keys will + not be present. If a + key is specified which + is not present in the + ConfigMap, the volume + setup will error unless + it is marked optional. + Paths must be relative + and may not contain + the '..' path or start + with '..'. + type: array + items: + description: Maps a + string key to a path + within a volume. + type: object + required: + - key + - path + properties: + key: + description: The + key to project. + type: string + mode: + description: 'Optional: + mode bits to use + on this file, + must be a value + between 0 and + 0777. If not specified, + the volume defaultMode + will be used. + This might be + in conflict with + other options + that affect the + file mode, like + fsGroup, and the + result can be + other mode bits + set.' + type: integer + format: int32 + path: + description: The + relative path + of the file to + map the key to. + May not be an + absolute path. + May not contain + the path element + '..'. May not + start with the + string '..'. + type: string + name: + description: 'Name of + the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful + fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether + the ConfigMap or its + keys must be defined + type: boolean + downwardAPI: + description: information about + the downwardAPI data to + project + type: object + properties: + items: + description: Items is + a list of DownwardAPIVolume + file + type: array + items: + description: DownwardAPIVolumeFile + represents information + to create the file + containing the pod + field + type: object + required: + - path + properties: + fieldRef: + description: 'Required: + Selects a field + of the pod: only + annotations, labels, + name and namespace + are supported.' + type: object + required: + - fieldPath + properties: + apiVersion: + description: Version + of the schema + the FieldPath + is written + in terms of, + defaults to + "v1". + type: string + fieldPath: + description: Path + of the field + to select + in the specified + API version. + type: string + mode: + description: 'Optional: + mode bits to use + on this file, + must be a value + between 0 and + 0777. If not specified, + the volume defaultMode + will be used. + This might be + in conflict with + other options + that affect the + file mode, like + fsGroup, and the + result can be + other mode bits + set.' + type: integer + format: int32 + path: + description: 'Required: + Path is the relative + path name of the + file to be created. + Must not be absolute + or contain the + ''..'' path. Must + be utf-8 encoded. + The first item + of the relative + path must not + start with ''..''' + type: string + resourceFieldRef: + description: 'Selects + a resource of + the container: + only resources + limits and requests + (limits.cpu, limits.memory, + requests.cpu and + requests.memory) + are currently + supported.' + type: object + required: + - resource + properties: + containerName: + description: 'Container + name: required + for volumes, + optional for + env vars' + type: string + divisor: + description: Specifies + the output + format of + the exposed + resources, + defaults to + "1" + type: string + resource: + description: 'Required: + resource to + select' + type: string + secret: + description: information about + the secret data to project + type: object + properties: + items: + description: If unspecified, + each key-value pair + in the Data field of + the referenced Secret + will be projected into + the volume as a file + whose name is the key + and content is the value. + If specified, the listed + keys will be projected + into the specified paths, + and unlisted keys will + not be present. If a + key is specified which + is not present in the + Secret, the volume setup + will error unless it + is marked optional. + Paths must be relative + and may not contain + the '..' path or start + with '..'. + type: array + items: + description: Maps a + string key to a path + within a volume. + type: object + required: + - key + - path + properties: + key: + description: The + key to project. + type: string + mode: + description: 'Optional: + mode bits to use + on this file, + must be a value + between 0 and + 0777. If not specified, + the volume defaultMode + will be used. + This might be + in conflict with + other options + that affect the + file mode, like + fsGroup, and the + result can be + other mode bits + set.' + type: integer + format: int32 + path: + description: The + relative path + of the file to + map the key to. + May not be an + absolute path. + May not contain + the path element + '..'. May not + start with the + string '..'. + type: string + name: + description: 'Name of + the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful + fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether + the Secret or its key + must be defined + type: boolean + serviceAccountToken: + description: information about + the serviceAccountToken + data to project + type: object + required: + - path + properties: + audience: + description: Audience + is the intended audience + of the token. A recipient + of a token must identify + itself with an identifier + specified in the audience + of the token, and otherwise + should reject the token. + The audience defaults + to the identifier of + the apiserver. + type: string + expirationSeconds: + description: ExpirationSeconds + is the requested duration + of validity of the service + account token. As the + token approaches expiration, + the kubelet volume plugin + will proactively rotate + the service account + token. The kubelet will + start trying to rotate + the token if the token + is older than 80 percent + of its time to live + or if the token is older + than 24 hours.Defaults + to 1 hour and must be + at least 10 minutes. + type: integer + format: int64 + path: + description: Path is the + path relative to the + mount point of the file + to project the token + into. + type: string + quobyte: + description: Quobyte represents a Quobyte + mount on the host that shares a pod's + lifetime + type: object + required: + - registry + - volume + properties: + group: + description: Group to map volume + access to Default is no group + type: string + readOnly: + description: ReadOnly here will + force the Quobyte volume to be + mounted with read-only permissions. + Defaults to false. + type: boolean + registry: + description: Registry represents + a single or multiple Quobyte Registry + services specified as a string + as host:port pair (multiple entries + are separated with commas) which + acts as the central registry for + volumes + type: string + tenant: + description: Tenant owning the given + Quobyte volume in the Backend + Used with dynamically provisioned + Quobyte volumes, value is set + by the plugin + type: string + user: + description: User to map volume + access to Defaults to serivceaccount + user + type: string + volume: + description: Volume is a string + that references an already created + Quobyte volume by name. + type: string + rbd: + description: 'RBD represents a Rados + Block Device mount on the host that + shares a pod''s lifetime. More info: + https://examples.k8s.io/volumes/rbd/README.md' + type: object + required: + - image + - monitors + properties: + fsType: + description: 'Filesystem type of + the volume that you want to mount. + Tip: Ensure that the filesystem + type is supported by the host + operating system. Examples: "ext4", + "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. More + info: https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors + in the filesystem from compromising + the machine' + type: string + image: + description: 'The rados image name. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'Keyring is the path + to key ring for RBDUser. Default + is /etc/ceph/keyring. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'A collection of Ceph + monitors. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: array + items: + type: string + pool: + description: 'The rados pool name. + Default is rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'ReadOnly here will + force the ReadOnly setting in + VolumeMounts. Defaults to false. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'SecretRef is name + of the authentication secret for + RBDUser. If provided overrides + keyring. Default is nil. More + info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: object + properties: + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + user: + description: 'The rados user name. + Default is admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + scaleIO: + description: ScaleIO represents a ScaleIO + persistent volume attached and mounted + on Kubernetes nodes. + type: object + required: + - gateway + - secretRef + - system + properties: + fsType: + description: Filesystem type to + mount. Must be a filesystem type + supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". + Default is "xfs". + type: string + gateway: + description: The host address of + the ScaleIO API Gateway. + type: string + protectionDomain: + description: The name of the ScaleIO + Protection Domain for the configured + storage. + type: string + readOnly: + description: Defaults to false (read/write). + ReadOnly here will force the ReadOnly + setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef references + to the secret for ScaleIO user + and other sensitive information. + If this is not provided, Login + operation will fail. + type: object + properties: + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + sslEnabled: + description: Flag to enable/disable + SSL communication with Gateway, + default false + type: boolean + storageMode: + description: Indicates whether the + storage for a volume should be + ThickProvisioned or ThinProvisioned. + Default is ThinProvisioned. + type: string + storagePool: + description: The ScaleIO Storage + Pool associated with the protection + domain. + type: string + system: + description: The name of the storage + system as configured in ScaleIO. + type: string + volumeName: + description: The name of a volume + already created in the ScaleIO + system that is associated with + this volume source. + type: string + secret: + description: 'Secret represents a secret + that should populate this volume. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: object + properties: + defaultMode: + description: 'Optional: mode bits + to use on created files by default. + Must be a value between 0 and + 0777. Defaults to 0644. Directories + within the path are not affected + by this setting. This might be + in conflict with other options + that affect the file mode, like + fsGroup, and the result can be + other mode bits set.' + type: integer + format: int32 + items: + description: If unspecified, each + key-value pair in the Data field + of the referenced Secret will + be projected into the volume as + a file whose name is the key and + content is the value. If specified, + the listed keys will be projected + into the specified paths, and + unlisted keys will not be present. + If a key is specified which is + not present in the Secret, the + volume setup will error unless + it is marked optional. Paths must + be relative and may not contain + the '..' path or start with '..'. + type: array + items: + description: Maps a string key + to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode + bits to use on this file, + must be a value between + 0 and 0777. If not specified, + the volume defaultMode will + be used. This might be in + conflict with other options + that affect the file mode, + like fsGroup, and the result + can be other mode bits set.' + type: integer + format: int32 + path: + description: The relative + path of the file to map + the key to. May not be an + absolute path. May not contain + the path element '..'. May + not start with the string + '..'. + type: string + optional: + description: Specify whether the + Secret or its keys must be defined + type: boolean + secretName: + description: 'Name of the secret + in the pod''s namespace to use. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + storageos: + description: StorageOS represents a + StorageOS volume attached and mounted + on Kubernetes nodes. + type: object + properties: + fsType: + description: Filesystem type to + mount. Must be a filesystem type + supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" + if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). + ReadOnly here will force the ReadOnly + setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef specifies + the secret to use for obtaining + the StorageOS API credentials. If + not specified, default values + will be attempted. + type: object + properties: + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + volumeName: + description: VolumeName is the human-readable + name of the StorageOS volume. Volume + names are only unique within a + namespace. + type: string + volumeNamespace: + description: VolumeNamespace specifies + the scope of the volume within + StorageOS. If no namespace is + specified then the Pod's namespace + will be used. This allows the + Kubernetes name scoping to be + mirrored within StorageOS for + tighter integration. Set VolumeName + to any name to override the default + behaviour. Set to "default" if + you are not using namespaces within + StorageOS. Namespaces that do + not pre-exist within StorageOS + will be created. + type: string + vsphereVolume: + description: VsphereVolume represents + a vSphere volume attached and mounted + on kubelets host machine + type: object + required: + - volumePath + properties: + fsType: + description: Filesystem type to + mount. Must be a filesystem type + supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" + if unspecified. + type: string + storagePolicyID: + description: Storage Policy Based + Management (SPBM) profile ID associated + with the StoragePolicyName. + type: string + storagePolicyName: + description: Storage Policy Based + Management (SPBM) profile name. + type: string + volumePath: + description: Path that identifies + vSphere volume vmdk + type: string + permissions: + type: array + items: + description: StrategyDeploymentPermissions describe the rbac + rules and service account needed by the install strategy + type: object + required: + - rules + - serviceAccountName + properties: + rules: + type: array + items: + description: PolicyRule holds information that describes + a policy rule, but does not contain information about + who the rule applies to or which namespace the rule + applies to. + type: object + required: + - verbs + properties: + apiGroups: + description: APIGroups is the name of the APIGroup + that contains the resources. If multiple API + groups are specified, any action requested against + one of the enumerated resources in any API group + will be allowed. + type: array + items: + type: string + nonResourceURLs: + description: NonResourceURLs is a set of partial + urls that a user should have access to. *s are + allowed, but only as the full, final step in the + path Since non-resource URLs are not namespaced, + this field is only applicable for ClusterRoles + referenced from a ClusterRoleBinding. Rules can + either apply to API resources (such as "pods" + or "secrets") or non-resource URL paths (such + as "/api"), but not both. + type: array + items: + type: string + resourceNames: + description: ResourceNames is an optional white + list of names that the rule applies to. An empty + set means that everything is allowed. + type: array + items: + type: string + resources: + description: Resources is a list of resources this + rule applies to. ResourceAll represents all resources. + type: array + items: + type: string + verbs: + description: Verbs is a list of Verbs that apply + to ALL the ResourceKinds and AttributeRestrictions + contained in this rule. VerbAll represents all + kinds. + type: array + items: + type: string + serviceAccountName: + type: string + strategy: + type: string + installModes: + description: InstallModes specify supported installation types + type: array + items: + description: InstallMode associates an InstallModeType with a flag + representing if the CSV supports it + type: object + required: + - supported + - type + properties: + supported: + type: boolean + type: + description: InstallModeType is a supported type of install mode + for CSV installation + type: string + keywords: + type: array + items: + type: string + labels: + description: Map of string keys and values that can be used to organize + and categorize (scope and select) objects. + type: object + additionalProperties: + type: string + links: + type: array + items: + type: object + properties: + name: + type: string + url: + type: string + maintainers: + type: array + items: + type: object + properties: + email: + type: string + name: + type: string + maturity: + type: string + minKubeVersion: + type: string + nativeAPIs: + type: array + items: + description: GroupVersionKind unambiguously identifies a kind. It + doesn't anonymously include GroupVersion to avoid automatic coersion. It + doesn't use a GroupVersion to avoid custom marshalling + type: object + required: + - group + - kind + - version + properties: + group: + type: string + kind: + type: string + version: + type: string + provider: + type: object + properties: + name: + type: string + url: + type: string + replaces: + description: The name of a CSV this one replaces. Should match the `metadata.Name` + field of the old CSV. + type: string + selector: + description: Label selector for related resources. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains + values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: operator represents a key's relationship to a + set of values. Valid operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string values. If the operator + is In or NotIn, the values array must be non-empty. If the + operator is Exists or DoesNotExist, the values array must + be empty. This array is replaced during a strategic merge + patch. + type: array + items: + type: string + matchLabels: + description: matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator is + "In", and the values array contains only "value". The requirements + are ANDed. + type: object + additionalProperties: + type: string + version: + description: OperatorVersion is a wrapper around semver.Version which + supports correct marshaling to YAML and JSON. + type: string status: + description: ClusterServiceVersionStatus represents information about the + status of a pod. Status may trail the actual state of a system. type: object - description: Status for a ClusterServiceVersion + properties: + certsLastUpdated: + description: Last time the owned APIService certs were updated + type: string + format: date-time + certsRotateAt: + description: Time the owned APIService certs will rotate next + type: string + format: date-time + conditions: + description: List of conditions, a history of state transitions + type: array + items: + description: Conditions appear in the status as a record of state + transitions on the ClusterServiceVersion + type: object + properties: + lastTransitionTime: + description: Last time the status transitioned from one status + to another. + type: string + format: date-time + lastUpdateTime: + description: Last time we updated the status + type: string + format: date-time + message: + description: A human readable message indicating details about + why the ClusterServiceVersion is in this condition. + type: string + phase: + description: Condition of the ClusterServiceVersion + type: string + reason: + description: A brief CamelCase message indicating details about + why the ClusterServiceVersion is in this state. e.g. 'RequirementsNotMet' + type: string + lastTransitionTime: + description: Last time the status transitioned from one status to another. + type: string + format: date-time + lastUpdateTime: + description: Last time we updated the status + type: string + format: date-time + message: + description: A human readable message indicating details about why the + ClusterServiceVersion is in this condition. + type: string + phase: + description: Current condition of the ClusterServiceVersion + type: string + reason: + description: A brief CamelCase message indicating details about why + the ClusterServiceVersion is in this state. e.g. 'RequirementsNotMet' + type: string + requirementStatus: + description: The status of each requirement for this CSV + type: array + items: + type: object + required: + - group + - kind + - message + - name + - status + - version + properties: + dependents: + type: array + items: + description: DependentStatus is the status for a dependent requirement + (to prevent infinite nesting) + type: object + required: + - group + - kind + - status + - version + properties: + group: + type: string + kind: + type: string + message: + type: string + status: + description: StatusReason is a camelcased reason for the + status of a RequirementStatus or DependentStatus + type: string + uuid: + type: string + version: + type: string + group: + type: string + kind: + type: string + message: + type: string + name: + type: string + status: + description: StatusReason is a camelcased reason for the status + of a RequirementStatus or DependentStatus + type: string + uuid: + type: string + version: + type: string diff --git a/deploy/chart/templates/0000_50_olm_04-installplan.crd.yaml b/deploy/chart/templates/0000_50_olm_04-installplan.crd.yaml index 28e49a9d90..3664f36942 100644 --- a/deploy/chart/templates/0000_50_olm_04-installplan.crd.yaml +++ b/deploy/chart/templates/0000_50_olm_04-installplan.crd.yaml @@ -4,7 +4,8 @@ metadata: name: installplans.operators.coreos.com annotations: displayName: Install Plan - description: Represents a plan to install and resolve dependencies for Cluster Services + description: Represents a plan to install and resolve dependencies for Cluster + Services spec: group: operators.coreos.com version: v1alpha1 @@ -39,36 +40,165 @@ spec: status: {} validation: openAPIV3Schema: - description: Represents a plan to install and resolve dependencies for Cluster Services. + description: InstallPlan defines the installation of a set of operators. + type: object + required: + - metadata + - spec properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object spec: + description: InstallPlanSpec defines a set of Application resources to be + installed type: object - description: Spec for an InstallPlan required: - - clusterServiceVersionNames - approval + - approved + - clusterServiceVersionNames properties: + approval: + description: Approval is the user approval policy for an InstallPlan. + type: string + approved: + type: boolean + clusterServiceVersionNames: + type: array + items: + type: string source: type: string - description: Name of the preferred CatalogSource sourceNamespace: type: string - description: Namespace that contains the preffered CatalogSource - clusterServiceVersionNames: + status: + description: "InstallPlanStatus represents the information about the status + of steps required to complete installation. \n Status may trail the actual + state of a system." + type: object + required: + - catalogSources + - phase + properties: + attenuatedServiceAccountRef: + description: AttenuatedServiceAccountRef references the service account + that is used to do scoped operator install. + type: object + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + catalogSources: type: array - description: A list of the names of the Cluster Services items: type: string - anyOf: - - properties: - approval: - enum: - - Manual - approved: - type: boolean - required: - - approved - - properties: - approval: - enum: - - Automatic + conditions: + type: array + items: + description: InstallPlanCondition represents the overall status of + the execution of an InstallPlan. + type: object + properties: + lastTransitionTime: + type: string + format: date-time + lastUpdateTime: + type: string + format: date-time + message: + type: string + reason: + description: ConditionReason is a camelcased reason for the state + transition. + type: string + status: + type: string + type: + description: InstallPlanConditionType describes the state of an + InstallPlan at a certain point as a whole. + type: string + phase: + description: InstallPlanPhase is the current status of a InstallPlan + as a whole. + type: string + plan: + type: array + items: + description: Step represents the status of an individual step in an + InstallPlan. + type: object + required: + - resolving + - resource + - status + properties: + resolving: + type: string + resource: + description: StepResource represents the status of a resource + to be tracked by an InstallPlan. + type: object + required: + - group + - kind + - name + - sourceName + - sourceNamespace + - version + properties: + group: + type: string + kind: + type: string + manifest: + type: string + name: + type: string + sourceName: + type: string + sourceNamespace: + type: string + version: + type: string + status: + description: StepStatus is the current status of a particular + resource an in InstallPlan + type: string diff --git a/deploy/chart/templates/0000_50_olm_05-subscription.crd.yaml b/deploy/chart/templates/0000_50_olm_05-subscription.crd.yaml index a44122870e..359ab0d8ba 100644 --- a/deploy/chart/templates/0000_50_olm_05-subscription.crd.yaml +++ b/deploy/chart/templates/0000_50_olm_05-subscription.crd.yaml @@ -4,7 +4,8 @@ metadata: name: subscriptions.operators.coreos.com annotations: displayName: Subscription - description: Subscribes service catalog to a source and channel to recieve updates for packages. + description: Subscribes service catalog to a source and channel to recieve updates + for packages. spec: group: operators.coreos.com version: v1alpha1 @@ -41,162 +42,315 @@ spec: status: {} validation: openAPIV3Schema: - description: Subscribes service catalog to a source and channel to recieve updates for packages. + description: Subscription keeps operators up to date by tracking changes to + Catalogs. + type: object + required: + - metadata + - spec properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object spec: + description: SubscriptionSpec defines an Application that can be installed type: object - description: Spec for a Subscription required: - - source - name + - source + - sourceNamespace properties: - source: - type: string - description: Name of a CatalogSource that defines where and how to find the channel - sourceNamespace: - type: string - description: The Kubernetes namespace where the CatalogSource used is located - name: - type: string - description: Name of the package that defines the application channel: type: string - description: Name of the channel to track - startingCSV: - type: string - description: Name of the AppType that this subscription tracks - installPlanApproval: - type: string - description: Approval mode for emitted InstallPlans - enum: - - Manual - - Automatic config: + description: SubscriptionConfig contains configuration specified for + a subscription. type: object - description: Operator Pod configuration, it is applied to the operator during install. It has higher precedence than the same configuration specified in a ClusterServiveVersion. properties: env: + description: Env is a list of environment variables to set in the + container. Cannot be updated. type: array - description: List of environment variables to set in the container items: + description: EnvVar represents an environment variable present + in a Container. type: object - description: EnvVar represents an environment variable present in a Container required: - - name + - name properties: name: + description: Name of the environment variable. Must be a C_IDENTIFIER. type: string - description: EnvVar represents an environment variable present in a Container value: + description: 'Variable references $(VAR_NAME) are expanded + using the previous defined environment variables in the + container and any service environment variables. If a variable + cannot be resolved, the reference in the input string will + be unchanged. The $(VAR_NAME) syntax can be escaped with + a double $$, ie: $$(VAR_NAME). Escaped references will never + be expanded, regardless of whether the variable exists or + not. Defaults to "".' type: string - description: Value of the environment variable specified valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. type: object - description: Source for the environment variable's value. Cannot be used if value is not empty properties: configMapKeyRef: + description: Selects a key of a ConfigMap. type: object - description: Selects a key of a ConfigMap required: - - key + - key properties: key: + description: The key to select. type: string - description: The key to select name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' type: string - description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names" optional: + description: Specify whether the ConfigMap or its + key must be defined type: boolean - description: Specify whether the ConfigMap or its key must be defined fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, metadata.labels, metadata.annotations, + spec.nodeName, spec.serviceAccountName, status.hostIP, + status.podIP.' type: object - description: "Selects a field of the pod: supports metadata.name, metadata.namespace, metadata.labels, metadata.annotations, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP" required: - - fieldPath + - fieldPath properties: apiVersion: + description: Version of the schema the FieldPath is + written in terms of, defaults to "v1". type: string - description: Version of the schema the FieldPath is written in terms of, defaults to v1 fieldPath: + description: Path of the field to select in the specified + API version. type: string - description: Path of the field to select in the specified API version resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.ephemeral-storage) are currently supported.' type: object - description: "Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported." required: - - resource + - resource properties: containerName: + description: 'Container name: required for volumes, + optional for env vars' type: string - description: "Container name: required for volumes, optional for env vars" divisor: + description: Specifies the output format of the exposed + resources, defaults to "1" type: string - description: Specifies the output format of the exposed resources, defaults to 1 resource: + description: 'Required: resource to select' type: string - description: "Required: resource to select" secretKeyRef: - type: object description: Selects a key of a secret in the pod's namespace + type: object required: - - key + - key properties: key: + description: The key of the secret to select from. Must + be a valid secret key. type: string - description: The key of the secret to select from. Must be a valid secret key name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' type: string - description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names" optional: + description: Specify whether the Secret or its key + must be defined type: boolean - description: Specify whether the Secret or its key must be defined envFrom: + description: EnvFrom is a list of sources to populate environment + variables in the container. The keys defined within a source must + be a C_IDENTIFIER. All invalid keys will be reported as an event + when the container is starting. When a key exists in multiple + sources, the value associated with the last source will take precedence. + Values defined by an Env with a duplicate key will take precedence. + Immutable. type: array - description: "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated" items: - type: object description: EnvFromSource represents the source of a set of ConfigMaps + type: object properties: configMapRef: + description: The ConfigMap to select from type: object - description: "ConfigMapEnvSource selects a ConfigMap to populate the environment variables with.\n\nThe contents of the target ConfigMap's Data field will represent the key-value pairs as environment variables" properties: name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' type: string - description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names" optional: - type: boolean description: Specify whether the ConfigMap must be defined + type: boolean prefix: + description: An optional identifier to prepend to each key + in the ConfigMap. Must be a C_IDENTIFIER. type: string - description: An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER secretRef: + description: The Secret to select from type: object - description: "SecretEnvSource selects a Secret to populate the environment variables with.\n\nThe contents of the target Secret's Data field will represent the key-value pairs as environment variables" properties: name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' type: string - description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names" optional: - type: boolean description: Specify whether the Secret must be defined - + type: boolean + nodeSelector: + description: 'NodeSelector is a selector which must be true for + the pod to fit on a node. Selector which must match a node''s + labels for the pod to be scheduled on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/' + type: object + additionalProperties: + type: string + resources: + description: 'Resources represents compute resources required by + this container. Immutable. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + properties: + limits: + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + additionalProperties: + type: string + requests: + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + additionalProperties: + type: string + selector: + description: Selector is the label selector for pods to be configured. + Existing ReplicaSets whose pods are selected by this will be the + ones affected by this deployment. It must match the pod template's + labels. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that + contains values, a key, and an operator that relates the + key and values. + type: object + required: + - key + - operator + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of string values. If the + operator is In or NotIn, the values array must be non-empty. + If the operator is Exists or DoesNotExist, the values + array must be empty. This array is replaced during a + strategic merge patch. + type: array + items: + type: string + matchLabels: + description: matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator + is "In", and the values array contains only "value". The requirements + are ANDed. + type: object + additionalProperties: + type: string + tolerations: + description: Tolerations are the pod's tolerations. + type: array + items: + description: The pod this Toleration is attached to tolerates + any taint that matches the triple using the + matching operator . + type: object + properties: + effect: + description: Effect indicates the taint effect to match. Empty + means match all taint effects. When specified, allowed values + are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, + operator must be Exists; this combination means to match + all values and all keys. + type: string + operator: + description: Operator represents a key's relationship to the + value. Valid operators are Exists and Equal. Defaults to + Equal. Exists is equivalent to wildcard for value, so that + a pod can tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of time + the toleration (which must be of effect NoExecute, otherwise + this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do + not evict). Zero and negative values will be treated as + 0 (evict immediately) by the system. + type: integer + format: int64 + value: + description: Value is the taint value the toleration matches + to. If the operator is Exists, the value should be empty, + otherwise just a regular string. + type: string volumeMounts: description: List of VolumeMounts to set in the container. + type: array items: description: VolumeMount describes a mounting of a Volume within a container. + type: object + required: + - mountPath + - name properties: mountPath: description: Path within the container at which the volume should be mounted. Must not contain ':'. type: string mountPropagation: - description: mountPropagation determines how mounts are - propagated from the host to container and the other way - around. When not set, MountPropagationNone is used. This - field is beta in 1.10. + description: mountPropagation determines how mounts are propagated + from the host to container and the other way around. When + not set, MountPropagationNone is used. This field is beta + in 1.10. type: string name: description: This must match the Name of a Volume. @@ -210,66 +364,68 @@ spec: volume should be mounted. Defaults to "" (volume's root). type: string subPathExpr: - description: Expanded path within the volume from which - the container's volume should be mounted. Behaves similarly + description: Expanded path within the volume from which the + container's volume should be mounted. Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. Defaults to "" (volume's root). SubPathExpr and SubPath are mutually exclusive. This field is beta in 1.15. type: string - required: - - mountPath - - name - type: object - type: array volumes: description: List of Volumes to set in the podSpec. + type: array items: - description: Volume represents a named volume in a pod that - may be accessed by any container in the pod. + description: Volume represents a named volume in a pod that may + be accessed by any container in the pod. + type: object + required: + - name properties: awsElasticBlockStore: description: 'AWSElasticBlockStore represents an AWS Disk - resource that is attached to a kubelet''s host machine - and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + resource that is attached to a kubelet''s host machine and + then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: object + required: + - volumeID properties: fsType: - description: 'Filesystem type of the volume that you - want to mount. Tip: Ensure that the filesystem type - is supported by the host operating system. Examples: - "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore - TODO: how do we prevent errors in the filesystem from - compromising the machine' + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", + "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors in the filesystem from + compromising the machine' type: string partition: description: 'The partition in the volume that you want - to mount. If omitted, the default is to mount by volume - name. Examples: For volume /dev/sda1, you specify - the partition as "1". Similarly, the volume partition - for /dev/sda is "0" (or you can leave the property - empty).' - format: int32 + to mount. If omitted, the default is to mount by volume + name. Examples: For volume /dev/sda1, you specify the + partition as "1". Similarly, the volume partition for + /dev/sda is "0" (or you can leave the property empty).' type: integer + format: int32 readOnly: description: 'Specify "true" to force and set the ReadOnly - property in VolumeMounts to "true". If omitted, the - default is "false". More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + property in VolumeMounts to "true". If omitted, the + default is "false". More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' type: boolean volumeID: description: 'Unique ID of the persistent disk resource - in AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + in AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' type: string - required: - - volumeID - type: object azureDisk: description: AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod. + type: object + required: + - diskName + - diskURI properties: cachingMode: description: 'Host Caching mode: None, Read Only, Read - Write.' + Write.' type: string diskName: description: The Name of the data disk in the blob storage @@ -280,181 +436,176 @@ spec: fsType: description: Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", - "xfs", "ntfs". Implicitly inferred to be "ext4" if - unspecified. + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. type: string kind: - description: 'Expected values Shared: multiple blob - disks per storage account Dedicated: single blob - disk per storage account Managed: azure managed data - disk (only in managed availability set). defaults - to shared' + description: 'Expected values Shared: multiple blob disks + per storage account Dedicated: single blob disk per + storage account Managed: azure managed data disk (only + in managed availability set). defaults to shared' type: string readOnly: description: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. type: boolean - required: - - diskName - - diskURI - type: object azureFile: - description: AzureFile represents an Azure File Service - mount on the host and bind mount to the pod. + description: AzureFile represents an Azure File Service mount + on the host and bind mount to the pod. + type: object + required: + - secretName + - shareName properties: readOnly: description: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. type: boolean secretName: - description: the name of secret that contains Azure - Storage Account Name and Key + description: the name of secret that contains Azure Storage + Account Name and Key type: string shareName: description: Share Name type: string - required: - - secretName - - shareName - type: object cephfs: description: CephFS represents a Ceph FS mount on the host that shares a pod's lifetime + type: object + required: + - monitors properties: monitors: - description: 'Required: Monitors is a collection of - Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + description: 'Required: Monitors is a collection of Ceph + monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: array items: type: string - type: array path: description: 'Optional: Used as the mounted root, rather - than the full Ceph tree, default is /' + than the full Ceph tree, default is /' type: string readOnly: description: 'Optional: Defaults to false (read/write). - ReadOnly here will force the ReadOnly setting in VolumeMounts. - More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + ReadOnly here will force the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' type: boolean secretFile: description: 'Optional: SecretFile is the path to key - ring for User, default is /etc/ceph/user.secret More - info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + ring for User, default is /etc/ceph/user.secret More + info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' type: string secretRef: description: 'Optional: SecretRef is reference to the - authentication secret for User, default is empty. - More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + authentication secret for User, default is empty. More + info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: object properties: name: description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + TODO: Add other useful fields. apiVersion, kind, + uid?' type: string - type: object user: - description: 'Optional: User is the rados user name, - default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + description: 'Optional: User is the rados user name, default + is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' type: string - required: - - monitors - type: object cinder: - description: 'Cinder represents a cinder volume attached - and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + description: 'Cinder represents a cinder volume attached and + mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: object + required: + - volumeID properties: fsType: description: 'Filesystem type to mount. Must be a filesystem - type supported by the host operating system. Examples: - "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' type: string readOnly: description: 'Optional: Defaults to false (read/write). - ReadOnly here will force the ReadOnly setting in VolumeMounts. - More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + ReadOnly here will force the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' type: boolean secretRef: description: 'Optional: points to a secret object containing - parameters used to connect to OpenStack.' + parameters used to connect to OpenStack.' + type: object properties: name: description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + TODO: Add other useful fields. apiVersion, kind, + uid?' type: string - type: object volumeID: - description: 'volume id used to identify the volume - in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + description: 'volume id used to identify the volume in + cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' type: string - required: - - volumeID - type: object configMap: description: ConfigMap represents a configMap that should populate this volume + type: object properties: defaultMode: - description: 'Optional: mode bits to use on created - files by default. Must be a value between 0 and 0777. - Defaults to 0644. Directories within the path are - not affected by this setting. This might be in conflict - with other options that affect the file mode, like - fsGroup, and the result can be other mode bits set.' - format: int32 + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. Directories within the path are not affected + by this setting. This might be in conflict with other + options that affect the file mode, like fsGroup, and + the result can be other mode bits set.' type: integer + format: int32 items: - description: If unspecified, each key-value pair in - the Data field of the referenced ConfigMap will be - projected into the volume as a file whose name is - the key and content is the value. If specified, the - listed keys will be projected into the specified paths, - and unlisted keys will not be present. If a key is - specified which is not present in the ConfigMap, the - volume setup will error unless it is marked optional. - Paths must be relative and may not contain the '..' - path or start with '..'. + description: If unspecified, each key-value pair in the + Data field of the referenced ConfigMap will be projected + into the volume as a file whose name is the key and + content is the value. If specified, the listed keys + will be projected into the specified paths, and unlisted + keys will not be present. If a key is specified which + is not present in the ConfigMap, the volume setup will + error unless it is marked optional. Paths must be relative + and may not contain the '..' path or start with '..'. + type: array items: - description: Maps a string key to a path within a - volume. + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path properties: key: description: The key to project. type: string mode: description: 'Optional: mode bits to use on this - file, must be a value between 0 and 0777. If - not specified, the volume defaultMode will be - used. This might be in conflict with other options - that affect the file mode, like fsGroup, and - the result can be other mode bits set.' - format: int32 + file, must be a value between 0 and 0777. If not + specified, the volume defaultMode will be used. + This might be in conflict with other options that + affect the file mode, like fsGroup, and the result + can be other mode bits set.' type: integer + format: int32 path: - description: The relative path of the file to - map the key to. May not be an absolute path. - May not contain the path element '..'. May not - start with the string '..'. + description: The relative path of the file to map + the key to. May not be an absolute path. May not + contain the path element '..'. May not start with + the string '..'. type: string - required: - - key - - path - type: object - type: array name: description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' + TODO: Add other useful fields. apiVersion, kind, uid?' type: string optional: description: Specify whether the ConfigMap or its keys must be defined type: boolean - type: object csi: description: CSI (Container Storage Interface) represents storage that is handled by an external CSI driver (Alpha feature). + type: object + required: + - driver properties: driver: description: Driver is the name of the CSI driver that @@ -463,63 +614,67 @@ spec: type: string fsType: description: Filesystem type to mount. Ex. "ext4", "xfs", - "ntfs". If not provided, the empty value is passed - to the associated CSI driver which will determine - the default filesystem to apply. + "ntfs". If not provided, the empty value is passed to + the associated CSI driver which will determine the default + filesystem to apply. type: string nodePublishSecretRef: - description: NodePublishSecretRef is a reference to - the secret object containing sensitive information - to pass to the CSI driver to complete the CSI NodePublishVolume + description: NodePublishSecretRef is a reference to the + secret object containing sensitive information to pass + to the CSI driver to complete the CSI NodePublishVolume and NodeUnpublishVolume calls. This field is optional, - and may be empty if no secret is required. If the - secret object contains more than one secret, all secret - references are passed. + and may be empty if no secret is required. If the secret + object contains more than one secret, all secret references + are passed. + type: object properties: name: description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + TODO: Add other useful fields. apiVersion, kind, + uid?' type: string - type: object readOnly: - description: Specifies a read-only configuration for - the volume. Defaults to false (read/write). + description: Specifies a read-only configuration for the + volume. Defaults to false (read/write). type: boolean volumeAttributes: + description: VolumeAttributes stores driver-specific properties + that are passed to the CSI driver. Consult your driver's + documentation for supported values. + type: object additionalProperties: type: string - description: VolumeAttributes stores driver-specific - properties that are passed to the CSI driver. Consult - your driver's documentation for supported values. - type: object - required: - - driver - type: object downwardAPI: description: DownwardAPI represents downward API about the pod that should populate this volume + type: object properties: defaultMode: - description: 'Optional: mode bits to use on created - files by default. Must be a value between 0 and 0777. - Defaults to 0644. Directories within the path are - not affected by this setting. This might be in conflict - with other options that affect the file mode, like - fsGroup, and the result can be other mode bits set.' - format: int32 + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. Directories within the path are not affected + by this setting. This might be in conflict with other + options that affect the file mode, like fsGroup, and + the result can be other mode bits set.' type: integer + format: int32 items: - description: Items is a list of downward API volume - file + description: Items is a list of downward API volume file + type: array items: description: DownwardAPIVolumeFile represents information to create the file containing the pod field + type: object + required: + - path properties: fieldRef: - description: 'Required: Selects a field of the - pod: only annotations, labels, name and namespace - are supported.' + description: 'Required: Selects a field of the pod: + only annotations, labels, name and namespace are + supported.' + type: object + required: + - fieldPath properties: apiVersion: description: Version of the schema the FieldPath @@ -529,34 +684,34 @@ spec: description: Path of the field to select in the specified API version. type: string - required: - - fieldPath - type: object mode: description: 'Optional: mode bits to use on this - file, must be a value between 0 and 0777. If - not specified, the volume defaultMode will be - used. This might be in conflict with other options - that affect the file mode, like fsGroup, and - the result can be other mode bits set.' - format: int32 + file, must be a value between 0 and 0777. If not + specified, the volume defaultMode will be used. + This might be in conflict with other options that + affect the file mode, like fsGroup, and the result + can be other mode bits set.' type: integer + format: int32 path: - description: 'Required: Path is the relative - path name of the file to be created. Must not - be absolute or contain the ''..'' path. Must - be utf-8 encoded. The first item of the relative - path must not start with ''..''' + description: 'Required: Path is the relative path + name of the file to be created. Must not be absolute + or contain the ''..'' path. Must be utf-8 encoded. + The first item of the relative path must not start + with ''..''' type: string resourceFieldRef: description: 'Selects a resource of the container: - only resources limits and requests (limits.cpu, - limits.memory, requests.cpu and requests.memory) - are currently supported.' + only resources limits and requests (limits.cpu, + limits.memory, requests.cpu and requests.memory) + are currently supported.' + type: object + required: + - resource properties: containerName: - description: 'Container name: required for - volumes, optional for env vars' + description: 'Container name: required for volumes, + optional for env vars' type: string divisor: description: Specifies the output format of @@ -565,74 +720,69 @@ spec: resource: description: 'Required: resource to select' type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object emptyDir: - description: 'EmptyDir represents a temporary directory - that shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + description: 'EmptyDir represents a temporary directory that + shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: object properties: medium: description: 'What type of storage medium should back - this directory. The default is "" which means to use - the node''s default medium. Must be an empty string - (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + this directory. The default is "" which means to use + the node''s default medium. Must be an empty string + (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' type: string sizeLimit: - description: 'Total amount of local storage required - for this EmptyDir volume. The size limit is also applicable - for memory medium. The maximum usage on memory medium - EmptyDir would be the minimum value between the SizeLimit - specified here and the sum of memory limits of all - containers in a pod. The default is nil which means - that the limit is undefined. More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' + description: 'Total amount of local storage required for + this EmptyDir volume. The size limit is also applicable + for memory medium. The maximum usage on memory medium + EmptyDir would be the minimum value between the SizeLimit + specified here and the sum of memory limits of all containers + in a pod. The default is nil which means that the limit + is undefined. More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' type: string - type: object fc: - description: FC represents a Fibre Channel resource that - is attached to a kubelet's host machine and then exposed - to the pod. + description: FC represents a Fibre Channel resource that is + attached to a kubelet's host machine and then exposed to + the pod. + type: object properties: fsType: description: 'Filesystem type to mount. Must be a filesystem - type supported by the host operating system. Ex. "ext4", - "xfs", "ntfs". Implicitly inferred to be "ext4" if - unspecified. TODO: how do we prevent errors in the - filesystem from compromising the machine' + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + TODO: how do we prevent errors in the filesystem from + compromising the machine' type: string lun: description: 'Optional: FC target lun number' - format: int32 type: integer + format: int32 readOnly: description: 'Optional: Defaults to false (read/write). - ReadOnly here will force the ReadOnly setting in VolumeMounts.' + ReadOnly here will force the ReadOnly setting in VolumeMounts.' type: boolean targetWWNs: description: 'Optional: FC target worldwide names (WWNs)' + type: array items: type: string - type: array wwids: description: 'Optional: FC volume world wide identifiers - (wwids) Either wwids or combination of targetWWNs - and lun must be set, but not both simultaneously.' + (wwids) Either wwids or combination of targetWWNs and + lun must be set, but not both simultaneously.' + type: array items: type: string - type: array - type: object flexVolume: description: FlexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin. + type: object + required: + - driver properties: driver: - description: Driver is the name of the driver to use - for this volume. + description: Driver is the name of the driver to use for + this volume. type: string fsType: description: Filesystem type to mount. Must be a filesystem @@ -641,94 +791,93 @@ spec: script. type: string options: - additionalProperties: - type: string description: 'Optional: Extra command options if any.' type: object + additionalProperties: + type: string readOnly: description: 'Optional: Defaults to false (read/write). - ReadOnly here will force the ReadOnly setting in VolumeMounts.' + ReadOnly here will force the ReadOnly setting in VolumeMounts.' type: boolean secretRef: description: 'Optional: SecretRef is reference to the - secret object containing sensitive information to - pass to the plugin scripts. This may be empty if no - secret object is specified. If the secret object contains - more than one secret, all secrets are passed to the - plugin scripts.' + secret object containing sensitive information to pass + to the plugin scripts. This may be empty if no secret + object is specified. If the secret object contains more + than one secret, all secrets are passed to the plugin + scripts.' + type: object properties: name: description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + TODO: Add other useful fields. apiVersion, kind, + uid?' type: string - type: object - required: - - driver - type: object flocker: description: Flocker represents a Flocker volume attached to a kubelet's host machine. This depends on the Flocker control service being running + type: object properties: datasetName: - description: Name of the dataset stored as metadata - -> name on the dataset for Flocker should be considered + description: Name of the dataset stored as metadata -> + name on the dataset for Flocker should be considered as deprecated type: string datasetUUID: description: UUID of the dataset. This is unique identifier of a Flocker dataset type: string - type: object gcePersistentDisk: description: 'GCEPersistentDisk represents a GCE Disk resource - that is attached to a kubelet''s host machine and then - exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: object + required: + - pdName properties: fsType: - description: 'Filesystem type of the volume that you - want to mount. Tip: Ensure that the filesystem type - is supported by the host operating system. Examples: - "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk - TODO: how do we prevent errors in the filesystem from - compromising the machine' + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", + "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors in the filesystem from + compromising the machine' type: string partition: description: 'The partition in the volume that you want - to mount. If omitted, the default is to mount by volume - name. Examples: For volume /dev/sda1, you specify - the partition as "1". Similarly, the volume partition - for /dev/sda is "0" (or you can leave the property - empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' - format: int32 + to mount. If omitted, the default is to mount by volume + name. Examples: For volume /dev/sda1, you specify the + partition as "1". Similarly, the volume partition for + /dev/sda is "0" (or you can leave the property empty). + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' type: integer + format: int32 pdName: - description: 'Unique name of the PD resource in GCE. - Used to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + description: 'Unique name of the PD resource in GCE. Used + to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' type: string readOnly: - description: 'ReadOnly here will force the ReadOnly - setting in VolumeMounts. Defaults to false. More info: - https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + description: 'ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' type: boolean - required: - - pdName - type: object gitRepo: description: 'GitRepo represents a git repository at a particular - revision. DEPRECATED: GitRepo is deprecated. To provision - a container with a git repo, mount an EmptyDir into an - InitContainer that clones the repo using git, then mount - the EmptyDir into the Pod''s container.' + revision. DEPRECATED: GitRepo is deprecated. To provision + a container with a git repo, mount an EmptyDir into an InitContainer + that clones the repo using git, then mount the EmptyDir + into the Pod''s container.' + type: object + required: + - repository properties: directory: - description: Target directory name. Must not contain - or start with '..'. If '.' is supplied, the volume - directory will be the git repository. Otherwise, - if specified, the volume will contain the git repository - in the subdirectory with the given name. + description: Target directory name. Must not contain or + start with '..'. If '.' is supplied, the volume directory + will be the git repository. Otherwise, if specified, + the volume will contain the git repository in the subdirectory + with the given name. type: string repository: description: Repository URL @@ -736,57 +885,57 @@ spec: revision: description: Commit hash for the specified revision. type: string - required: - - repository - type: object glusterfs: - description: 'Glusterfs represents a Glusterfs mount on - the host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' + description: 'Glusterfs represents a Glusterfs mount on the + host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' + type: object + required: + - endpoints + - path properties: endpoints: description: 'EndpointsName is the endpoint name that - details Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + details Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' type: string path: description: 'Path is the Glusterfs volume path. More - info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' type: string readOnly: - description: 'ReadOnly here will force the Glusterfs - volume to be mounted with read-only permissions. Defaults - to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + description: 'ReadOnly here will force the Glusterfs volume + to be mounted with read-only permissions. Defaults to + false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' type: boolean - required: - - endpoints - - path - type: object hostPath: - description: 'HostPath represents a pre-existing file or - directory on the host machine that is directly exposed - to the container. This is generally used for system agents - or other privileged things that are allowed to see the - host machine. Most containers will NOT need this. More - info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath - --- TODO(jonesdl) We need to restrict who can use host - directory mounts and who can/can not mount host directories - as read/write.' + description: 'HostPath represents a pre-existing file or directory + on the host machine that is directly exposed to the container. + This is generally used for system agents or other privileged + things that are allowed to see the host machine. Most containers + will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + --- TODO(jonesdl) We need to restrict who can use host directory + mounts and who can/can not mount host directories as read/write.' + type: object + required: + - path properties: path: - description: 'Path of the directory on the host. If - the path is a symlink, it will follow the link to - the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + description: 'Path of the directory on the host. If the + path is a symlink, it will follow the link to the real + path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' type: string type: description: 'Type for HostPath Volume Defaults to "" - More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' type: string - required: - - path - type: object iscsi: description: 'ISCSI represents an ISCSI Disk resource that - is attached to a kubelet''s host machine and then exposed - to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + type: object + required: + - iqn + - lun + - targetPortal properties: chapAuthDiscovery: description: whether support iSCSI Discovery CHAP authentication @@ -795,38 +944,38 @@ spec: description: whether support iSCSI Session CHAP authentication type: boolean fsType: - description: 'Filesystem type of the volume that you - want to mount. Tip: Ensure that the filesystem type - is supported by the host operating system. Examples: - "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi - TODO: how do we prevent errors in the filesystem from - compromising the machine' + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", + "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors in the filesystem from + compromising the machine' type: string initiatorName: description: Custom iSCSI Initiator Name. If initiatorName is specified with iscsiInterface simultaneously, new - iSCSI interface : will - be created for the connection. + iSCSI interface : will be + created for the connection. type: string iqn: description: Target iSCSI Qualified Name. type: string iscsiInterface: - description: iSCSI Interface Name that uses an iSCSI - transport. Defaults to 'default' (tcp). + description: iSCSI Interface Name that uses an iSCSI transport. + Defaults to 'default' (tcp). type: string lun: description: iSCSI Target Lun number. - format: int32 type: integer + format: int32 portals: - description: iSCSI Target Portal List. The portal is - either an IP or ip_addr:port if the port is other - than default (typically TCP ports 860 and 3260). + description: iSCSI Target Portal List. The portal is either + an IP or ip_addr:port if the port is other than default + (typically TCP ports 860 and 3260). + type: array items: type: string - type: array readOnly: description: ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. @@ -834,86 +983,82 @@ spec: secretRef: description: CHAP Secret for iSCSI target and initiator authentication + type: object properties: name: description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + TODO: Add other useful fields. apiVersion, kind, + uid?' type: string - type: object targetPortal: description: iSCSI Target Portal. The Portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260). type: string - required: - - iqn - - lun - - targetPortal - type: object name: description: 'Volume''s name. Must be a DNS_LABEL and unique - within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' type: string nfs: description: 'NFS represents an NFS mount on the host that - shares a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + shares a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: object + required: + - path + - server properties: path: description: 'Path that is exported by the NFS server. - More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' type: string readOnly: description: 'ReadOnly here will force the NFS export - to be mounted with read-only permissions. Defaults - to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + to be mounted with read-only permissions. Defaults to + false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' type: boolean server: description: 'Server is the hostname or IP address of - the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' type: string - required: - - path - - server - type: object persistentVolumeClaim: description: 'PersistentVolumeClaimVolumeSource represents - a reference to a PersistentVolumeClaim in the same namespace. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + a reference to a PersistentVolumeClaim in the same namespace. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: object + required: + - claimName properties: claimName: description: 'ClaimName is the name of a PersistentVolumeClaim - in the same namespace as the pod using this volume. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + in the same namespace as the pod using this volume. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' type: string readOnly: description: Will force the ReadOnly setting in VolumeMounts. Default false. type: boolean - required: - - claimName - type: object photonPersistentDisk: description: PhotonPersistentDisk represents a PhotonController - persistent disk attached and mounted on kubelets host - machine + persistent disk attached and mounted on kubelets host machine + type: object + required: + - pdID properties: fsType: description: Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", - "xfs", "ntfs". Implicitly inferred to be "ext4" if - unspecified. + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. type: string pdID: description: ID that identifies Photon Controller persistent disk type: string - required: - - pdID - type: object portworxVolume: - description: PortworxVolume represents a portworx volume - attached and mounted on kubelets host machine + description: PortworxVolume represents a portworx volume attached + and mounted on kubelets host machine + type: object + required: + - volumeID properties: fsType: description: FSType represents the filesystem type to @@ -926,105 +1071,113 @@ spec: here will force the ReadOnly setting in VolumeMounts. type: boolean volumeID: - description: VolumeID uniquely identifies a Portworx - volume + description: VolumeID uniquely identifies a Portworx volume type: string - required: - - volumeID - type: object projected: description: Items for all in one resources secrets, configmaps, and downward API + type: object + required: + - sources properties: defaultMode: description: Mode bits to use on created files by default. Must be a value between 0 and 0777. Directories within the path are not affected by this setting. This might - be in conflict with other options that affect the - file mode, like fsGroup, and the result can be other - mode bits set. - format: int32 + be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode + bits set. type: integer + format: int32 sources: description: list of volume projections + type: array items: description: Projection that may be projected along with other supported volume types + type: object properties: configMap: description: information about the configMap data to project + type: object properties: items: description: If unspecified, each key-value - pair in the Data field of the referenced - ConfigMap will be projected into the volume - as a file whose name is the key and content - is the value. If specified, the listed keys - will be projected into the specified paths, - and unlisted keys will not be present. If - a key is specified which is not present - in the ConfigMap, the volume setup will - error unless it is marked optional. Paths - must be relative and may not contain the - '..' path or start with '..'. + pair in the Data field of the referenced ConfigMap + will be projected into the volume as a file + whose name is the key and content is the value. + If specified, the listed keys will be projected + into the specified paths, and unlisted keys + will not be present. If a key is specified + which is not present in the ConfigMap, the + volume setup will error unless it is marked + optional. Paths must be relative and may not + contain the '..' path or start with '..'. + type: array items: - description: Maps a string key to a path - within a volume. + description: Maps a string key to a path within + a volume. + type: object + required: + - key + - path properties: key: description: The key to project. type: string mode: - description: 'Optional: mode bits to - use on this file, must be a value - between 0 and 0777. If not specified, - the volume defaultMode will be used. - This might be in conflict with other - options that affect the file mode, - like fsGroup, and the result can be - other mode bits set.' - format: int32 + description: 'Optional: mode bits to use + on this file, must be a value between + 0 and 0777. If not specified, the volume + defaultMode will be used. This might + be in conflict with other options that + affect the file mode, like fsGroup, + and the result can be other mode bits + set.' type: integer + format: int32 path: description: The relative path of the - file to map the key to. May not be - an absolute path. May not contain - the path element '..'. May not start - with the string '..'. + file to map the key to. May not be an + absolute path. May not contain the path + element '..'. May not start with the + string '..'. type: string - required: - - key - - path - type: object - type: array name: description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' type: string optional: - description: Specify whether the ConfigMap - or its keys must be defined + description: Specify whether the ConfigMap or + its keys must be defined type: boolean - type: object downwardAPI: - description: information about the downwardAPI - data to project + description: information about the downwardAPI data + to project + type: object properties: items: description: Items is a list of DownwardAPIVolume file + type: array items: description: DownwardAPIVolumeFile represents information to create the file containing the pod field + type: object + required: + - path properties: fieldRef: description: 'Required: Selects a field - of the pod: only annotations, labels, - name and namespace are supported.' + of the pod: only annotations, labels, + name and namespace are supported.' + type: object + required: + - fieldPath properties: apiVersion: description: Version of the schema @@ -1035,39 +1188,38 @@ spec: description: Path of the field to select in the specified API version. type: string - required: - - fieldPath - type: object mode: - description: 'Optional: mode bits to - use on this file, must be a value - between 0 and 0777. If not specified, - the volume defaultMode will be used. - This might be in conflict with other - options that affect the file mode, - like fsGroup, and the result can be - other mode bits set.' - format: int32 + description: 'Optional: mode bits to use + on this file, must be a value between + 0 and 0777. If not specified, the volume + defaultMode will be used. This might + be in conflict with other options that + affect the file mode, like fsGroup, + and the result can be other mode bits + set.' type: integer + format: int32 path: - description: 'Required: Path is the - relative path name of the file to - be created. Must not be absolute or - contain the ''..'' path. Must be utf-8 - encoded. The first item of the relative - path must not start with ''..''' + description: 'Required: Path is the relative + path name of the file to be created. + Must not be absolute or contain the + ''..'' path. Must be utf-8 encoded. + The first item of the relative path + must not start with ''..''' type: string resourceFieldRef: - description: 'Selects a resource of - the container: only resources limits - and requests (limits.cpu, limits.memory, - requests.cpu and requests.memory) - are currently supported.' + description: 'Selects a resource of the + container: only resources limits and + requests (limits.cpu, limits.memory, + requests.cpu and requests.memory) are + currently supported.' + type: object + required: + - resource properties: containerName: description: 'Container name: required - for volumes, optional for env - vars' + for volumes, optional for env vars' type: string divisor: description: Specifies the output @@ -1075,78 +1227,72 @@ spec: defaults to "1" type: string resource: - description: 'Required: resource - to select' + description: 'Required: resource to + select' type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object secret: - description: information about the secret data - to project + description: information about the secret data to + project + type: object properties: items: description: If unspecified, each key-value - pair in the Data field of the referenced - Secret will be projected into the volume - as a file whose name is the key and content - is the value. If specified, the listed keys - will be projected into the specified paths, - and unlisted keys will not be present. If - a key is specified which is not present - in the Secret, the volume setup will error - unless it is marked optional. Paths must - be relative and may not contain the '..' - path or start with '..'. + pair in the Data field of the referenced Secret + will be projected into the volume as a file + whose name is the key and content is the value. + If specified, the listed keys will be projected + into the specified paths, and unlisted keys + will not be present. If a key is specified + which is not present in the Secret, the volume + setup will error unless it is marked optional. + Paths must be relative and may not contain + the '..' path or start with '..'. + type: array items: - description: Maps a string key to a path - within a volume. + description: Maps a string key to a path within + a volume. + type: object + required: + - key + - path properties: key: description: The key to project. type: string mode: - description: 'Optional: mode bits to - use on this file, must be a value - between 0 and 0777. If not specified, - the volume defaultMode will be used. - This might be in conflict with other - options that affect the file mode, - like fsGroup, and the result can be - other mode bits set.' - format: int32 + description: 'Optional: mode bits to use + on this file, must be a value between + 0 and 0777. If not specified, the volume + defaultMode will be used. This might + be in conflict with other options that + affect the file mode, like fsGroup, + and the result can be other mode bits + set.' type: integer + format: int32 path: description: The relative path of the - file to map the key to. May not be - an absolute path. May not contain - the path element '..'. May not start - with the string '..'. + file to map the key to. May not be an + absolute path. May not contain the path + element '..'. May not start with the + string '..'. type: string - required: - - key - - path - type: object - type: array name: description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' type: string optional: - description: Specify whether the Secret or - its key must be defined + description: Specify whether the Secret or its + key must be defined type: boolean - type: object serviceAccountToken: description: information about the serviceAccountToken data to project + type: object + required: + - path properties: audience: description: Audience is the intended audience @@ -1162,29 +1308,24 @@ spec: token. As the token approaches expiration, the kubelet volume plugin will proactively rotate the service account token. The kubelet - will start trying to rotate the token if - the token is older than 80 percent of its - time to live or if the token is older than - 24 hours.Defaults to 1 hour and must be - at least 10 minutes. - format: int64 + will start trying to rotate the token if the + token is older than 80 percent of its time + to live or if the token is older than 24 hours.Defaults + to 1 hour and must be at least 10 minutes. type: integer + format: int64 path: - description: Path is the path relative to - the mount point of the file to project the - token into. + description: Path is the path relative to the + mount point of the file to project the token + into. type: string - required: - - path - type: object - type: object - type: array - required: - - sources - type: object quobyte: description: Quobyte represents a Quobyte mount on the host that shares a pod's lifetime + type: object + required: + - registry + - volume properties: group: description: Group to map volume access to Default is @@ -1192,18 +1333,18 @@ spec: type: string readOnly: description: ReadOnly here will force the Quobyte volume - to be mounted with read-only permissions. Defaults - to false. + to be mounted with read-only permissions. Defaults to + false. type: boolean registry: description: Registry represents a single or multiple - Quobyte Registry services specified as a string as - host:port pair (multiple entries are separated with - commas) which acts as the central registry for volumes + Quobyte Registry services specified as a string as host:port + pair (multiple entries are separated with commas) which + acts as the central registry for volumes type: string tenant: - description: Tenant owning the given Quobyte volume - in the Backend Used with dynamically provisioned Quobyte + description: Tenant owning the given Quobyte volume in + the Backend Used with dynamically provisioned Quobyte volumes, value is set by the plugin type: string user: @@ -1214,68 +1355,67 @@ spec: description: Volume is a string that references an already created Quobyte volume by name. type: string - required: - - registry - - volume - type: object rbd: - description: 'RBD represents a Rados Block Device mount - on the host that shares a pod''s lifetime. More info: - https://examples.k8s.io/volumes/rbd/README.md' + description: 'RBD represents a Rados Block Device mount on + the host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md' + type: object + required: + - image + - monitors properties: fsType: - description: 'Filesystem type of the volume that you - want to mount. Tip: Ensure that the filesystem type - is supported by the host operating system. Examples: - "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd - TODO: how do we prevent errors in the filesystem from - compromising the machine' + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", + "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors in the filesystem from + compromising the machine' type: string image: description: 'The rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' type: string keyring: description: 'Keyring is the path to key ring for RBDUser. - Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' type: string monitors: description: 'A collection of Ceph monitors. More info: - https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: array items: type: string - type: array pool: description: 'The rados pool name. Default is rbd. More - info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' type: string readOnly: - description: 'ReadOnly here will force the ReadOnly - setting in VolumeMounts. Defaults to false. More info: - https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + description: 'ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' type: boolean secretRef: description: 'SecretRef is name of the authentication - secret for RBDUser. If provided overrides keyring. - Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + secret for RBDUser. If provided overrides keyring. Default + is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: object properties: name: description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + TODO: Add other useful fields. apiVersion, kind, + uid?' type: string - type: object user: - description: 'The rados user name. Default is admin. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + description: 'The rados user name. Default is admin. More + info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' type: string - required: - - image - - monitors - type: object scaleIO: description: ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes. + type: object + required: + - gateway + - secretRef + - system properties: fsType: description: Filesystem type to mount. Must be a filesystem @@ -1294,16 +1434,16 @@ spec: here will force the ReadOnly setting in VolumeMounts. type: boolean secretRef: - description: SecretRef references to the secret for - ScaleIO user and other sensitive information. If this - is not provided, Login operation will fail. + description: SecretRef references to the secret for ScaleIO + user and other sensitive information. If this is not + provided, Login operation will fail. + type: object properties: name: description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + TODO: Add other useful fields. apiVersion, kind, + uid?' type: string - type: object sslEnabled: description: Flag to enable/disable SSL communication with Gateway, default false @@ -1322,84 +1462,76 @@ spec: in ScaleIO. type: string volumeName: - description: The name of a volume already created in - the ScaleIO system that is associated with this volume - source. + description: The name of a volume already created in the + ScaleIO system that is associated with this volume source. type: string - required: - - gateway - - secretRef - - system - type: object secret: description: 'Secret represents a secret that should populate - this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: object properties: defaultMode: - description: 'Optional: mode bits to use on created - files by default. Must be a value between 0 and 0777. - Defaults to 0644. Directories within the path are - not affected by this setting. This might be in conflict - with other options that affect the file mode, like - fsGroup, and the result can be other mode bits set.' - format: int32 + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. Directories within the path are not affected + by this setting. This might be in conflict with other + options that affect the file mode, like fsGroup, and + the result can be other mode bits set.' type: integer + format: int32 items: - description: If unspecified, each key-value pair in - the Data field of the referenced Secret will be projected + description: If unspecified, each key-value pair in the + Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the Secret, the volume setup will - error unless it is marked optional. Paths must be - relative and may not contain the '..' path or start - with '..'. + error unless it is marked optional. Paths must be relative + and may not contain the '..' path or start with '..'. + type: array items: - description: Maps a string key to a path within a - volume. + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path properties: key: description: The key to project. type: string mode: description: 'Optional: mode bits to use on this - file, must be a value between 0 and 0777. If - not specified, the volume defaultMode will be - used. This might be in conflict with other options - that affect the file mode, like fsGroup, and - the result can be other mode bits set.' - format: int32 + file, must be a value between 0 and 0777. If not + specified, the volume defaultMode will be used. + This might be in conflict with other options that + affect the file mode, like fsGroup, and the result + can be other mode bits set.' type: integer + format: int32 path: - description: The relative path of the file to - map the key to. May not be an absolute path. - May not contain the path element '..'. May not - start with the string '..'. + description: The relative path of the file to map + the key to. May not be an absolute path. May not + contain the path element '..'. May not start with + the string '..'. type: string - required: - - key - - path - type: object - type: array optional: - description: Specify whether the Secret or its keys - must be defined + description: Specify whether the Secret or its keys must + be defined type: boolean secretName: description: 'Name of the secret in the pod''s namespace - to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' type: string - type: object storageos: description: StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes. + type: object properties: fsType: description: Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", - "xfs", "ntfs". Implicitly inferred to be "ext4" if - unspecified. + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. type: string readOnly: description: Defaults to false (read/write). ReadOnly @@ -1409,55 +1541,237 @@ spec: description: SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted. + type: object properties: name: description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + TODO: Add other useful fields. apiVersion, kind, + uid?' type: string - type: object volumeName: description: VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace. type: string volumeNamespace: - description: VolumeNamespace specifies the scope of - the volume within StorageOS. If no namespace is specified + description: VolumeNamespace specifies the scope of the + volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows - the Kubernetes name scoping to be mirrored within - StorageOS for tighter integration. Set VolumeName - to any name to override the default behaviour. Set - to "default" if you are not using namespaces within - StorageOS. Namespaces that do not pre-exist within - StorageOS will be created. + the Kubernetes name scoping to be mirrored within StorageOS + for tighter integration. Set VolumeName to any name + to override the default behaviour. Set to "default" + if you are not using namespaces within StorageOS. Namespaces + that do not pre-exist within StorageOS will be created. type: string - type: object vsphereVolume: description: VsphereVolume represents a vSphere volume attached and mounted on kubelets host machine + type: object + required: + - volumePath properties: fsType: description: Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", - "xfs", "ntfs". Implicitly inferred to be "ext4" if - unspecified. + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. type: string storagePolicyID: - description: Storage Policy Based Management (SPBM) - profile ID associated with the StoragePolicyName. + description: Storage Policy Based Management (SPBM) profile + ID associated with the StoragePolicyName. type: string storagePolicyName: - description: Storage Policy Based Management (SPBM) - profile name. + description: Storage Policy Based Management (SPBM) profile + name. type: string volumePath: description: Path that identifies vSphere volume vmdk type: string - required: - - volumePath - type: object - required: - - name + installPlanApproval: + description: Approval is the user approval policy for an InstallPlan. + type: string + name: + type: string + source: + type: string + sourceNamespace: + type: string + startingCSV: + type: string + status: + type: object + required: + - lastUpdated + properties: + catalogHealth: + description: CatalogHealth contains the Subscription's view of its relevant + CatalogSources' status. It is used to determine SubscriptionStatusConditions + related to CatalogSources. + type: array + items: + description: SubscriptionCatalogHealth describes the health of a CatalogSource + the Subscription knows about. + type: object + required: + - catalogSourceRef + - healthy + - lastUpdated + properties: + catalogSourceRef: + description: CatalogSourceRef is a reference to a CatalogSource. type: object - type: array + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead + of an entire object, this string should contain a valid + JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen + only to have some well-defined way of referencing a part + of an object. TODO: this design is not final and this field + is subject to change in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference + is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + healthy: + description: Healthy is true if the CatalogSource is healthy; + false otherwise. + type: boolean + lastUpdated: + description: LastUpdated represents the last time that the CatalogSourceHealth + changed + type: string + format: date-time + conditions: + description: Conditions is a list of the latest available observations + about a Subscription's current state. + type: array + items: + description: SubscriptionCondition represents the latest available + observations of a Subscription's state. + type: object + required: + - status + - type + properties: + lastHeartbeatTime: + description: LastHeartbeatTime is the last time we got an update + on a given condition + type: string + format: date-time + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transit from one status to another + type: string + format: date-time + message: + description: Message is a human-readable message indicating details + about last transition. + type: string + reason: + description: Reason is a one-word CamelCase reason for the condition's + last transition. + type: string + status: + description: Status is the status of the condition, one of True, + False, Unknown. + type: string + type: + description: Type is the type of Subscription condition. + type: string + currentCSV: + description: CurrentCSV is the CSV the Subscription is progressing to. + type: string + installPlanRef: + description: InstallPlanRef is a reference to the latest InstallPlan + that contains the Subscription's current CSV. + type: object + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + installedCSV: + description: InstalledCSV is the CSV currently installed by the Subscription. + type: string + installplan: + description: 'Install is a reference to the latest InstallPlan generated + for the Subscription. DEPRECATED: InstallPlanRef' + type: object + required: + - apiVersion + - kind + - name + - uuid + properties: + apiVersion: + type: string + kind: + type: string + name: + type: string + uuid: + description: UID is a type that holds unique ID values, including + UUIDs. Because we don't ONLY use UUIDs, this is an alias to string. Being + a type captures intent and helps make sure that UIDs and names + do not get conflated. + type: string + lastUpdated: + description: LastUpdated represents the last time that the Subscription + status was updated. + type: string + format: date-time + reason: + description: Reason is the reason the Subscription was transitioned + to its current state. + type: string + state: + description: State represents the current state of the Subscription + type: string diff --git a/deploy/chart/templates/0000_50_olm_06-catalogsource.crd.yaml b/deploy/chart/templates/0000_50_olm_06-catalogsource.crd.yaml index 688fcc5736..470739bb87 100644 --- a/deploy/chart/templates/0000_50_olm_06-catalogsource.crd.yaml +++ b/deploy/chart/templates/0000_50_olm_06-catalogsource.crd.yaml @@ -39,90 +39,130 @@ spec: type: date JSONPath: .metadata.creationTimestamp subresources: - # status enables the status subresource. + # status enables the status subresource. status: {} validation: openAPIV3Schema: - description: A source configured to find packages and updates. + description: CatalogSource is a repository of CSVs, CRDs, and operator packages. + type: object + required: + - metadata + - spec properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object spec: type: object - description: Spec for a catalog source. required: - sourceType properties: - sourceType: + address: + description: 'Address is a host that OLM can use to connect to a pre-existing + registry. Format: : Only used when SourceType + = SourceTypeGrpc. Ignored when the Image field is set.' type: string - description: The type of the source. `configmap` is the new name for `internal` - enum: - - internal # deprecated - - configmap - - grpc - configMap: + description: ConfigMap is the name of the ConfigMap to be used to back + a configmap-server registry. Only used when SourceType = SourceTypeConfigmap + or SourceTypeInternal. type: string - description: The name of a ConfigMap that holds the entries for an in-memory catalog. - - address: + description: type: string - description: An optional address. When set, directs OLM to connect to use a pre-existing registry server at this address. - - image: - type: string - description: An image that serves a grpc registry. Only valid for `grpc` sourceType. If both image and address are set, OLM does not use the address field. - displayName: + description: Metadata + type: string + icon: + type: object + required: + - base64data + - mediatype + properties: + base64data: + type: string + mediatype: + type: string + image: + description: Image is an operator-registry container image to instantiate + a registry-server with. Only used when SourceType = SourceTypeGrpc. + If present, the address field is ignored. type: string - description: Pretty name for display - publisher: type: string - description: The name of an entity that publishes this catalog - secrets: + description: Secrets represent set of secrets that can be used to access + the contents of the catalog. It is best to keep this list small, since + each will need to be tried for every catalog entry. type: array - description: A set of secrets that can be used to access the contents of the catalog. It is best to keep this list small, since each will need to be tried for every catalog entry. items: type: string - description: A name of a secret in the namespace where the CatalogSource is defined. + sourceType: + description: SourceType is the type of source + type: string status: type: object - description: The status of the CatalogSource properties: configMapReference: type: object - description: If sourceType is `internal` or `configmap`, then this holds a reference to the configmap associated with this CatalogSource. + required: + - name + - namespace properties: + lastUpdateTime: + type: string + format: date-time name: type: string - description: name of the configmap namespace: type: string - description: namespace of the configmap resourceVersion: type: string - description: resourceVersion of the configmap uid: + description: UID is a type that holds unique ID values, including + UUIDs. Because we don't ONLY use UUIDs, this is an alias to string. Being + a type captures intent and helps make sure that UIDs and names + do not get conflated. + type: string + connectionState: + type: object + required: + - lastObservedState + properties: + address: type: string - description: uid of the configmap + lastConnect: + type: string + format: date-time + lastObservedState: + type: string + message: + description: A human readable message indicating details about why the + ClusterServiceVersion is in this condition. + type: string + reason: + description: Reason is the reason the Subscription was transitioned + to its current state. + type: string registryService: type: object properties: + createdAt: + type: string + format: date-time + port: + type: string protocol: type: string - description: protocol of the registry service - enum: - - grpc serviceName: type: string - description: name of the registry service serviceNamespace: type: string - description: namespace of the registry service - port: - type: string - description: port of the registry service - lastSync: - type: string - description: the last time the catalog was updated. If this time is less than the last updated time on the object, the catalog will be re-cached. - diff --git a/deploy/chart/templates/0000_50_olm_10-operatorgroup.crd.yaml b/deploy/chart/templates/0000_50_olm_10-operatorgroup.crd.yaml index d0b44a4d93..1fac9e1a8e 100644 --- a/deploy/chart/templates/0000_50_olm_10-operatorgroup.crd.yaml +++ b/deploy/chart/templates/0000_50_olm_10-operatorgroup.crd.yaml @@ -6,94 +6,154 @@ spec: group: operators.coreos.com version: v1 versions: - - name: v1 - served: true - storage: true - - name: v1alpha2 - served: true - storage: false + - name: v1 + served: true + storage: true names: plural: operatorgroups singular: operatorgroup kind: OperatorGroup listKind: OperatorGroupList shortNames: - - og + - og categories: - - olm + - olm scope: Namespaced subresources: # status enables the status subresource. status: {} - validation: - openAPIV3Schema: - description: A grouping of namespaces for usage with an operator. + "validation": + "openAPIV3Schema": + description: OperatorGroup is the unit of multitenancy for OLM managed operators. + It constrains the installation of operators in its namespace to a specified + set of target namespaces. + type: object + required: + - metadata properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object spec: + description: OperatorGroupSpec is the spec for an OperatorGroup resource. type: object - description: Spec for an OperatorGroup. properties: selector: + description: Selector selects the OperatorGroup's target namespaces. type: object - description: Optional label selector to find resources associated with or managed by the operator - anyOf: - - properties: - matchLabels: - type: object - description: Label key:value pairs to match directly - required: - - matchLabels - - properties: - matchExpressions: - type: array - description: A set of expressions to match against the resource. - items: - allOf: - - type: object - required: - - key - - operator - - values - properties: - key: - type: string - description: the key to match - operator: - type: string - description: the operator for the expression - enum: - - In - - NotIn - - Exists - - DoesNotExist - values: - type: array - description: set of values for the expression - required: - - matchExpressions - targetNamespaces: - type: array - description: Optional list of target namespaces. If set, OLM will ignore selector. - items: - type: string - pattern: ^\S+$ + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains + values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: operator represents a key's relationship to a + set of values. Valid operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string values. If the operator + is In or NotIn, the values array must be non-empty. If the + operator is Exists or DoesNotExist, the values array must + be empty. This array is replaced during a strategic merge + patch. + type: array + items: + type: string + matchLabels: + description: matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator is + "In", and the values array contains only "value". The requirements + are ANDed. + type: object + additionalProperties: + type: string serviceAccountName: + description: ServiceAccountName is the admin specified service account + which will be used to deploy operator(s) in this operator group. type: string staticProvidedAPIs: + description: Static tells OLM not to update the OperatorGroup's providedAPIs + annotation type: boolean - description: If true, OLM will not modify the OperatorGroup's providedAPIs annotation. + targetNamespaces: + description: TargetNamespaces is an explicit set of namespaces to target. + If it is set, Selector is ignored. + type: array + items: + type: string status: + description: OperatorGroupStatus is the status for an OperatorGroupResource. type: object - description: The status of the OperatorGroup. + required: + - lastUpdated properties: lastUpdated: - format: date-time + description: LastUpdated is a timestamp of the last time the OperatorGroup's + status was Updated. type: string + format: date-time namespaces: + description: Namespaces is the set of target namespaces for the OperatorGroup. + type: array items: type: string - type: array - required: - - lastUpdated - required: - - metadata + serviceAccountRef: + description: ServiceAccountRef references the service account object + specified. + type: object + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string diff --git a/manifests/0000_50_olm_03-clusterserviceversion.crd.yaml b/manifests/0000_50_olm_03-clusterserviceversion.crd.yaml index 613893753f..610a42a24e 100644 --- a/manifests/0000_50_olm_03-clusterserviceversion.crd.yaml +++ b/manifests/0000_50_olm_03-clusterserviceversion.crd.yaml @@ -4,7 +4,8 @@ metadata: name: clusterserviceversions.operators.coreos.com annotations: displayName: Operator Version - description: Represents an Operator that should be running on the cluster, including requirements and install strategy. + description: Represents an Operator that should be running on the cluster, including + requirements and install strategy. spec: names: plural: clusterserviceversions @@ -35,732 +36,8400 @@ spec: group: operators.coreos.com version: v1alpha1 versions: - - name: v1alpha1 - served: true - storage: true + - name: v1alpha1 + served: true + storage: true scope: Namespaced + preserveUnknownFields: false subresources: # status enables the status subresource. status: {} validation: openAPIV3Schema: - description: Represents an Operator that should be running on the cluster, including requirements and install strategy. + description: ClusterServiceVersion is a Custom Resource of type `ClusterServiceVersionSpec`. + type: object + required: + - metadata + - spec properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object spec: + description: ClusterServiceVersionSpec declarations tell OLM how to install + an operator that can manage apps for a given version. type: object - description: Spec for a ClusterServiceVersion required: - displayName - install properties: - displayName: - type: string - description: Human readable name of the application that will be displayed in the ALM UI - - description: - type: string - description: Human readable description of what the application does - - minKubeVersion: - type: string - description: Minimum kubernetes version requirement on the server to deploy operator - pattern: ^\bv?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(-(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*)?(\+[0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*)?$ - - keywords: - type: array - description: List of keywords which will be used to discover and categorize app types - items: - type: string - - maintainers: - type: array - description: Those responsible for the creation of this specific app type - items: - type: object - description: Information for a single maintainer - required: - - name - - email - properties: - name: - type: string - description: Maintainer's name - email: - type: string - description: Maintainer's email address - format: email - optionalProperties: - type: string - description: "Any additional key-value metadata you wish to expose about the maintainer, e.g. github: " - - links: - type: array - description: Interesting links to find more information about the project, such as marketing page, documentation, or github page - items: - type: object - description: A single link to describe one aspect of the project - required: - - name - - url - properties: - name: - type: string - description: Name of the link type, e.g. homepage or github url - url: - type: string - description: URL to which the link should point - format: uri - - icon: - type: array - description: Icon which should be rendered with the application information - items: - type: object - required: - - base64data - - mediatype - properties: - base64data: - type: string - description: Base64 binary representation of the icon image - mediatype: - type: string - description: Mediatype for the binary data specified in the base64data property - enum: - - image/gif - - image/jpeg - - image/png - - image/svg+xml - version: - type: string - description: Version string, recommended that users use semantic versioning - pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(-(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*)?(\+[0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*)?$ - - replaces: - type: string - description: Name of the ClusterServiceVersion custom resource that this version replaces - - maturity: - type: string - description: What level of maturity the software has achieved at this version - enum: - - planning - - pre-alpha - - alpha - - beta - - stable - - mature - - inactive - - deprecated - labels: - type: object - description: Labels that will be applied to associated resources created by the operator. - selector: + annotations: + description: Annotations is an unstructured key value map stored with + a resource that may be set by external tools to store and retrieve + arbitrary metadata. type: object - description: Label selector to find resources associated with or managed by the operator - properties: - matchLabels: - type: object - description: Label key:value pairs to match directly - matchExpressions: - type: array - description: A set of expressions to match against the resource. - items: - allOf: - - type: object - required: - - key - - operator - - values - properties: - key: - type: string - description: the key to match - operator: - type: string - description: the operator for the expression - enum: - - In - - NotIn - - Exists - - DoesNotExist - values: - type: array - description: set of values for the expression - nativeAPIs: - type: array - description: What resources are required by the Operator, but must be provided by the underlying cluster and not as an extension. - items: - type: object - required: - - group - - version - - kind - properties: - group: - type: string - description: Group of the API resource - version: - type: string - description: Version of the API resource - kind: - type: string - description: Kind of the API resource + additionalProperties: + type: string apiservicedefinitions: + description: APIServiceDefinitions declares all of the extension apis + managed or required by an operator being ran by ClusterServiceVersion. type: object properties: owned: type: array - description: What resources this operator is responsible for managing. No two running operators should manage the same resource. items: + description: APIServiceDescription provides details to OLM about + apis provided via aggregation type: object required: - group - - version - kind - name - - deploymentName - - displayName - - description + - version properties: - group: - type: string - description: Group of the APIService (e.g. app.coreos.com) - name: + actionDescriptors: + type: array + items: + description: ActionDescriptor describes a declarative action + that can be performed on a custom resource instance + type: object + required: + - path + properties: + description: + type: string + displayName: + type: string + path: + type: string + value: + description: RawMessage is a raw encoded JSON value. + It implements Marshaler and Unmarshaler and can be + used to delay JSON decoding or precompute a JSON encoding. + type: string + format: byte + x-descriptors: + type: array + items: + type: string + containerPort: + type: integer + format: int32 + deploymentName: type: string - description: The plural name for the APIService provided - version: + description: type: string - description: The version field of the APIService - kind: + displayName: type: string - description: The kind field of the APIService - deploymentName: + group: type: string - description: Name of the extension api-server's deployment - containerPort: - type: number - description: Port where the extension api-server serves TLS traffic - displayName: + kind: type: string - description: A human-readable name for the APIService. - description: + name: type: string - description: A description of the APIService resources: type: array items: + description: APIResourceReference is a Kubernetes resource + type used by a custom resource type: object - description: A list of resources that should be displayed for the APIService required: - kind + - name - version properties: + kind: + type: string name: type: string - description: If a APIService, the fully qualified name of the APIService (e.g. my-resource-v1.app.coreos.com) version: type: string - description: The version of the resource kind - kind: - type: string - description: The kind field of the resource kind - statusDescriptors: + specDescriptors: type: array items: + description: SpecDescriptor describes a field in a spec + block of a CRD so that OLM can consume it type: object - description: A spec for a field in the status block of the API resource required: - path - - displayName - - description properties: - path: + description: type: string - description: A jsonpath indexing into the status object on the API resource where the the status value can be found. displayName: type: string - description: A human-readable name for the status entry. - description: - type: string - description: A description of the status entry. - x-descriptors: - type: array - description: A list of descriptors for the status entry that indicate the meaning of the field. - items: - type: string - value: - description: If present, the value of this status is the same for all instances of the API resource and can be found here instead of on the API resource. - specDescriptors: - type: array - items: - type: object - description: A spec for a field in the spec block of the APIService resource. - required: - - path - - displayName - - description - properties: path: type: string - description: A jsonpath indexing into the spec object on the API resource where the the spec value can be found. - displayName: - type: string - description: A human-readable name for the spec entry. - description: + value: + description: RawMessage is a raw encoded JSON value. + It implements Marshaler and Unmarshaler and can be + used to delay JSON decoding or precompute a JSON encoding. type: string - description: A description of the spec entry. + format: byte x-descriptors: type: array - description: A list of descriptors for the spec entry that indicate the meaning of the field. items: type: string - value: - description: If present, the value of this spec is the same for all instances of the API Resource and can be found here instead of on the API resource. - actionDescriptors: + statusDescriptors: type: array items: + description: StatusDescriptor describes a field in a status + block of a CRD so that OLM can consume it type: object - description: A spec for actions that can be performed on instances of the API resource required: - path - - displayName - - description properties: - path: + description: type: string - description: A jsonpath indexing into the spec object on the API resource where the the spec value can be found. displayName: type: string - description: A human-readable name for the action. - description: + path: type: string - description: A description of the action. + value: + description: RawMessage is a raw encoded JSON value. + It implements Marshaler and Unmarshaler and can be + used to delay JSON decoding or precompute a JSON encoding. + type: string + format: byte x-descriptors: type: array - description: A list of descriptors for the action that indicate the meaning of the action. items: type: string - value: - description: If present, the value of this action is the same for all instances of the API resource and can be found here instead of on the API resource. + version: + type: string required: type: array - description: What resources this operator is responsible for managing. No two running operators should manage the same resource. items: + description: APIServiceDescription provides details to OLM about + apis provided via aggregation type: object required: - group - - version - kind - name - - displayName - - description + - version properties: - group: - type: string - description: Group of the APIService (e.g. app.coreos.com) - version: - type: string - description: The version field of the APIService - kind: - type: string - description: The kind field of the APIService - name: - type: string - description: The plural name for the APIService provided - deploymentName: - type: string - description: Name of the extension api-server's deployment - containerPort: - type: number - description: Port where the extension api-server serves TLS traffic - displayName: - type: string - description: A human-readable name for the APIService. - description: - type: string - description: A description of the APIService - statusDescriptors: + actionDescriptors: type: array items: + description: ActionDescriptor describes a declarative action + that can be performed on a custom resource instance type: object - description: A spec for a field in the status block of the APIService required: - path - - displayName - - description properties: - path: + description: type: string - description: A jsonpath indexing into the status object on the API Resource where the the status value can be found. displayName: type: string - description: A human-readable name for the status entry. - description: + path: type: string - description: A description of the status entry. + value: + description: RawMessage is a raw encoded JSON value. + It implements Marshaler and Unmarshaler and can be + used to delay JSON decoding or precompute a JSON encoding. + type: string + format: byte x-descriptors: type: array - description: A list of descriptors for the status entry that indicate the meaning of the field. items: type: string - value: - description: If present, the value of this status is the same for all instances of the API Resource and can be found here instead of on the API Resource. - - customresourcedefinitions: - type: object - properties: - owned: - type: array - description: What resources this operator is responsible for managing. No two running operators should manage the same resource. - items: - type: object - required: - - name - - version - - kind - - displayName - - description - properties: - name: - type: string - description: Fully qualified name of the CustomResourceDefinition (e.g. my-resource-v1.app.coreos.com) - version: + containerPort: + type: integer + format: int32 + deploymentName: type: string - description: The version field of the CustomResourceDefinition - kind: + description: type: string - description: The kind field of the CustomResourceDefinition displayName: type: string - description: A human-readable name for the CRD. - description: + group: + type: string + kind: + type: string + name: type: string - description: A description of the CRD resources: type: array items: + description: APIResourceReference is a Kubernetes resource + type used by a custom resource type: object - description: A list of resources that should be displayed for the CRD required: - - kind - - version + - kind + - name + - version properties: + kind: + type: string name: type: string - description: If a CRD, the fully qualified name of the CustomResourceDefinition (e.g. my-resource-v1.app.coreos.com) version: type: string - description: The version of the resource kind - kind: - type: string - description: The kind field of the resource kind - statusDescriptors: + specDescriptors: type: array items: + description: SpecDescriptor describes a field in a spec + block of a CRD so that OLM can consume it type: object - description: A spec for a field in the status block of the CRD required: - - path - - displayName - - description + - path properties: - path: + description: type: string - description: A jsonpath indexing into the status object on the CR where the the status value can be found. displayName: type: string - description: A human-readable name for the status entry. - description: + path: type: string - description: A description of the status entry. + value: + description: RawMessage is a raw encoded JSON value. + It implements Marshaler and Unmarshaler and can be + used to delay JSON decoding or precompute a JSON encoding. + type: string + format: byte x-descriptors: type: array - description: A list of descriptors for the status entry that indicate the meaning of the field. items: type: string - value: - description: If present, the value of this status is the same for all instances of the CRD and can be found here instead of on the CR. - specDescriptors: + statusDescriptors: type: array items: + description: StatusDescriptor describes a field in a status + block of a CRD so that OLM can consume it type: object - description: A spec for a field in the spec block of the CRD required: - - path - - displayName - - description + - path properties: - path: + description: type: string - description: A jsonpath indexing into the spec object on the CR where the the spec value can be found. displayName: type: string - description: A human-readable name for the spec entry. - description: + path: + type: string + value: + description: RawMessage is a raw encoded JSON value. + It implements Marshaler and Unmarshaler and can be + used to delay JSON decoding or precompute a JSON encoding. type: string - description: A description of the spec entry. + format: byte x-descriptors: type: array - description: A list of descriptors for the spec entry that indicate the meaning of the field. items: type: string - value: - description: If present, the value of this spec is the same for all instances of the CRD and can be found here instead of on the CR. + version: + type: string + customresourcedefinitions: + description: "CustomResourceDefinitions declares all of the CRDs managed + or required by an operator being ran by ClusterServiceVersion. \n + If the CRD is present in the Owned list, it is implicitly required." + type: object + properties: + owned: + type: array + items: + description: CRDDescription provides details to OLM about the + CRDs + type: object + required: + - kind + - name + - version + properties: actionDescriptors: type: array items: + description: ActionDescriptor describes a declarative action + that can be performed on a custom resource instance type: object - description: A spec for actions that can be performed on instances of the CRD required: - - path - - displayName - - description + - path properties: - path: + description: type: string - description: A jsonpath indexing into the spec object on the CR where the the spec value can be found. displayName: type: string - description: A human-readable name for the action. - description: + path: type: string - description: A description of the action. + value: + description: RawMessage is a raw encoded JSON value. + It implements Marshaler and Unmarshaler and can be + used to delay JSON decoding or precompute a JSON encoding. + type: string + format: byte x-descriptors: type: array - description: A list of descriptors for the action that indicate the meaning of the action. items: type: string - value: - description: If present, the value of this action is the same for all instances of the CRD and can be found here instead of on the CR. - required: - type: array - description: What resources this operator is responsible for managing. No two running operators should manage the same resource. - items: - type: object - required: - - name - - version - - kind - - displayName - - description - properties: - name: + description: type: string - description: Fully qualified name of the CustomResourceDefinition (e.g. my-resource-v1.app.coreos.com) - version: + displayName: type: string - description: The version field of the CustomResourceDefinition kind: type: string - description: The kind field of the CustomResourceDefinition - displayName: - type: string - description: A human-readable name for the CRD. - description: + name: type: string - description: A description of the CRD - statusDescriptors: + resources: + type: array + items: + description: APIResourceReference is a Kubernetes resource + type used by a custom resource + type: object + required: + - kind + - name + - version + properties: + kind: + type: string + name: + type: string + version: + type: string + specDescriptors: type: array items: + description: SpecDescriptor describes a field in a spec + block of a CRD so that OLM can consume it type: object - description: A spec for a field in the status block of the CRD required: - - path - - displayName - - description + - path properties: - path: + description: type: string - description: A jsonpath indexing into the status object on the CR where the the status value can be found. displayName: type: string - description: A human-readable name for the status entry. - description: + path: + type: string + value: + description: RawMessage is a raw encoded JSON value. + It implements Marshaler and Unmarshaler and can be + used to delay JSON decoding or precompute a JSON encoding. type: string - description: A description of the status entry. + format: byte x-descriptors: type: array - description: A list of descriptors for the status entry that indicate the meaning of the field. items: type: string + statusDescriptors: + type: array + items: + description: StatusDescriptor describes a field in a status + block of a CRD so that OLM can consume it + type: object + required: + - path + properties: + description: + type: string + displayName: + type: string + path: + type: string value: - description: If present, the value of this status is the same for all instances of the CRD and can be found here instead of on the CR. - - - install: - type: object - description: Information required to install this specific version of the operator software - oneOf: - - type: object - required: - - strategy - - spec - properties: - strategy: - type: string - enum: ['image'] - spec: - type: object - required: - - image - properties: - image: + description: RawMessage is a raw encoded JSON value. + It implements Marshaler and Unmarshaler and can be + used to delay JSON decoding or precompute a JSON encoding. + type: string + format: byte + x-descriptors: + type: array + items: + type: string + version: type: string - - type: object required: - - strategy - - spec - properties: - strategy: - type: string - enum: ['deployment'] - spec: + type: array + items: + description: CRDDescription provides details to OLM about the + CRDs type: object required: - - deployments + - kind + - name + - version properties: - installModes: + actionDescriptors: type: array - description: List of supported install modes for the operator items: + description: ActionDescriptor describes a declarative action + that can be performed on a custom resource instance type: object - description: A tuple representing a mode of installation and whether the operator supports it required: - - type - - supported + - path properties: - type: - type: string - description: A type of install mode - enum: - - OwnNamespace - - SingleNamespace - - MultiNamespace - - AllNamespaces - supported: - type: boolean - description: Represents if the install mode type is supported - deployments: + description: + type: string + displayName: + type: string + path: + type: string + value: + description: RawMessage is a raw encoded JSON value. + It implements Marshaler and Unmarshaler and can be + used to delay JSON decoding or precompute a JSON encoding. + type: string + format: byte + x-descriptors: + type: array + items: + type: string + description: + type: string + displayName: + type: string + kind: + type: string + name: + type: string + resources: type: array - description: List of deployments to create items: + description: APIResourceReference is a Kubernetes resource + type used by a custom resource type: object - description: A name and deployment to create in the cluster required: - - name - - spec + - kind + - name + - version properties: + kind: + type: string name: type: string - description: the consistent name of the deployment - spec: - type: object - description: The deployment spec to create in the cluster - permissions: + version: + type: string + specDescriptors: type: array - description: Permissions needed by the deployement to run correctly items: + description: SpecDescriptor describes a field in a spec + block of a CRD so that OLM can consume it type: object required: - - serviceAccountName - - rules + - path properties: - serviceAccountName: + description: type: string - description: The service account name to create for the deployment - rules: + displayName: + type: string + path: + type: string + value: + description: RawMessage is a raw encoded JSON value. + It implements Marshaler and Unmarshaler and can be + used to delay JSON decoding or precompute a JSON encoding. + type: string + format: byte + x-descriptors: type: array items: - type: object - description: a rule required by the service account - properties: - apiGroups: - type: array - description: apiGroups the rule applies to - items: - type: string - resources: - type: array - items: - type: string - resourceNames: - type: array - items: - type: string - verbs: - type: array - items: - type: string - enum: - - "*" - - assign - - get - - list - - watch - - create - - update - - patch - - delete - - deletecollection - - initialize - - use - clusterPermissions: + type: string + statusDescriptors: type: array - description: Cluster permissions needed by the deployement to run correctly items: + description: StatusDescriptor describes a field in a status + block of a CRD so that OLM can consume it type: object required: - - serviceAccountName - - rules + - path properties: - serviceAccountName: + description: + type: string + displayName: + type: string + path: + type: string + value: + description: RawMessage is a raw encoded JSON value. + It implements Marshaler and Unmarshaler and can be + used to delay JSON decoding or precompute a JSON encoding. type: string - description: The service account name to create for the deployment - rules: + format: byte + x-descriptors: type: array items: + type: string + version: + type: string + description: + type: string + displayName: + type: string + icon: + type: array + items: + type: object + required: + - base64data + - mediatype + properties: + base64data: + type: string + mediatype: + type: string + install: + description: NamedInstallStrategy represents the block of an ClusterServiceVersion + resource where the install strategy is specified. + type: object + required: + - strategy + properties: + spec: + description: StrategyDetailsDeployment represents the parsed details + of a Deployment InstallStrategy. + type: object + required: + - deployments + properties: + clusterPermissions: + type: array + items: + description: StrategyDeploymentPermissions describe the rbac + rules and service account needed by the install strategy + type: object + required: + - rules + - serviceAccountName + properties: + rules: + type: array + items: + description: PolicyRule holds information that describes + a policy rule, but does not contain information about + who the rule applies to or which namespace the rule + applies to. + type: object + required: + - verbs + properties: + apiGroups: + description: APIGroups is the name of the APIGroup + that contains the resources. If multiple API + groups are specified, any action requested against + one of the enumerated resources in any API group + will be allowed. + type: array + items: + type: string + nonResourceURLs: + description: NonResourceURLs is a set of partial + urls that a user should have access to. *s are + allowed, but only as the full, final step in the + path Since non-resource URLs are not namespaced, + this field is only applicable for ClusterRoles + referenced from a ClusterRoleBinding. Rules can + either apply to API resources (such as "pods" + or "secrets") or non-resource URL paths (such + as "/api"), but not both. + type: array + items: + type: string + resourceNames: + description: ResourceNames is an optional white + list of names that the rule applies to. An empty + set means that everything is allowed. + type: array + items: + type: string + resources: + description: Resources is a list of resources this + rule applies to. ResourceAll represents all resources. + type: array + items: + type: string + verbs: + description: Verbs is a list of Verbs that apply + to ALL the ResourceKinds and AttributeRestrictions + contained in this rule. VerbAll represents all + kinds. + type: array + items: + type: string + serviceAccountName: + type: string + deployments: + type: array + items: + description: StrategyDeploymentSpec contains the name and + spec for the deployment ALM should create + type: object + required: + - name + - spec + properties: + name: + type: string + spec: + description: DeploymentSpec is the specification of the + desired behavior of the Deployment. + type: object + required: + - selector + - template + properties: + minReadySeconds: + description: Minimum number of seconds for which a + newly created pod should be ready without any of + its container crashing, for it to be considered + available. Defaults to 0 (pod will be considered + available as soon as it is ready) + type: integer + format: int32 + paused: + description: Indicates that the deployment is paused. + type: boolean + progressDeadlineSeconds: + description: The maximum time in seconds for a deployment + to make progress before it is considered to be failed. + The deployment controller will continue to process + failed deployments and a condition with a ProgressDeadlineExceeded + reason will be surfaced in the deployment status. + Note that progress will not be estimated during + the time a deployment is paused. Defaults to 600s. + type: integer + format: int32 + replicas: + description: Number of desired pods. This is a pointer + to distinguish between explicit zero and not specified. + Defaults to 1. + type: integer + format: int32 + revisionHistoryLimit: + description: The number of old ReplicaSets to retain + to allow rollback. This is a pointer to distinguish + between explicit zero and not specified. Defaults + to 10. + type: integer + format: int32 + selector: + description: Label selector for pods. Existing ReplicaSets + whose pods are selected by this will be the ones + affected by this deployment. It must match the pod + template's labels. type: object - required: - - verbs - description: a rule required by the service account properties: - apiGroups: - type: array - description: apiGroups the rule applies to - items: - type: string - resources: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. type: array items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + type: object + required: + - key + - operator + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + type: array + items: + type: string + matchLabels: + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + additionalProperties: type: string - resourceNames: - type: array - items: - type: string - nonResourceURLs: - type: array - items: - type: string - verbs: - type: array - items: - type: string - enum: - - "*" - - assign - - get - - list - - watch - - create - - update - - patch - - put - - post - - delete - - deletecollection - - initialize - - use + strategy: + description: The deployment strategy to use to replace + existing pods with new ones. + type: object + properties: + rollingUpdate: + description: 'Rolling update config params. Present + only if DeploymentStrategyType = RollingUpdate. + --- TODO: Update this to follow our convention + for oneOf, whatever we decide it to be.' + type: object + properties: + maxSurge: + description: 'The maximum number of pods that + can be scheduled above the desired number + of pods. Value can be an absolute number + (ex: 5) or a percentage of desired pods + (ex: 10%). This can not be 0 if MaxUnavailable + is 0. Absolute number is calculated from + percentage by rounding up. Defaults to 25%. + Example: when this is set to 30%, the new + ReplicaSet can be scaled up immediately + when the rolling update starts, such that + the total number of old and new pods do + not exceed 130% of desired pods. Once old + pods have been killed, new ReplicaSet can + be scaled up further, ensuring that total + number of pods running at any time during + the update is at most 130% of desired pods.' + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + maxUnavailable: + description: 'The maximum number of pods that + can be unavailable during the update. Value + can be an absolute number (ex: 5) or a percentage + of desired pods (ex: 10%). Absolute number + is calculated from percentage by rounding + down. This can not be 0 if MaxSurge is 0. + Defaults to 25%. Example: when this is set + to 30%, the old ReplicaSet can be scaled + down to 70% of desired pods immediately + when the rolling update starts. Once new + pods are ready, old ReplicaSet can be scaled + down further, followed by scaling up the + new ReplicaSet, ensuring that the total + number of pods available at all times during + the update is at least 70% of desired pods.' + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + type: + description: Type of deployment. Can be "Recreate" + or "RollingUpdate". Default is RollingUpdate. + type: string + template: + description: Template describes the pods that will + be created. + type: object + properties: + metadata: + description: 'Standard object''s metadata. More + info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' + type: object + # TODO: Autogen this... + x-kubernetes-preserve-unknown-fields: true + spec: + description: 'Specification of the desired behavior + of the pod. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' + type: object + required: + - containers + properties: + activeDeadlineSeconds: + description: Optional duration in seconds + the pod may be active on the node relative + to StartTime before the system will actively + try to mark it failed and kill associated + containers. Value must be a positive integer. + type: integer + format: int64 + affinity: + description: If specified, the pod's scheduling + constraints + type: object + properties: + nodeAffinity: + description: Describes node affinity scheduling + rules for the pod. + type: object + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer + to schedule pods to nodes that satisfy + the affinity expressions specified + by this field, but it may choose + a node that violates one or more + of the expressions. The node that + is most preferred is the one with + the greatest sum of weights, i.e. + for each node that meets all of + the scheduling requirements (resource + request, requiredDuringScheduling + affinity expressions, etc.), compute + a sum by iterating through the elements + of this field and adding "weight" + to the sum if the node matches the + corresponding matchExpressions; + the node(s) with the highest sum + are the most preferred. + type: array + items: + description: An empty preferred + scheduling term matches all objects + with implicit weight 0 (i.e. it's + a no-op). A null preferred scheduling + term matches no objects (i.e. + is also a no-op). + type: object + required: + - preference + - weight + properties: + preference: + description: A node selector + term, associated with the + corresponding weight. + type: object + properties: + matchExpressions: + description: A list of node + selector requirements + by node's labels. + type: array + items: + description: A node selector + requirement is a selector + that contains values, + a key, and an operator + that relates the key + and values. + type: object + required: + - key + - operator + properties: + key: + description: The label + key that the selector + applies to. + type: string + operator: + description: Represents + a key's relationship + to a set of values. + Valid operators + are In, NotIn, Exists, + DoesNotExist. Gt, + and Lt. + type: string + values: + description: An array + of string values. + If the operator + is In or NotIn, + the values array + must be non-empty. + If the operator + is Exists or DoesNotExist, + the values array + must be empty. If + the operator is + Gt or Lt, the values + array must have + a single element, + which will be interpreted + as an integer. This + array is replaced + during a strategic + merge patch. + type: array + items: + type: string + matchFields: + description: A list of node + selector requirements + by node's fields. + type: array + items: + description: A node selector + requirement is a selector + that contains values, + a key, and an operator + that relates the key + and values. + type: object + required: + - key + - operator + properties: + key: + description: The label + key that the selector + applies to. + type: string + operator: + description: Represents + a key's relationship + to a set of values. + Valid operators + are In, NotIn, Exists, + DoesNotExist. Gt, + and Lt. + type: string + values: + description: An array + of string values. + If the operator + is In or NotIn, + the values array + must be non-empty. + If the operator + is Exists or DoesNotExist, + the values array + must be empty. If + the operator is + Gt or Lt, the values + array must have + a single element, + which will be interpreted + as an integer. This + array is replaced + during a strategic + merge patch. + type: array + items: + type: string + weight: + description: Weight associated + with matching the corresponding + nodeSelectorTerm, in the range + 1-100. + type: integer + format: int32 + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements + specified by this field are not + met at scheduling time, the pod + will not be scheduled onto the node. + If the affinity requirements specified + by this field cease to be met at + some point during pod execution + (e.g. due to an update), the system + may or may not try to eventually + evict the pod from its node. + type: object + required: + - nodeSelectorTerms + properties: + nodeSelectorTerms: + description: Required. A list + of node selector terms. The + terms are ORed. + type: array + items: + description: A null or empty + node selector term matches + no objects. The requirements + of them are ANDed. The TopologySelectorTerm + type implements a subset of + the NodeSelectorTerm. + type: object + properties: + matchExpressions: + description: A list of node + selector requirements + by node's labels. + type: array + items: + description: A node selector + requirement is a selector + that contains values, + a key, and an operator + that relates the key + and values. + type: object + required: + - key + - operator + properties: + key: + description: The label + key that the selector + applies to. + type: string + operator: + description: Represents + a key's relationship + to a set of values. + Valid operators + are In, NotIn, Exists, + DoesNotExist. Gt, + and Lt. + type: string + values: + description: An array + of string values. + If the operator + is In or NotIn, + the values array + must be non-empty. + If the operator + is Exists or DoesNotExist, + the values array + must be empty. If + the operator is + Gt or Lt, the values + array must have + a single element, + which will be interpreted + as an integer. This + array is replaced + during a strategic + merge patch. + type: array + items: + type: string + matchFields: + description: A list of node + selector requirements + by node's fields. + type: array + items: + description: A node selector + requirement is a selector + that contains values, + a key, and an operator + that relates the key + and values. + type: object + required: + - key + - operator + properties: + key: + description: The label + key that the selector + applies to. + type: string + operator: + description: Represents + a key's relationship + to a set of values. + Valid operators + are In, NotIn, Exists, + DoesNotExist. Gt, + and Lt. + type: string + values: + description: An array + of string values. + If the operator + is In or NotIn, + the values array + must be non-empty. + If the operator + is Exists or DoesNotExist, + the values array + must be empty. If + the operator is + Gt or Lt, the values + array must have + a single element, + which will be interpreted + as an integer. This + array is replaced + during a strategic + merge patch. + type: array + items: + type: string + podAffinity: + description: Describes pod affinity scheduling + rules (e.g. co-locate this pod in the + same node, zone, etc. as some other + pod(s)). + type: object + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer + to schedule pods to nodes that satisfy + the affinity expressions specified + by this field, but it may choose + a node that violates one or more + of the expressions. The node that + is most preferred is the one with + the greatest sum of weights, i.e. + for each node that meets all of + the scheduling requirements (resource + request, requiredDuringScheduling + affinity expressions, etc.), compute + a sum by iterating through the elements + of this field and adding "weight" + to the sum if the node has pods + which matches the corresponding + podAffinityTerm; the node(s) with + the highest sum are the most preferred. + type: array + items: + description: The weights of all + of the matched WeightedPodAffinityTerm + fields are added per-node to find + the most preferred node(s) + type: object + required: + - podAffinityTerm + - weight + properties: + podAffinityTerm: + description: Required. A pod + affinity term, associated + with the corresponding weight. + type: object + required: + - topologyKey + properties: + labelSelector: + description: A label query + over a set of resources, + in this case pods. + type: object + properties: + matchExpressions: + description: matchExpressions + is a list of label + selector requirements. + The requirements are + ANDed. + type: array + items: + description: A label + selector requirement + is a selector that + contains values, + a key, and an operator + that relates the + key and values. + type: object + required: + - key + - operator + properties: + key: + description: key + is the label + key that the + selector applies + to. + type: string + operator: + description: operator + represents a + key's relationship + to a set of + values. Valid + operators are + In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values + is an array + of string values. + If the operator + is In or NotIn, + the values array + must be non-empty. + If the operator + is Exists or + DoesNotExist, + the values array + must be empty. + This array is + replaced during + a strategic + merge patch. + type: array + items: + type: string + matchLabels: + description: matchLabels + is a map of {key,value} + pairs. A single {key,value} + in the matchLabels + map is equivalent + to an element of matchExpressions, + whose key field is + "key", the operator + is "In", and the values + array contains only + "value". The requirements + are ANDed. + type: object + additionalProperties: + type: string + namespaces: + description: namespaces + specifies which namespaces + the labelSelector applies + to (matches against); + null or empty list means + "this pod's namespace" + type: array + items: + type: string + topologyKey: + description: This pod should + be co-located (affinity) + or not co-located (anti-affinity) + with the pods matching + the labelSelector in the + specified namespaces, + where co-located is defined + as running on a node whose + value of the label with + key topologyKey matches + that of any node on which + any of the selected pods + is running. Empty topologyKey + is not allowed. + type: string + weight: + description: weight associated + with matching the corresponding + podAffinityTerm, in the range + 1-100. + type: integer + format: int32 + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements + specified by this field are not + met at scheduling time, the pod + will not be scheduled onto the node. + If the affinity requirements specified + by this field cease to be met at + some point during pod execution + (e.g. due to a pod label update), + the system may or may not try to + eventually evict the pod from its + node. When there are multiple elements, + the lists of nodes corresponding + to each podAffinityTerm are intersected, + i.e. all terms must be satisfied. + type: array + items: + description: Defines a set of pods + (namely those matching the labelSelector + relative to the given namespace(s)) + that this pod should be co-located + (affinity) or not co-located (anti-affinity) + with, where co-located is defined + as running on a node whose value + of the label with key + matches that of any node on which + a pod of the set of pods is running + type: object + required: + - topologyKey + properties: + labelSelector: + description: A label query over + a set of resources, in this + case pods. + type: object + properties: + matchExpressions: + description: matchExpressions + is a list of label selector + requirements. The requirements + are ANDed. + type: array + items: + description: A label selector + requirement is a selector + that contains values, + a key, and an operator + that relates the key + and values. + type: object + required: + - key + - operator + properties: + key: + description: key is + the label key that + the selector applies + to. + type: string + operator: + description: operator + represents a key's + relationship to + a set of values. + Valid operators + are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values + is an array of string + values. If the operator + is In or NotIn, + the values array + must be non-empty. + If the operator + is Exists or DoesNotExist, + the values array + must be empty. This + array is replaced + during a strategic + merge patch. + type: array + items: + type: string + matchLabels: + description: matchLabels + is a map of {key,value} + pairs. A single {key,value} + in the matchLabels map + is equivalent to an element + of matchExpressions, whose + key field is "key", the + operator is "In", and + the values array contains + only "value". The requirements + are ANDed. + type: object + additionalProperties: + type: string + namespaces: + description: namespaces specifies + which namespaces the labelSelector + applies to (matches against); + null or empty list means "this + pod's namespace" + type: array + items: + type: string + topologyKey: + description: This pod should + be co-located (affinity) or + not co-located (anti-affinity) + with the pods matching the + labelSelector in the specified + namespaces, where co-located + is defined as running on a + node whose value of the label + with key topologyKey matches + that of any node on which + any of the selected pods is + running. Empty topologyKey + is not allowed. + type: string + podAntiAffinity: + description: Describes pod anti-affinity + scheduling rules (e.g. avoid putting + this pod in the same node, zone, etc. + as some other pod(s)). + type: object + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer + to schedule pods to nodes that satisfy + the anti-affinity expressions specified + by this field, but it may choose + a node that violates one or more + of the expressions. The node that + is most preferred is the one with + the greatest sum of weights, i.e. + for each node that meets all of + the scheduling requirements (resource + request, requiredDuringScheduling + anti-affinity expressions, etc.), + compute a sum by iterating through + the elements of this field and adding + "weight" to the sum if the node + has pods which matches the corresponding + podAffinityTerm; the node(s) with + the highest sum are the most preferred. + type: array + items: + description: The weights of all + of the matched WeightedPodAffinityTerm + fields are added per-node to find + the most preferred node(s) + type: object + required: + - podAffinityTerm + - weight + properties: + podAffinityTerm: + description: Required. A pod + affinity term, associated + with the corresponding weight. + type: object + required: + - topologyKey + properties: + labelSelector: + description: A label query + over a set of resources, + in this case pods. + type: object + properties: + matchExpressions: + description: matchExpressions + is a list of label + selector requirements. + The requirements are + ANDed. + type: array + items: + description: A label + selector requirement + is a selector that + contains values, + a key, and an operator + that relates the + key and values. + type: object + required: + - key + - operator + properties: + key: + description: key + is the label + key that the + selector applies + to. + type: string + operator: + description: operator + represents a + key's relationship + to a set of + values. Valid + operators are + In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values + is an array + of string values. + If the operator + is In or NotIn, + the values array + must be non-empty. + If the operator + is Exists or + DoesNotExist, + the values array + must be empty. + This array is + replaced during + a strategic + merge patch. + type: array + items: + type: string + matchLabels: + description: matchLabels + is a map of {key,value} + pairs. A single {key,value} + in the matchLabels + map is equivalent + to an element of matchExpressions, + whose key field is + "key", the operator + is "In", and the values + array contains only + "value". The requirements + are ANDed. + type: object + additionalProperties: + type: string + namespaces: + description: namespaces + specifies which namespaces + the labelSelector applies + to (matches against); + null or empty list means + "this pod's namespace" + type: array + items: + type: string + topologyKey: + description: This pod should + be co-located (affinity) + or not co-located (anti-affinity) + with the pods matching + the labelSelector in the + specified namespaces, + where co-located is defined + as running on a node whose + value of the label with + key topologyKey matches + that of any node on which + any of the selected pods + is running. Empty topologyKey + is not allowed. + type: string + weight: + description: weight associated + with matching the corresponding + podAffinityTerm, in the range + 1-100. + type: integer + format: int32 + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity + requirements specified by this field + are not met at scheduling time, + the pod will not be scheduled onto + the node. If the anti-affinity requirements + specified by this field cease to + be met at some point during pod + execution (e.g. due to a pod label + update), the system may or may not + try to eventually evict the pod + from its node. When there are multiple + elements, the lists of nodes corresponding + to each podAffinityTerm are intersected, + i.e. all terms must be satisfied. + type: array + items: + description: Defines a set of pods + (namely those matching the labelSelector + relative to the given namespace(s)) + that this pod should be co-located + (affinity) or not co-located (anti-affinity) + with, where co-located is defined + as running on a node whose value + of the label with key + matches that of any node on which + a pod of the set of pods is running + type: object + required: + - topologyKey + properties: + labelSelector: + description: A label query over + a set of resources, in this + case pods. + type: object + properties: + matchExpressions: + description: matchExpressions + is a list of label selector + requirements. The requirements + are ANDed. + type: array + items: + description: A label selector + requirement is a selector + that contains values, + a key, and an operator + that relates the key + and values. + type: object + required: + - key + - operator + properties: + key: + description: key is + the label key that + the selector applies + to. + type: string + operator: + description: operator + represents a key's + relationship to + a set of values. + Valid operators + are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values + is an array of string + values. If the operator + is In or NotIn, + the values array + must be non-empty. + If the operator + is Exists or DoesNotExist, + the values array + must be empty. This + array is replaced + during a strategic + merge patch. + type: array + items: + type: string + matchLabels: + description: matchLabels + is a map of {key,value} + pairs. A single {key,value} + in the matchLabels map + is equivalent to an element + of matchExpressions, whose + key field is "key", the + operator is "In", and + the values array contains + only "value". The requirements + are ANDed. + type: object + additionalProperties: + type: string + namespaces: + description: namespaces specifies + which namespaces the labelSelector + applies to (matches against); + null or empty list means "this + pod's namespace" + type: array + items: + type: string + topologyKey: + description: This pod should + be co-located (affinity) or + not co-located (anti-affinity) + with the pods matching the + labelSelector in the specified + namespaces, where co-located + is defined as running on a + node whose value of the label + with key topologyKey matches + that of any node on which + any of the selected pods is + running. Empty topologyKey + is not allowed. + type: string + automountServiceAccountToken: + description: AutomountServiceAccountToken + indicates whether a service account token + should be automatically mounted. + type: boolean + containers: + description: List of containers belonging + to the pod. Containers cannot currently + be added or removed. There must be at least + one container in a Pod. Cannot be updated. + type: array + items: + description: A single application container + that you want to run within a pod. + type: object + required: + - name + properties: + args: + description: 'Arguments to the entrypoint. + The docker image''s CMD is used if + this is not provided. Variable references + $(VAR_NAME) are expanded using the + container''s environment. If a variable + cannot be resolved, the reference + in the input string will be unchanged. + The $(VAR_NAME) syntax can be escaped + with a double $$, ie: $$(VAR_NAME). + Escaped references will never be expanded, + regardless of whether the variable + exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + type: array + items: + type: string + command: + description: 'Entrypoint array. Not + executed within a shell. The docker + image''s ENTRYPOINT is used if this + is not provided. Variable references + $(VAR_NAME) are expanded using the + container''s environment. If a variable + cannot be resolved, the reference + in the input string will be unchanged. + The $(VAR_NAME) syntax can be escaped + with a double $$, ie: $$(VAR_NAME). + Escaped references will never be expanded, + regardless of whether the variable + exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + type: array + items: + type: string + env: + description: List of environment variables + to set in the container. Cannot be + updated. + type: array + items: + description: EnvVar represents an + environment variable present in + a Container. + type: object + required: + - name + properties: + name: + description: Name of the environment + variable. Must be a C_IDENTIFIER. + type: string + value: + description: 'Variable references + $(VAR_NAME) are expanded using + the previous defined environment + variables in the container and + any service environment variables. + If a variable cannot be resolved, + the reference in the input string + will be unchanged. The $(VAR_NAME) + syntax can be escaped with a + double $$, ie: $$(VAR_NAME). + Escaped references will never + be expanded, regardless of whether + the variable exists or not. + Defaults to "".' + type: string + valueFrom: + description: Source for the environment + variable's value. Cannot be + used if value is not empty. + type: object + properties: + configMapKeyRef: + description: Selects a key + of a ConfigMap. + type: object + required: + - key + properties: + key: + description: The key to + select. + type: string + name: + description: 'Name of + the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful + fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether + the ConfigMap or its + key must be defined + type: boolean + fieldRef: + description: 'Selects a field + of the pod: supports metadata.name, + metadata.namespace, metadata.labels, + metadata.annotations, spec.nodeName, + spec.serviceAccountName, + status.hostIP, status.podIP.' + type: object + required: + - fieldPath + properties: + apiVersion: + description: Version of + the schema the FieldPath + is written in terms + of, defaults to "v1". + type: string + fieldPath: + description: Path of the + field to select in the + specified API version. + type: string + resourceFieldRef: + description: 'Selects a resource + of the container: only resources + limits and requests (limits.cpu, + limits.memory, limits.ephemeral-storage, + requests.cpu, requests.memory + and requests.ephemeral-storage) + are currently supported.' + type: object + required: + - resource + properties: + containerName: + description: 'Container + name: required for volumes, + optional for env vars' + type: string + divisor: + description: Specifies + the output format of + the exposed resources, + defaults to "1" + type: string + resource: + description: 'Required: + resource to select' + type: string + secretKeyRef: + description: Selects a key + of a secret in the pod's + namespace + type: object + required: + - key + properties: + key: + description: The key of + the secret to select + from. Must be a valid + secret key. + type: string + name: + description: 'Name of + the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful + fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether + the Secret or its key + must be defined + type: boolean + envFrom: + description: List of sources to populate + environment variables in the container. + The keys defined within a source must + be a C_IDENTIFIER. All invalid keys + will be reported as an event when + the container is starting. When a + key exists in multiple sources, the + value associated with the last source + will take precedence. Values defined + by an Env with a duplicate key will + take precedence. Cannot be updated. + type: array + items: + description: EnvFromSource represents + the source of a set of ConfigMaps + type: object + properties: + configMapRef: + description: The ConfigMap to + select from + type: object + properties: + name: + description: 'Name of the + referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + optional: + description: Specify whether + the ConfigMap must be defined + type: boolean + prefix: + description: An optional identifier + to prepend to each key in the + ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select + from + type: object + properties: + name: + description: 'Name of the + referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + optional: + description: Specify whether + the Secret must be defined + type: boolean + image: + description: 'Docker image name. More + info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher + level config management to default + or override container images in workload + controllers like Deployments and StatefulSets.' + type: string + imagePullPolicy: + description: 'Image pull policy. One + of Always, Never, IfNotPresent. Defaults + to Always if :latest tag is specified, + or IfNotPresent otherwise. Cannot + be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' + type: string + lifecycle: + description: Actions that the management + system should take in response to + container lifecycle events. Cannot + be updated. + type: object + properties: + postStart: + description: 'PostStart is called + immediately after a container + is created. If the handler fails, + the container is terminated and + restarted according to its restart + policy. Other management of the + container blocks until the hook + completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + type: object + properties: + exec: + description: One and only one + of the following should be + specified. Exec specifies + the action to take. + type: object + properties: + command: + description: Command is + the command line to execute + inside the container, + the working directory + for the command is root + ('/') in the container's + filesystem. The command + is simply exec'd, it is + not run inside a shell, + so traditional shell instructions + ('|', etc) won't work. + To use a shell, you need + to explicitly call out + to that shell. Exit status + of 0 is treated as live/healthy + and non-zero is unhealthy. + type: array + items: + type: string + httpGet: + description: HTTPGet specifies + the http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to + connect to, defaults to + the pod IP. You probably + want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers + to set in the request. + HTTP allows repeated headers. + type: array + items: + description: HTTPHeader + describes a custom header + to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + path: + description: Path to access + on the HTTP server. + type: string + port: + description: Name or number + of the port to access + on the container. Number + must be in the range 1 + to 65535. Name must be + an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use + for connecting to the + host. Defaults to HTTP. + type: string + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP + port. TCP hooks not yet supported + TODO: implement a realistic + TCP lifecycle hook' + type: object + required: + - port + properties: + host: + description: 'Optional: + Host name to connect to, + defaults to the pod IP.' + type: string + port: + description: Number or name + of the port to access + on the container. Number + must be in the range 1 + to 65535. Name must be + an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + preStop: + description: 'PreStop is called + immediately before a container + is terminated due to an API request + or management event such as liveness/startup + probe failure, preemption, resource + contention, etc. The handler is + not called if the container crashes + or exits. The reason for termination + is passed to the handler. The + Pod''s termination grace period + countdown begins before the PreStop + hooked is executed. Regardless + of the outcome of the handler, + the container will eventually + terminate within the Pod''s termination + grace period. Other management + of the container blocks until + the hook completes or until the + termination grace period is reached. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + type: object + properties: + exec: + description: One and only one + of the following should be + specified. Exec specifies + the action to take. + type: object + properties: + command: + description: Command is + the command line to execute + inside the container, + the working directory + for the command is root + ('/') in the container's + filesystem. The command + is simply exec'd, it is + not run inside a shell, + so traditional shell instructions + ('|', etc) won't work. + To use a shell, you need + to explicitly call out + to that shell. Exit status + of 0 is treated as live/healthy + and non-zero is unhealthy. + type: array + items: + type: string + httpGet: + description: HTTPGet specifies + the http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to + connect to, defaults to + the pod IP. You probably + want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers + to set in the request. + HTTP allows repeated headers. + type: array + items: + description: HTTPHeader + describes a custom header + to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + path: + description: Path to access + on the HTTP server. + type: string + port: + description: Name or number + of the port to access + on the container. Number + must be in the range 1 + to 65535. Name must be + an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use + for connecting to the + host. Defaults to HTTP. + type: string + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP + port. TCP hooks not yet supported + TODO: implement a realistic + TCP lifecycle hook' + type: object + required: + - port + properties: + host: + description: 'Optional: + Host name to connect to, + defaults to the pod IP.' + type: string + port: + description: Number or name + of the port to access + on the container. Number + must be in the range 1 + to 65535. Name must be + an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + livenessProbe: + description: 'Periodic probe of container + liveness. Container will be restarted + if the probe fails. Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: object + properties: + exec: + description: One and only one of + the following should be specified. + Exec specifies the action to take. + type: object + properties: + command: + description: Command is the + command line to execute inside + the container, the working + directory for the command is + root ('/') in the container's + filesystem. The command is + simply exec'd, it is not run + inside a shell, so traditional + shell instructions ('|', etc) + won't work. To use a shell, + you need to explicitly call + out to that shell. Exit status + of 0 is treated as live/healthy + and non-zero is unhealthy. + type: array + items: + type: string + failureThreshold: + description: Minimum consecutive + failures for the probe to be considered + failed after having succeeded. + Defaults to 3. Minimum value is + 1. + type: integer + format: int32 + httpGet: + description: HTTPGet specifies the + http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to connect + to, defaults to the pod IP. + You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers + to set in the request. HTTP + allows repeated headers. + type: array + items: + description: HTTPHeader describes + a custom header to be used + in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + path: + description: Path to access + on the HTTP server. + type: string + port: + description: Name or number + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for + connecting to the host. Defaults + to HTTP. + type: string + initialDelaySeconds: + description: 'Number of seconds + after the container has started + before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + periodSeconds: + description: How often (in seconds) + to perform the probe. Default + to 10 seconds. Minimum value is + 1. + type: integer + format: int32 + successThreshold: + description: Minimum consecutive + successes for the probe to be + considered successful after having + failed. Defaults to 1. Must be + 1 for liveness and startup. Minimum + value is 1. + type: integer + format: int32 + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP port. + TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle + hook' + type: object + required: + - port + properties: + host: + description: 'Optional: Host + name to connect to, defaults + to the pod IP.' + type: string + port: + description: Number or name + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: 'Number of seconds + after which the probe times out. + Defaults to 1 second. Minimum + value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + name: + description: Name of the container specified + as a DNS_LABEL. Each container in + a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: List of ports to expose + from the container. Exposing a port + here gives the system additional information + about the network connections a container + uses, but is primarily informational. + Not specifying a port here DOES NOT + prevent that port from being exposed. + Any port which is listening on the + default "0.0.0.0" address inside a + container will be accessible from + the network. Cannot be updated. + type: array + items: + description: ContainerPort represents + a network port in a single container. + type: object + required: + - containerPort + properties: + containerPort: + description: Number of port to + expose on the pod's IP address. + This must be a valid port number, + 0 < x < 65536. + type: integer + format: int32 + hostIP: + description: What host IP to bind + the external port to. + type: string + hostPort: + description: Number of port to + expose on the host. If specified, + this must be a valid port number, + 0 < x < 65536. If HostNetwork + is specified, this must match + ContainerPort. Most containers + do not need this. + type: integer + format: int32 + name: + description: If specified, this + must be an IANA_SVC_NAME and + unique within the pod. Each + named port in a pod must have + a unique name. Name for the + port that can be referred to + by services. + type: string + protocol: + description: Protocol for port. + Must be UDP, TCP, or SCTP. Defaults + to "TCP". + type: string + readinessProbe: + description: 'Periodic probe of container + service readiness. Container will + be removed from service endpoints + if the probe fails. Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: object + properties: + exec: + description: One and only one of + the following should be specified. + Exec specifies the action to take. + type: object + properties: + command: + description: Command is the + command line to execute inside + the container, the working + directory for the command is + root ('/') in the container's + filesystem. The command is + simply exec'd, it is not run + inside a shell, so traditional + shell instructions ('|', etc) + won't work. To use a shell, + you need to explicitly call + out to that shell. Exit status + of 0 is treated as live/healthy + and non-zero is unhealthy. + type: array + items: + type: string + failureThreshold: + description: Minimum consecutive + failures for the probe to be considered + failed after having succeeded. + Defaults to 3. Minimum value is + 1. + type: integer + format: int32 + httpGet: + description: HTTPGet specifies the + http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to connect + to, defaults to the pod IP. + You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers + to set in the request. HTTP + allows repeated headers. + type: array + items: + description: HTTPHeader describes + a custom header to be used + in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + path: + description: Path to access + on the HTTP server. + type: string + port: + description: Name or number + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for + connecting to the host. Defaults + to HTTP. + type: string + initialDelaySeconds: + description: 'Number of seconds + after the container has started + before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + periodSeconds: + description: How often (in seconds) + to perform the probe. Default + to 10 seconds. Minimum value is + 1. + type: integer + format: int32 + successThreshold: + description: Minimum consecutive + successes for the probe to be + considered successful after having + failed. Defaults to 1. Must be + 1 for liveness and startup. Minimum + value is 1. + type: integer + format: int32 + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP port. + TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle + hook' + type: object + required: + - port + properties: + host: + description: 'Optional: Host + name to connect to, defaults + to the pod IP.' + type: string + port: + description: Number or name + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: 'Number of seconds + after which the probe times out. + Defaults to 1 second. Minimum + value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + resources: + description: 'Compute Resources required + by this container. Cannot be updated. + More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + properties: + limits: + description: 'Limits describes the + maximum amount of compute resources + allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + additionalProperties: + type: string + requests: + description: 'Requests describes + the minimum amount of compute + resources required. If Requests + is omitted for a container, it + defaults to Limits if that is + explicitly specified, otherwise + to an implementation-defined value. + More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + additionalProperties: + type: string + securityContext: + description: 'Security options the pod + should run with. More info: https://kubernetes.io/docs/concepts/policy/security-context/ + More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' + type: object + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation + controls whether a process can + gain more privileges than its + parent process. This bool directly + controls if the no_new_privs flag + will be set on the container process. + AllowPrivilegeEscalation is true + always when the container is: + 1) run as Privileged 2) has CAP_SYS_ADMIN' + type: boolean + capabilities: + description: The capabilities to + add/drop when running containers. + Defaults to the default set of + capabilities granted by the container + runtime. + type: object + properties: + add: + description: Added capabilities + type: array + items: + description: Capability represent + POSIX capabilities type + type: string + drop: + description: Removed capabilities + type: array + items: + description: Capability represent + POSIX capabilities type + type: string + privileged: + description: Run container in privileged + mode. Processes in privileged + containers are essentially equivalent + to root on the host. Defaults + to false. + type: boolean + procMount: + description: procMount denotes the + type of proc mount to use for + the containers. The default is + DefaultProcMount which uses the + container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType + feature flag to be enabled. + type: string + readOnlyRootFilesystem: + description: Whether this container + has a read-only root filesystem. + Default is false. + type: boolean + runAsGroup: + description: The GID to run the + entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If + set in both SecurityContext and + PodSecurityContext, the value + specified in SecurityContext takes + precedence. + type: integer + format: int64 + runAsNonRoot: + description: Indicates that the + container must run as a non-root + user. If true, the Kubelet will + validate the image at runtime + to ensure that it does not run + as UID 0 (root) and fail to start + the container if it does. If unset + or false, no such validation will + be performed. May also be set + in PodSecurityContext. If set + in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext + takes precedence. + type: boolean + runAsUser: + description: The UID to run the + entrypoint of the container process. + Defaults to user specified in + image metadata if unspecified. + May also be set in PodSecurityContext. If + set in both SecurityContext and + PodSecurityContext, the value + specified in SecurityContext takes + precedence. + type: integer + format: int64 + seLinuxOptions: + description: The SELinux context + to be applied to the container. + If unspecified, the container + runtime will allocate a random + SELinux context for each container. May + also be set in PodSecurityContext. If + set in both SecurityContext and + PodSecurityContext, the value + specified in SecurityContext takes + precedence. + type: object + properties: + level: + description: Level is SELinux + level label that applies to + the container. + type: string + role: + description: Role is a SELinux + role label that applies to + the container. + type: string + type: + description: Type is a SELinux + type label that applies to + the container. + type: string + user: + description: User is a SELinux + user label that applies to + the container. + type: string + windowsOptions: + description: The Windows specific + settings applied to all containers. + If unspecified, the options from + the PodSecurityContext will be + used. If set in both SecurityContext + and PodSecurityContext, the value + specified in SecurityContext takes + precedence. + type: object + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec + is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the + GMSA credential spec named + by the GMSACredentialSpecName + field. This field is alpha-level + and is only honored by servers + that enable the WindowsGMSA + feature flag. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName + is the name of the GMSA credential + spec to use. This field is + alpha-level and is only honored + by servers that enable the + WindowsGMSA feature flag. + type: string + runAsUserName: + description: The UserName in + Windows to run the entrypoint + of the container process. + Defaults to the user specified + in image metadata if unspecified. + May also be set in PodSecurityContext. + If set in both SecurityContext + and PodSecurityContext, the + value specified in SecurityContext + takes precedence. This field + is alpha-level and it is only + honored by servers that enable + the WindowsRunAsUserName feature + flag. + type: string + startupProbe: + description: 'StartupProbe indicates + that the Pod has successfully initialized. + If specified, no other probes are + executed until this completes successfully. + If this probe fails, the Pod will + be restarted, just as if the livenessProbe + failed. This can be used to provide + different probe parameters at the + beginning of a Pod''s lifecycle, when + it might take a long time to load + data or warm a cache, than during + steady-state operation. This cannot + be updated. This is an alpha feature + enabled by the StartupProbe feature + flag. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: object + properties: + exec: + description: One and only one of + the following should be specified. + Exec specifies the action to take. + type: object + properties: + command: + description: Command is the + command line to execute inside + the container, the working + directory for the command is + root ('/') in the container's + filesystem. The command is + simply exec'd, it is not run + inside a shell, so traditional + shell instructions ('|', etc) + won't work. To use a shell, + you need to explicitly call + out to that shell. Exit status + of 0 is treated as live/healthy + and non-zero is unhealthy. + type: array + items: + type: string + failureThreshold: + description: Minimum consecutive + failures for the probe to be considered + failed after having succeeded. + Defaults to 3. Minimum value is + 1. + type: integer + format: int32 + httpGet: + description: HTTPGet specifies the + http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to connect + to, defaults to the pod IP. + You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers + to set in the request. HTTP + allows repeated headers. + type: array + items: + description: HTTPHeader describes + a custom header to be used + in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + path: + description: Path to access + on the HTTP server. + type: string + port: + description: Name or number + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for + connecting to the host. Defaults + to HTTP. + type: string + initialDelaySeconds: + description: 'Number of seconds + after the container has started + before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + periodSeconds: + description: How often (in seconds) + to perform the probe. Default + to 10 seconds. Minimum value is + 1. + type: integer + format: int32 + successThreshold: + description: Minimum consecutive + successes for the probe to be + considered successful after having + failed. Defaults to 1. Must be + 1 for liveness and startup. Minimum + value is 1. + type: integer + format: int32 + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP port. + TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle + hook' + type: object + required: + - port + properties: + host: + description: 'Optional: Host + name to connect to, defaults + to the pod IP.' + type: string + port: + description: Number or name + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: 'Number of seconds + after which the probe times out. + Defaults to 1 second. Minimum + value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + stdin: + description: Whether this container + should allocate a buffer for stdin + in the container runtime. If this + is not set, reads from stdin in the + container will always result in EOF. + Default is false. + type: boolean + stdinOnce: + description: Whether the container runtime + should close the stdin channel after + it has been opened by a single attach. + When stdin is true the stdin stream + will remain open across multiple attach + sessions. If stdinOnce is set to true, + stdin is opened on container start, + is empty until the first client attaches + to stdin, and then remains open and + accepts data until the client disconnects, + at which time stdin is closed and + remains closed until the container + is restarted. If this flag is false, + a container processes that reads from + stdin will never receive an EOF. Default + is false + type: boolean + terminationMessagePath: + description: 'Optional: Path at which + the file to which the container''s + termination message will be written + is mounted into the container''s filesystem. + Message written is intended to be + brief final status, such as an assertion + failure message. Will be truncated + by the node if greater than 4096 bytes. + The total message length across all + containers will be limited to 12kb. + Defaults to /dev/termination-log. + Cannot be updated.' + type: string + terminationMessagePolicy: + description: Indicate how the termination + message should be populated. File + will use the contents of terminationMessagePath + to populate the container status message + on both success and failure. FallbackToLogsOnError + will use the last chunk of container + log output if the termination message + file is empty and the container exited + with an error. The log output is limited + to 2048 bytes or 80 lines, whichever + is smaller. Defaults to File. Cannot + be updated. + type: string + tty: + description: Whether this container + should allocate a TTY for itself, + also requires 'stdin' to be true. + Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list + of block devices to be used by the + container. This is a beta feature. + type: array + items: + description: volumeDevice describes + a mapping of a raw block device + within a container. + type: object + required: + - devicePath + - name + properties: + devicePath: + description: devicePath is the + path inside of the container + that the device will be mapped + to. + type: string + name: + description: name must match the + name of a persistentVolumeClaim + in the pod + type: string + volumeMounts: + description: Pod volumes to mount into + the container's filesystem. Cannot + be updated. + type: array + items: + description: VolumeMount describes + a mounting of a Volume within a + container. + type: object + required: + - mountPath + - name + properties: + mountPath: + description: Path within the container + at which the volume should be + mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation + determines how mounts are propagated + from the host to container and + the other way around. When not + set, MountPropagationNone is + used. This field is beta in + 1.10. + type: string + name: + description: This must match the + Name of a Volume. + type: string + readOnly: + description: Mounted read-only + if true, read-write otherwise + (false or unspecified). Defaults + to false. + type: boolean + subPath: + description: Path within the volume + from which the container's volume + should be mounted. Defaults + to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within + the volume from which the container's + volume should be mounted. Behaves + similarly to SubPath but environment + variable references $(VAR_NAME) + are expanded using the container's + environment. Defaults to "" + (volume's root). SubPathExpr + and SubPath are mutually exclusive. + This field is beta in 1.15. + type: string + workingDir: + description: Container's working directory. + If not specified, the container runtime's + default will be used, which might + be configured in the container image. + Cannot be updated. + type: string + dnsConfig: + description: Specifies the DNS parameters + of a pod. Parameters specified here will + be merged to the generated DNS configuration + based on DNSPolicy. + type: object + properties: + nameservers: + description: A list of DNS name server + IP addresses. This will be appended + to the base nameservers generated from + DNSPolicy. Duplicated nameservers will + be removed. + type: array + items: + type: string + options: + description: A list of DNS resolver options. + This will be merged with the base options + generated from DNSPolicy. Duplicated + entries will be removed. Resolution + options given in Options will override + those that appear in the base DNSPolicy. + type: array + items: + description: PodDNSConfigOption defines + DNS resolver options of a pod. + type: object + properties: + name: + description: Required. + type: string + value: + type: string + searches: + description: A list of DNS search domains + for host-name lookup. This will be appended + to the base search paths generated from + DNSPolicy. Duplicated search paths will + be removed. + type: array + items: + type: string + dnsPolicy: + description: Set DNS policy for the pod. Defaults + to "ClusterFirst". Valid values are 'ClusterFirstWithHostNet', + 'ClusterFirst', 'Default' or 'None'. DNS + parameters given in DNSConfig will be merged + with the policy selected with DNSPolicy. + To have DNS options set along with hostNetwork, + you have to specify DNS policy explicitly + to 'ClusterFirstWithHostNet'. + type: string + enableServiceLinks: + description: 'EnableServiceLinks indicates + whether information about services should + be injected into pod''s environment variables, + matching the syntax of Docker links. Optional: + Defaults to true.' + type: boolean + ephemeralContainers: + description: List of ephemeral containers + run in this pod. Ephemeral containers may + be run in an existing pod to perform user-initiated + actions such as debugging. This list cannot + be specified when creating a pod, and it + cannot be modified by updating the pod spec. + In order to add an ephemeral container to + an existing pod, use the pod's ephemeralcontainers + subresource. This field is alpha-level and + is only honored by servers that enable the + EphemeralContainers feature. + type: array + items: + description: An EphemeralContainer is a + container that may be added temporarily + to an existing pod for user-initiated + activities such as debugging. Ephemeral + containers have no resource or scheduling + guarantees, and they will not be restarted + when they exit or when a pod is removed + or restarted. If an ephemeral container + causes a pod to exceed its resource allocation, + the pod may be evicted. Ephemeral containers + may not be added by directly updating + the pod spec. They must be added via the + pod's ephemeralcontainers subresource, + and they will appear in the pod spec once + added. This is an alpha feature enabled + by the EphemeralContainers feature flag. + type: object + required: + - name + properties: + args: + description: 'Arguments to the entrypoint. + The docker image''s CMD is used if + this is not provided. Variable references + $(VAR_NAME) are expanded using the + container''s environment. If a variable + cannot be resolved, the reference + in the input string will be unchanged. + The $(VAR_NAME) syntax can be escaped + with a double $$, ie: $$(VAR_NAME). + Escaped references will never be expanded, + regardless of whether the variable + exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + type: array + items: + type: string + command: + description: 'Entrypoint array. Not + executed within a shell. The docker + image''s ENTRYPOINT is used if this + is not provided. Variable references + $(VAR_NAME) are expanded using the + container''s environment. If a variable + cannot be resolved, the reference + in the input string will be unchanged. + The $(VAR_NAME) syntax can be escaped + with a double $$, ie: $$(VAR_NAME). + Escaped references will never be expanded, + regardless of whether the variable + exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + type: array + items: + type: string + env: + description: List of environment variables + to set in the container. Cannot be + updated. + type: array + items: + description: EnvVar represents an + environment variable present in + a Container. + type: object + required: + - name + properties: + name: + description: Name of the environment + variable. Must be a C_IDENTIFIER. + type: string + value: + description: 'Variable references + $(VAR_NAME) are expanded using + the previous defined environment + variables in the container and + any service environment variables. + If a variable cannot be resolved, + the reference in the input string + will be unchanged. The $(VAR_NAME) + syntax can be escaped with a + double $$, ie: $$(VAR_NAME). + Escaped references will never + be expanded, regardless of whether + the variable exists or not. + Defaults to "".' + type: string + valueFrom: + description: Source for the environment + variable's value. Cannot be + used if value is not empty. + type: object + properties: + configMapKeyRef: + description: Selects a key + of a ConfigMap. + type: object + required: + - key + properties: + key: + description: The key to + select. + type: string + name: + description: 'Name of + the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful + fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether + the ConfigMap or its + key must be defined + type: boolean + fieldRef: + description: 'Selects a field + of the pod: supports metadata.name, + metadata.namespace, metadata.labels, + metadata.annotations, spec.nodeName, + spec.serviceAccountName, + status.hostIP, status.podIP.' + type: object + required: + - fieldPath + properties: + apiVersion: + description: Version of + the schema the FieldPath + is written in terms + of, defaults to "v1". + type: string + fieldPath: + description: Path of the + field to select in the + specified API version. + type: string + resourceFieldRef: + description: 'Selects a resource + of the container: only resources + limits and requests (limits.cpu, + limits.memory, limits.ephemeral-storage, + requests.cpu, requests.memory + and requests.ephemeral-storage) + are currently supported.' + type: object + required: + - resource + properties: + containerName: + description: 'Container + name: required for volumes, + optional for env vars' + type: string + divisor: + description: Specifies + the output format of + the exposed resources, + defaults to "1" + type: string + resource: + description: 'Required: + resource to select' + type: string + secretKeyRef: + description: Selects a key + of a secret in the pod's + namespace + type: object + required: + - key + properties: + key: + description: The key of + the secret to select + from. Must be a valid + secret key. + type: string + name: + description: 'Name of + the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful + fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether + the Secret or its key + must be defined + type: boolean + envFrom: + description: List of sources to populate + environment variables in the container. + The keys defined within a source must + be a C_IDENTIFIER. All invalid keys + will be reported as an event when + the container is starting. When a + key exists in multiple sources, the + value associated with the last source + will take precedence. Values defined + by an Env with a duplicate key will + take precedence. Cannot be updated. + type: array + items: + description: EnvFromSource represents + the source of a set of ConfigMaps + type: object + properties: + configMapRef: + description: The ConfigMap to + select from + type: object + properties: + name: + description: 'Name of the + referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + optional: + description: Specify whether + the ConfigMap must be defined + type: boolean + prefix: + description: An optional identifier + to prepend to each key in the + ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select + from + type: object + properties: + name: + description: 'Name of the + referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + optional: + description: Specify whether + the Secret must be defined + type: boolean + image: + description: 'Docker image name. More + info: https://kubernetes.io/docs/concepts/containers/images' + type: string + imagePullPolicy: + description: 'Image pull policy. One + of Always, Never, IfNotPresent. Defaults + to Always if :latest tag is specified, + or IfNotPresent otherwise. Cannot + be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' + type: string + lifecycle: + description: Lifecycle is not allowed + for ephemeral containers. + type: object + properties: + postStart: + description: 'PostStart is called + immediately after a container + is created. If the handler fails, + the container is terminated and + restarted according to its restart + policy. Other management of the + container blocks until the hook + completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + type: object + properties: + exec: + description: One and only one + of the following should be + specified. Exec specifies + the action to take. + type: object + properties: + command: + description: Command is + the command line to execute + inside the container, + the working directory + for the command is root + ('/') in the container's + filesystem. The command + is simply exec'd, it is + not run inside a shell, + so traditional shell instructions + ('|', etc) won't work. + To use a shell, you need + to explicitly call out + to that shell. Exit status + of 0 is treated as live/healthy + and non-zero is unhealthy. + type: array + items: + type: string + httpGet: + description: HTTPGet specifies + the http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to + connect to, defaults to + the pod IP. You probably + want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers + to set in the request. + HTTP allows repeated headers. + type: array + items: + description: HTTPHeader + describes a custom header + to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + path: + description: Path to access + on the HTTP server. + type: string + port: + description: Name or number + of the port to access + on the container. Number + must be in the range 1 + to 65535. Name must be + an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use + for connecting to the + host. Defaults to HTTP. + type: string + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP + port. TCP hooks not yet supported + TODO: implement a realistic + TCP lifecycle hook' + type: object + required: + - port + properties: + host: + description: 'Optional: + Host name to connect to, + defaults to the pod IP.' + type: string + port: + description: Number or name + of the port to access + on the container. Number + must be in the range 1 + to 65535. Name must be + an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + preStop: + description: 'PreStop is called + immediately before a container + is terminated due to an API request + or management event such as liveness/startup + probe failure, preemption, resource + contention, etc. The handler is + not called if the container crashes + or exits. The reason for termination + is passed to the handler. The + Pod''s termination grace period + countdown begins before the PreStop + hooked is executed. Regardless + of the outcome of the handler, + the container will eventually + terminate within the Pod''s termination + grace period. Other management + of the container blocks until + the hook completes or until the + termination grace period is reached. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + type: object + properties: + exec: + description: One and only one + of the following should be + specified. Exec specifies + the action to take. + type: object + properties: + command: + description: Command is + the command line to execute + inside the container, + the working directory + for the command is root + ('/') in the container's + filesystem. The command + is simply exec'd, it is + not run inside a shell, + so traditional shell instructions + ('|', etc) won't work. + To use a shell, you need + to explicitly call out + to that shell. Exit status + of 0 is treated as live/healthy + and non-zero is unhealthy. + type: array + items: + type: string + httpGet: + description: HTTPGet specifies + the http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to + connect to, defaults to + the pod IP. You probably + want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers + to set in the request. + HTTP allows repeated headers. + type: array + items: + description: HTTPHeader + describes a custom header + to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + path: + description: Path to access + on the HTTP server. + type: string + port: + description: Name or number + of the port to access + on the container. Number + must be in the range 1 + to 65535. Name must be + an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use + for connecting to the + host. Defaults to HTTP. + type: string + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP + port. TCP hooks not yet supported + TODO: implement a realistic + TCP lifecycle hook' + type: object + required: + - port + properties: + host: + description: 'Optional: + Host name to connect to, + defaults to the pod IP.' + type: string + port: + description: Number or name + of the port to access + on the container. Number + must be in the range 1 + to 65535. Name must be + an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + livenessProbe: + description: Probes are not allowed + for ephemeral containers. + type: object + properties: + exec: + description: One and only one of + the following should be specified. + Exec specifies the action to take. + type: object + properties: + command: + description: Command is the + command line to execute inside + the container, the working + directory for the command is + root ('/') in the container's + filesystem. The command is + simply exec'd, it is not run + inside a shell, so traditional + shell instructions ('|', etc) + won't work. To use a shell, + you need to explicitly call + out to that shell. Exit status + of 0 is treated as live/healthy + and non-zero is unhealthy. + type: array + items: + type: string + failureThreshold: + description: Minimum consecutive + failures for the probe to be considered + failed after having succeeded. + Defaults to 3. Minimum value is + 1. + type: integer + format: int32 + httpGet: + description: HTTPGet specifies the + http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to connect + to, defaults to the pod IP. + You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers + to set in the request. HTTP + allows repeated headers. + type: array + items: + description: HTTPHeader describes + a custom header to be used + in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + path: + description: Path to access + on the HTTP server. + type: string + port: + description: Name or number + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for + connecting to the host. Defaults + to HTTP. + type: string + initialDelaySeconds: + description: 'Number of seconds + after the container has started + before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + periodSeconds: + description: How often (in seconds) + to perform the probe. Default + to 10 seconds. Minimum value is + 1. + type: integer + format: int32 + successThreshold: + description: Minimum consecutive + successes for the probe to be + considered successful after having + failed. Defaults to 1. Must be + 1 for liveness and startup. Minimum + value is 1. + type: integer + format: int32 + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP port. + TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle + hook' + type: object + required: + - port + properties: + host: + description: 'Optional: Host + name to connect to, defaults + to the pod IP.' + type: string + port: + description: Number or name + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: 'Number of seconds + after which the probe times out. + Defaults to 1 second. Minimum + value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + name: + description: Name of the ephemeral container + specified as a DNS_LABEL. This name + must be unique among all containers, + init containers and ephemeral containers. + type: string + ports: + description: Ports are not allowed for + ephemeral containers. + type: array + items: + description: ContainerPort represents + a network port in a single container. + type: object + required: + - containerPort + properties: + containerPort: + description: Number of port to + expose on the pod's IP address. + This must be a valid port number, + 0 < x < 65536. + type: integer + format: int32 + hostIP: + description: What host IP to bind + the external port to. + type: string + hostPort: + description: Number of port to + expose on the host. If specified, + this must be a valid port number, + 0 < x < 65536. If HostNetwork + is specified, this must match + ContainerPort. Most containers + do not need this. + type: integer + format: int32 + name: + description: If specified, this + must be an IANA_SVC_NAME and + unique within the pod. Each + named port in a pod must have + a unique name. Name for the + port that can be referred to + by services. + type: string + protocol: + description: Protocol for port. + Must be UDP, TCP, or SCTP. Defaults + to "TCP". + type: string + readinessProbe: + description: Probes are not allowed + for ephemeral containers. + type: object + properties: + exec: + description: One and only one of + the following should be specified. + Exec specifies the action to take. + type: object + properties: + command: + description: Command is the + command line to execute inside + the container, the working + directory for the command is + root ('/') in the container's + filesystem. The command is + simply exec'd, it is not run + inside a shell, so traditional + shell instructions ('|', etc) + won't work. To use a shell, + you need to explicitly call + out to that shell. Exit status + of 0 is treated as live/healthy + and non-zero is unhealthy. + type: array + items: + type: string + failureThreshold: + description: Minimum consecutive + failures for the probe to be considered + failed after having succeeded. + Defaults to 3. Minimum value is + 1. + type: integer + format: int32 + httpGet: + description: HTTPGet specifies the + http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to connect + to, defaults to the pod IP. + You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers + to set in the request. HTTP + allows repeated headers. + type: array + items: + description: HTTPHeader describes + a custom header to be used + in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + path: + description: Path to access + on the HTTP server. + type: string + port: + description: Name or number + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for + connecting to the host. Defaults + to HTTP. + type: string + initialDelaySeconds: + description: 'Number of seconds + after the container has started + before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + periodSeconds: + description: How often (in seconds) + to perform the probe. Default + to 10 seconds. Minimum value is + 1. + type: integer + format: int32 + successThreshold: + description: Minimum consecutive + successes for the probe to be + considered successful after having + failed. Defaults to 1. Must be + 1 for liveness and startup. Minimum + value is 1. + type: integer + format: int32 + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP port. + TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle + hook' + type: object + required: + - port + properties: + host: + description: 'Optional: Host + name to connect to, defaults + to the pod IP.' + type: string + port: + description: Number or name + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: 'Number of seconds + after which the probe times out. + Defaults to 1 second. Minimum + value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + resources: + description: Resources are not allowed + for ephemeral containers. Ephemeral + containers use spare resources already + allocated to the pod. + type: object + properties: + limits: + description: 'Limits describes the + maximum amount of compute resources + allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + additionalProperties: + type: string + requests: + description: 'Requests describes + the minimum amount of compute + resources required. If Requests + is omitted for a container, it + defaults to Limits if that is + explicitly specified, otherwise + to an implementation-defined value. + More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + additionalProperties: + type: string + securityContext: + description: SecurityContext is not + allowed for ephemeral containers. + type: object + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation + controls whether a process can + gain more privileges than its + parent process. This bool directly + controls if the no_new_privs flag + will be set on the container process. + AllowPrivilegeEscalation is true + always when the container is: + 1) run as Privileged 2) has CAP_SYS_ADMIN' + type: boolean + capabilities: + description: The capabilities to + add/drop when running containers. + Defaults to the default set of + capabilities granted by the container + runtime. + type: object + properties: + add: + description: Added capabilities + type: array + items: + description: Capability represent + POSIX capabilities type + type: string + drop: + description: Removed capabilities + type: array + items: + description: Capability represent + POSIX capabilities type + type: string + privileged: + description: Run container in privileged + mode. Processes in privileged + containers are essentially equivalent + to root on the host. Defaults + to false. + type: boolean + procMount: + description: procMount denotes the + type of proc mount to use for + the containers. The default is + DefaultProcMount which uses the + container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType + feature flag to be enabled. + type: string + readOnlyRootFilesystem: + description: Whether this container + has a read-only root filesystem. + Default is false. + type: boolean + runAsGroup: + description: The GID to run the + entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If + set in both SecurityContext and + PodSecurityContext, the value + specified in SecurityContext takes + precedence. + type: integer + format: int64 + runAsNonRoot: + description: Indicates that the + container must run as a non-root + user. If true, the Kubelet will + validate the image at runtime + to ensure that it does not run + as UID 0 (root) and fail to start + the container if it does. If unset + or false, no such validation will + be performed. May also be set + in PodSecurityContext. If set + in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext + takes precedence. + type: boolean + runAsUser: + description: The UID to run the + entrypoint of the container process. + Defaults to user specified in + image metadata if unspecified. + May also be set in PodSecurityContext. If + set in both SecurityContext and + PodSecurityContext, the value + specified in SecurityContext takes + precedence. + type: integer + format: int64 + seLinuxOptions: + description: The SELinux context + to be applied to the container. + If unspecified, the container + runtime will allocate a random + SELinux context for each container. May + also be set in PodSecurityContext. If + set in both SecurityContext and + PodSecurityContext, the value + specified in SecurityContext takes + precedence. + type: object + properties: + level: + description: Level is SELinux + level label that applies to + the container. + type: string + role: + description: Role is a SELinux + role label that applies to + the container. + type: string + type: + description: Type is a SELinux + type label that applies to + the container. + type: string + user: + description: User is a SELinux + user label that applies to + the container. + type: string + windowsOptions: + description: The Windows specific + settings applied to all containers. + If unspecified, the options from + the PodSecurityContext will be + used. If set in both SecurityContext + and PodSecurityContext, the value + specified in SecurityContext takes + precedence. + type: object + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec + is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the + GMSA credential spec named + by the GMSACredentialSpecName + field. This field is alpha-level + and is only honored by servers + that enable the WindowsGMSA + feature flag. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName + is the name of the GMSA credential + spec to use. This field is + alpha-level and is only honored + by servers that enable the + WindowsGMSA feature flag. + type: string + runAsUserName: + description: The UserName in + Windows to run the entrypoint + of the container process. + Defaults to the user specified + in image metadata if unspecified. + May also be set in PodSecurityContext. + If set in both SecurityContext + and PodSecurityContext, the + value specified in SecurityContext + takes precedence. This field + is alpha-level and it is only + honored by servers that enable + the WindowsRunAsUserName feature + flag. + type: string + startupProbe: + description: Probes are not allowed + for ephemeral containers. + type: object + properties: + exec: + description: One and only one of + the following should be specified. + Exec specifies the action to take. + type: object + properties: + command: + description: Command is the + command line to execute inside + the container, the working + directory for the command is + root ('/') in the container's + filesystem. The command is + simply exec'd, it is not run + inside a shell, so traditional + shell instructions ('|', etc) + won't work. To use a shell, + you need to explicitly call + out to that shell. Exit status + of 0 is treated as live/healthy + and non-zero is unhealthy. + type: array + items: + type: string + failureThreshold: + description: Minimum consecutive + failures for the probe to be considered + failed after having succeeded. + Defaults to 3. Minimum value is + 1. + type: integer + format: int32 + httpGet: + description: HTTPGet specifies the + http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to connect + to, defaults to the pod IP. + You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers + to set in the request. HTTP + allows repeated headers. + type: array + items: + description: HTTPHeader describes + a custom header to be used + in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + path: + description: Path to access + on the HTTP server. + type: string + port: + description: Name or number + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for + connecting to the host. Defaults + to HTTP. + type: string + initialDelaySeconds: + description: 'Number of seconds + after the container has started + before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + periodSeconds: + description: How often (in seconds) + to perform the probe. Default + to 10 seconds. Minimum value is + 1. + type: integer + format: int32 + successThreshold: + description: Minimum consecutive + successes for the probe to be + considered successful after having + failed. Defaults to 1. Must be + 1 for liveness and startup. Minimum + value is 1. + type: integer + format: int32 + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP port. + TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle + hook' + type: object + required: + - port + properties: + host: + description: 'Optional: Host + name to connect to, defaults + to the pod IP.' + type: string + port: + description: Number or name + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: 'Number of seconds + after which the probe times out. + Defaults to 1 second. Minimum + value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + stdin: + description: Whether this container + should allocate a buffer for stdin + in the container runtime. If this + is not set, reads from stdin in the + container will always result in EOF. + Default is false. + type: boolean + stdinOnce: + description: Whether the container runtime + should close the stdin channel after + it has been opened by a single attach. + When stdin is true the stdin stream + will remain open across multiple attach + sessions. If stdinOnce is set to true, + stdin is opened on container start, + is empty until the first client attaches + to stdin, and then remains open and + accepts data until the client disconnects, + at which time stdin is closed and + remains closed until the container + is restarted. If this flag is false, + a container processes that reads from + stdin will never receive an EOF. Default + is false + type: boolean + targetContainerName: + description: If set, the name of the + container from PodSpec that this ephemeral + container targets. The ephemeral container + will be run in the namespaces (IPC, + PID, etc) of this container. If not + set then the ephemeral container is + run in whatever namespaces are shared + for the pod. Note that the container + runtime must support this feature. + type: string + terminationMessagePath: + description: 'Optional: Path at which + the file to which the container''s + termination message will be written + is mounted into the container''s filesystem. + Message written is intended to be + brief final status, such as an assertion + failure message. Will be truncated + by the node if greater than 4096 bytes. + The total message length across all + containers will be limited to 12kb. + Defaults to /dev/termination-log. + Cannot be updated.' + type: string + terminationMessagePolicy: + description: Indicate how the termination + message should be populated. File + will use the contents of terminationMessagePath + to populate the container status message + on both success and failure. FallbackToLogsOnError + will use the last chunk of container + log output if the termination message + file is empty and the container exited + with an error. The log output is limited + to 2048 bytes or 80 lines, whichever + is smaller. Defaults to File. Cannot + be updated. + type: string + tty: + description: Whether this container + should allocate a TTY for itself, + also requires 'stdin' to be true. + Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list + of block devices to be used by the + container. This is a beta feature. + type: array + items: + description: volumeDevice describes + a mapping of a raw block device + within a container. + type: object + required: + - devicePath + - name + properties: + devicePath: + description: devicePath is the + path inside of the container + that the device will be mapped + to. + type: string + name: + description: name must match the + name of a persistentVolumeClaim + in the pod + type: string + volumeMounts: + description: Pod volumes to mount into + the container's filesystem. Cannot + be updated. + type: array + items: + description: VolumeMount describes + a mounting of a Volume within a + container. + type: object + required: + - mountPath + - name + properties: + mountPath: + description: Path within the container + at which the volume should be + mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation + determines how mounts are propagated + from the host to container and + the other way around. When not + set, MountPropagationNone is + used. This field is beta in + 1.10. + type: string + name: + description: This must match the + Name of a Volume. + type: string + readOnly: + description: Mounted read-only + if true, read-write otherwise + (false or unspecified). Defaults + to false. + type: boolean + subPath: + description: Path within the volume + from which the container's volume + should be mounted. Defaults + to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within + the volume from which the container's + volume should be mounted. Behaves + similarly to SubPath but environment + variable references $(VAR_NAME) + are expanded using the container's + environment. Defaults to "" + (volume's root). SubPathExpr + and SubPath are mutually exclusive. + This field is beta in 1.15. + type: string + workingDir: + description: Container's working directory. + If not specified, the container runtime's + default will be used, which might + be configured in the container image. + Cannot be updated. + type: string + hostAliases: + description: HostAliases is an optional list + of hosts and IPs that will be injected into + the pod's hosts file if specified. This + is only valid for non-hostNetwork pods. + type: array + items: + description: HostAlias holds the mapping + between IP and hostnames that will be + injected as an entry in the pod's hosts + file. + type: object + properties: + hostnames: + description: Hostnames for the above + IP address. + type: array + items: + type: string + ip: + description: IP address of the host + file entry. + type: string + hostIPC: + description: 'Use the host''s ipc namespace. + Optional: Default to false.' + type: boolean + hostNetwork: + description: Host networking requested for + this pod. Use the host's network namespace. + If this option is set, the ports that will + be used must be specified. Default to false. + type: boolean + hostPID: + description: 'Use the host''s pid namespace. + Optional: Default to false.' + type: boolean + hostname: + description: Specifies the hostname of the + Pod If not specified, the pod's hostname + will be set to a system-defined value. + type: string + imagePullSecrets: + description: 'ImagePullSecrets is an optional + list of references to secrets in the same + namespace to use for pulling any of the + images used by this PodSpec. If specified, + these secrets will be passed to individual + puller implementations for them to use. + For example, in the case of docker, only + DockerConfig type secrets are honored. More + info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod' + type: array + items: + description: LocalObjectReference contains + enough information to let you locate the + referenced object inside the same namespace. + type: object + properties: + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + initContainers: + description: 'List of initialization containers + belonging to the pod. Init containers are + executed in order prior to containers being + started. If any init container fails, the + pod is considered to have failed and is + handled according to its restartPolicy. + The name for an init container or normal + container must be unique among all containers. + Init containers may not have Lifecycle actions, + Readiness probes, Liveness probes, or Startup + probes. The resourceRequirements of an init + container are taken into account during + scheduling by finding the highest request/limit + for each resource type, and then using the + max of of that value or the sum of the normal + containers. Limits are applied to init containers + in a similar fashion. Init containers cannot + currently be added or removed. Cannot be + updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/' + type: array + items: + description: A single application container + that you want to run within a pod. + type: object + required: + - name + properties: + args: + description: 'Arguments to the entrypoint. + The docker image''s CMD is used if + this is not provided. Variable references + $(VAR_NAME) are expanded using the + container''s environment. If a variable + cannot be resolved, the reference + in the input string will be unchanged. + The $(VAR_NAME) syntax can be escaped + with a double $$, ie: $$(VAR_NAME). + Escaped references will never be expanded, + regardless of whether the variable + exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + type: array + items: + type: string + command: + description: 'Entrypoint array. Not + executed within a shell. The docker + image''s ENTRYPOINT is used if this + is not provided. Variable references + $(VAR_NAME) are expanded using the + container''s environment. If a variable + cannot be resolved, the reference + in the input string will be unchanged. + The $(VAR_NAME) syntax can be escaped + with a double $$, ie: $$(VAR_NAME). + Escaped references will never be expanded, + regardless of whether the variable + exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + type: array + items: + type: string + env: + description: List of environment variables + to set in the container. Cannot be + updated. + type: array + items: + description: EnvVar represents an + environment variable present in + a Container. + type: object + required: + - name + properties: + name: + description: Name of the environment + variable. Must be a C_IDENTIFIER. + type: string + value: + description: 'Variable references + $(VAR_NAME) are expanded using + the previous defined environment + variables in the container and + any service environment variables. + If a variable cannot be resolved, + the reference in the input string + will be unchanged. The $(VAR_NAME) + syntax can be escaped with a + double $$, ie: $$(VAR_NAME). + Escaped references will never + be expanded, regardless of whether + the variable exists or not. + Defaults to "".' + type: string + valueFrom: + description: Source for the environment + variable's value. Cannot be + used if value is not empty. + type: object + properties: + configMapKeyRef: + description: Selects a key + of a ConfigMap. + type: object + required: + - key + properties: + key: + description: The key to + select. + type: string + name: + description: 'Name of + the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful + fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether + the ConfigMap or its + key must be defined + type: boolean + fieldRef: + description: 'Selects a field + of the pod: supports metadata.name, + metadata.namespace, metadata.labels, + metadata.annotations, spec.nodeName, + spec.serviceAccountName, + status.hostIP, status.podIP.' + type: object + required: + - fieldPath + properties: + apiVersion: + description: Version of + the schema the FieldPath + is written in terms + of, defaults to "v1". + type: string + fieldPath: + description: Path of the + field to select in the + specified API version. + type: string + resourceFieldRef: + description: 'Selects a resource + of the container: only resources + limits and requests (limits.cpu, + limits.memory, limits.ephemeral-storage, + requests.cpu, requests.memory + and requests.ephemeral-storage) + are currently supported.' + type: object + required: + - resource + properties: + containerName: + description: 'Container + name: required for volumes, + optional for env vars' + type: string + divisor: + description: Specifies + the output format of + the exposed resources, + defaults to "1" + type: string + resource: + description: 'Required: + resource to select' + type: string + secretKeyRef: + description: Selects a key + of a secret in the pod's + namespace + type: object + required: + - key + properties: + key: + description: The key of + the secret to select + from. Must be a valid + secret key. + type: string + name: + description: 'Name of + the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful + fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether + the Secret or its key + must be defined + type: boolean + envFrom: + description: List of sources to populate + environment variables in the container. + The keys defined within a source must + be a C_IDENTIFIER. All invalid keys + will be reported as an event when + the container is starting. When a + key exists in multiple sources, the + value associated with the last source + will take precedence. Values defined + by an Env with a duplicate key will + take precedence. Cannot be updated. + type: array + items: + description: EnvFromSource represents + the source of a set of ConfigMaps + type: object + properties: + configMapRef: + description: The ConfigMap to + select from + type: object + properties: + name: + description: 'Name of the + referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + optional: + description: Specify whether + the ConfigMap must be defined + type: boolean + prefix: + description: An optional identifier + to prepend to each key in the + ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select + from + type: object + properties: + name: + description: 'Name of the + referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + optional: + description: Specify whether + the Secret must be defined + type: boolean + image: + description: 'Docker image name. More + info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher + level config management to default + or override container images in workload + controllers like Deployments and StatefulSets.' + type: string + imagePullPolicy: + description: 'Image pull policy. One + of Always, Never, IfNotPresent. Defaults + to Always if :latest tag is specified, + or IfNotPresent otherwise. Cannot + be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' + type: string + lifecycle: + description: Actions that the management + system should take in response to + container lifecycle events. Cannot + be updated. + type: object + properties: + postStart: + description: 'PostStart is called + immediately after a container + is created. If the handler fails, + the container is terminated and + restarted according to its restart + policy. Other management of the + container blocks until the hook + completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + type: object + properties: + exec: + description: One and only one + of the following should be + specified. Exec specifies + the action to take. + type: object + properties: + command: + description: Command is + the command line to execute + inside the container, + the working directory + for the command is root + ('/') in the container's + filesystem. The command + is simply exec'd, it is + not run inside a shell, + so traditional shell instructions + ('|', etc) won't work. + To use a shell, you need + to explicitly call out + to that shell. Exit status + of 0 is treated as live/healthy + and non-zero is unhealthy. + type: array + items: + type: string + httpGet: + description: HTTPGet specifies + the http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to + connect to, defaults to + the pod IP. You probably + want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers + to set in the request. + HTTP allows repeated headers. + type: array + items: + description: HTTPHeader + describes a custom header + to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + path: + description: Path to access + on the HTTP server. + type: string + port: + description: Name or number + of the port to access + on the container. Number + must be in the range 1 + to 65535. Name must be + an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use + for connecting to the + host. Defaults to HTTP. + type: string + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP + port. TCP hooks not yet supported + TODO: implement a realistic + TCP lifecycle hook' + type: object + required: + - port + properties: + host: + description: 'Optional: + Host name to connect to, + defaults to the pod IP.' + type: string + port: + description: Number or name + of the port to access + on the container. Number + must be in the range 1 + to 65535. Name must be + an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + preStop: + description: 'PreStop is called + immediately before a container + is terminated due to an API request + or management event such as liveness/startup + probe failure, preemption, resource + contention, etc. The handler is + not called if the container crashes + or exits. The reason for termination + is passed to the handler. The + Pod''s termination grace period + countdown begins before the PreStop + hooked is executed. Regardless + of the outcome of the handler, + the container will eventually + terminate within the Pod''s termination + grace period. Other management + of the container blocks until + the hook completes or until the + termination grace period is reached. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + type: object + properties: + exec: + description: One and only one + of the following should be + specified. Exec specifies + the action to take. + type: object + properties: + command: + description: Command is + the command line to execute + inside the container, + the working directory + for the command is root + ('/') in the container's + filesystem. The command + is simply exec'd, it is + not run inside a shell, + so traditional shell instructions + ('|', etc) won't work. + To use a shell, you need + to explicitly call out + to that shell. Exit status + of 0 is treated as live/healthy + and non-zero is unhealthy. + type: array + items: + type: string + httpGet: + description: HTTPGet specifies + the http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to + connect to, defaults to + the pod IP. You probably + want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers + to set in the request. + HTTP allows repeated headers. + type: array + items: + description: HTTPHeader + describes a custom header + to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + path: + description: Path to access + on the HTTP server. + type: string + port: + description: Name or number + of the port to access + on the container. Number + must be in the range 1 + to 65535. Name must be + an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use + for connecting to the + host. Defaults to HTTP. + type: string + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP + port. TCP hooks not yet supported + TODO: implement a realistic + TCP lifecycle hook' + type: object + required: + - port + properties: + host: + description: 'Optional: + Host name to connect to, + defaults to the pod IP.' + type: string + port: + description: Number or name + of the port to access + on the container. Number + must be in the range 1 + to 65535. Name must be + an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + livenessProbe: + description: 'Periodic probe of container + liveness. Container will be restarted + if the probe fails. Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: object + properties: + exec: + description: One and only one of + the following should be specified. + Exec specifies the action to take. + type: object + properties: + command: + description: Command is the + command line to execute inside + the container, the working + directory for the command is + root ('/') in the container's + filesystem. The command is + simply exec'd, it is not run + inside a shell, so traditional + shell instructions ('|', etc) + won't work. To use a shell, + you need to explicitly call + out to that shell. Exit status + of 0 is treated as live/healthy + and non-zero is unhealthy. + type: array + items: + type: string + failureThreshold: + description: Minimum consecutive + failures for the probe to be considered + failed after having succeeded. + Defaults to 3. Minimum value is + 1. + type: integer + format: int32 + httpGet: + description: HTTPGet specifies the + http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to connect + to, defaults to the pod IP. + You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers + to set in the request. HTTP + allows repeated headers. + type: array + items: + description: HTTPHeader describes + a custom header to be used + in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + path: + description: Path to access + on the HTTP server. + type: string + port: + description: Name or number + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for + connecting to the host. Defaults + to HTTP. + type: string + initialDelaySeconds: + description: 'Number of seconds + after the container has started + before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + periodSeconds: + description: How often (in seconds) + to perform the probe. Default + to 10 seconds. Minimum value is + 1. + type: integer + format: int32 + successThreshold: + description: Minimum consecutive + successes for the probe to be + considered successful after having + failed. Defaults to 1. Must be + 1 for liveness and startup. Minimum + value is 1. + type: integer + format: int32 + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP port. + TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle + hook' + type: object + required: + - port + properties: + host: + description: 'Optional: Host + name to connect to, defaults + to the pod IP.' + type: string + port: + description: Number or name + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: 'Number of seconds + after which the probe times out. + Defaults to 1 second. Minimum + value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + name: + description: Name of the container specified + as a DNS_LABEL. Each container in + a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: List of ports to expose + from the container. Exposing a port + here gives the system additional information + about the network connections a container + uses, but is primarily informational. + Not specifying a port here DOES NOT + prevent that port from being exposed. + Any port which is listening on the + default "0.0.0.0" address inside a + container will be accessible from + the network. Cannot be updated. + type: array + items: + description: ContainerPort represents + a network port in a single container. + type: object + required: + - containerPort + properties: + containerPort: + description: Number of port to + expose on the pod's IP address. + This must be a valid port number, + 0 < x < 65536. + type: integer + format: int32 + hostIP: + description: What host IP to bind + the external port to. + type: string + hostPort: + description: Number of port to + expose on the host. If specified, + this must be a valid port number, + 0 < x < 65536. If HostNetwork + is specified, this must match + ContainerPort. Most containers + do not need this. + type: integer + format: int32 + name: + description: If specified, this + must be an IANA_SVC_NAME and + unique within the pod. Each + named port in a pod must have + a unique name. Name for the + port that can be referred to + by services. + type: string + protocol: + description: Protocol for port. + Must be UDP, TCP, or SCTP. Defaults + to "TCP". + type: string + readinessProbe: + description: 'Periodic probe of container + service readiness. Container will + be removed from service endpoints + if the probe fails. Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: object + properties: + exec: + description: One and only one of + the following should be specified. + Exec specifies the action to take. + type: object + properties: + command: + description: Command is the + command line to execute inside + the container, the working + directory for the command is + root ('/') in the container's + filesystem. The command is + simply exec'd, it is not run + inside a shell, so traditional + shell instructions ('|', etc) + won't work. To use a shell, + you need to explicitly call + out to that shell. Exit status + of 0 is treated as live/healthy + and non-zero is unhealthy. + type: array + items: + type: string + failureThreshold: + description: Minimum consecutive + failures for the probe to be considered + failed after having succeeded. + Defaults to 3. Minimum value is + 1. + type: integer + format: int32 + httpGet: + description: HTTPGet specifies the + http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to connect + to, defaults to the pod IP. + You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers + to set in the request. HTTP + allows repeated headers. + type: array + items: + description: HTTPHeader describes + a custom header to be used + in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + path: + description: Path to access + on the HTTP server. + type: string + port: + description: Name or number + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for + connecting to the host. Defaults + to HTTP. + type: string + initialDelaySeconds: + description: 'Number of seconds + after the container has started + before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + periodSeconds: + description: How often (in seconds) + to perform the probe. Default + to 10 seconds. Minimum value is + 1. + type: integer + format: int32 + successThreshold: + description: Minimum consecutive + successes for the probe to be + considered successful after having + failed. Defaults to 1. Must be + 1 for liveness and startup. Minimum + value is 1. + type: integer + format: int32 + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP port. + TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle + hook' + type: object + required: + - port + properties: + host: + description: 'Optional: Host + name to connect to, defaults + to the pod IP.' + type: string + port: + description: Number or name + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: 'Number of seconds + after which the probe times out. + Defaults to 1 second. Minimum + value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + resources: + description: 'Compute Resources required + by this container. Cannot be updated. + More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + properties: + limits: + description: 'Limits describes the + maximum amount of compute resources + allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + additionalProperties: + type: string + requests: + description: 'Requests describes + the minimum amount of compute + resources required. If Requests + is omitted for a container, it + defaults to Limits if that is + explicitly specified, otherwise + to an implementation-defined value. + More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + additionalProperties: + type: string + securityContext: + description: 'Security options the pod + should run with. More info: https://kubernetes.io/docs/concepts/policy/security-context/ + More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' + type: object + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation + controls whether a process can + gain more privileges than its + parent process. This bool directly + controls if the no_new_privs flag + will be set on the container process. + AllowPrivilegeEscalation is true + always when the container is: + 1) run as Privileged 2) has CAP_SYS_ADMIN' + type: boolean + capabilities: + description: The capabilities to + add/drop when running containers. + Defaults to the default set of + capabilities granted by the container + runtime. + type: object + properties: + add: + description: Added capabilities + type: array + items: + description: Capability represent + POSIX capabilities type + type: string + drop: + description: Removed capabilities + type: array + items: + description: Capability represent + POSIX capabilities type + type: string + privileged: + description: Run container in privileged + mode. Processes in privileged + containers are essentially equivalent + to root on the host. Defaults + to false. + type: boolean + procMount: + description: procMount denotes the + type of proc mount to use for + the containers. The default is + DefaultProcMount which uses the + container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType + feature flag to be enabled. + type: string + readOnlyRootFilesystem: + description: Whether this container + has a read-only root filesystem. + Default is false. + type: boolean + runAsGroup: + description: The GID to run the + entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If + set in both SecurityContext and + PodSecurityContext, the value + specified in SecurityContext takes + precedence. + type: integer + format: int64 + runAsNonRoot: + description: Indicates that the + container must run as a non-root + user. If true, the Kubelet will + validate the image at runtime + to ensure that it does not run + as UID 0 (root) and fail to start + the container if it does. If unset + or false, no such validation will + be performed. May also be set + in PodSecurityContext. If set + in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext + takes precedence. + type: boolean + runAsUser: + description: The UID to run the + entrypoint of the container process. + Defaults to user specified in + image metadata if unspecified. + May also be set in PodSecurityContext. If + set in both SecurityContext and + PodSecurityContext, the value + specified in SecurityContext takes + precedence. + type: integer + format: int64 + seLinuxOptions: + description: The SELinux context + to be applied to the container. + If unspecified, the container + runtime will allocate a random + SELinux context for each container. May + also be set in PodSecurityContext. If + set in both SecurityContext and + PodSecurityContext, the value + specified in SecurityContext takes + precedence. + type: object + properties: + level: + description: Level is SELinux + level label that applies to + the container. + type: string + role: + description: Role is a SELinux + role label that applies to + the container. + type: string + type: + description: Type is a SELinux + type label that applies to + the container. + type: string + user: + description: User is a SELinux + user label that applies to + the container. + type: string + windowsOptions: + description: The Windows specific + settings applied to all containers. + If unspecified, the options from + the PodSecurityContext will be + used. If set in both SecurityContext + and PodSecurityContext, the value + specified in SecurityContext takes + precedence. + type: object + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec + is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the + GMSA credential spec named + by the GMSACredentialSpecName + field. This field is alpha-level + and is only honored by servers + that enable the WindowsGMSA + feature flag. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName + is the name of the GMSA credential + spec to use. This field is + alpha-level and is only honored + by servers that enable the + WindowsGMSA feature flag. + type: string + runAsUserName: + description: The UserName in + Windows to run the entrypoint + of the container process. + Defaults to the user specified + in image metadata if unspecified. + May also be set in PodSecurityContext. + If set in both SecurityContext + and PodSecurityContext, the + value specified in SecurityContext + takes precedence. This field + is alpha-level and it is only + honored by servers that enable + the WindowsRunAsUserName feature + flag. + type: string + startupProbe: + description: 'StartupProbe indicates + that the Pod has successfully initialized. + If specified, no other probes are + executed until this completes successfully. + If this probe fails, the Pod will + be restarted, just as if the livenessProbe + failed. This can be used to provide + different probe parameters at the + beginning of a Pod''s lifecycle, when + it might take a long time to load + data or warm a cache, than during + steady-state operation. This cannot + be updated. This is an alpha feature + enabled by the StartupProbe feature + flag. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: object + properties: + exec: + description: One and only one of + the following should be specified. + Exec specifies the action to take. + type: object + properties: + command: + description: Command is the + command line to execute inside + the container, the working + directory for the command is + root ('/') in the container's + filesystem. The command is + simply exec'd, it is not run + inside a shell, so traditional + shell instructions ('|', etc) + won't work. To use a shell, + you need to explicitly call + out to that shell. Exit status + of 0 is treated as live/healthy + and non-zero is unhealthy. + type: array + items: + type: string + failureThreshold: + description: Minimum consecutive + failures for the probe to be considered + failed after having succeeded. + Defaults to 3. Minimum value is + 1. + type: integer + format: int32 + httpGet: + description: HTTPGet specifies the + http request to perform. + type: object + required: + - port + properties: + host: + description: Host name to connect + to, defaults to the pod IP. + You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers + to set in the request. HTTP + allows repeated headers. + type: array + items: + description: HTTPHeader describes + a custom header to be used + in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: The header + field name + type: string + value: + description: The header + field value + type: string + path: + description: Path to access + on the HTTP server. + type: string + port: + description: Name or number + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for + connecting to the host. Defaults + to HTTP. + type: string + initialDelaySeconds: + description: 'Number of seconds + after the container has started + before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + periodSeconds: + description: How often (in seconds) + to perform the probe. Default + to 10 seconds. Minimum value is + 1. + type: integer + format: int32 + successThreshold: + description: Minimum consecutive + successes for the probe to be + considered successful after having + failed. Defaults to 1. Must be + 1 for liveness and startup. Minimum + value is 1. + type: integer + format: int32 + tcpSocket: + description: 'TCPSocket specifies + an action involving a TCP port. + TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle + hook' + type: object + required: + - port + properties: + host: + description: 'Optional: Host + name to connect to, defaults + to the pod IP.' + type: string + port: + description: Number or name + of the port to access on the + container. Number must be + in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: 'Number of seconds + after which the probe times out. + Defaults to 1 second. Minimum + value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + type: integer + format: int32 + stdin: + description: Whether this container + should allocate a buffer for stdin + in the container runtime. If this + is not set, reads from stdin in the + container will always result in EOF. + Default is false. + type: boolean + stdinOnce: + description: Whether the container runtime + should close the stdin channel after + it has been opened by a single attach. + When stdin is true the stdin stream + will remain open across multiple attach + sessions. If stdinOnce is set to true, + stdin is opened on container start, + is empty until the first client attaches + to stdin, and then remains open and + accepts data until the client disconnects, + at which time stdin is closed and + remains closed until the container + is restarted. If this flag is false, + a container processes that reads from + stdin will never receive an EOF. Default + is false + type: boolean + terminationMessagePath: + description: 'Optional: Path at which + the file to which the container''s + termination message will be written + is mounted into the container''s filesystem. + Message written is intended to be + brief final status, such as an assertion + failure message. Will be truncated + by the node if greater than 4096 bytes. + The total message length across all + containers will be limited to 12kb. + Defaults to /dev/termination-log. + Cannot be updated.' + type: string + terminationMessagePolicy: + description: Indicate how the termination + message should be populated. File + will use the contents of terminationMessagePath + to populate the container status message + on both success and failure. FallbackToLogsOnError + will use the last chunk of container + log output if the termination message + file is empty and the container exited + with an error. The log output is limited + to 2048 bytes or 80 lines, whichever + is smaller. Defaults to File. Cannot + be updated. + type: string + tty: + description: Whether this container + should allocate a TTY for itself, + also requires 'stdin' to be true. + Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list + of block devices to be used by the + container. This is a beta feature. + type: array + items: + description: volumeDevice describes + a mapping of a raw block device + within a container. + type: object + required: + - devicePath + - name + properties: + devicePath: + description: devicePath is the + path inside of the container + that the device will be mapped + to. + type: string + name: + description: name must match the + name of a persistentVolumeClaim + in the pod + type: string + volumeMounts: + description: Pod volumes to mount into + the container's filesystem. Cannot + be updated. + type: array + items: + description: VolumeMount describes + a mounting of a Volume within a + container. + type: object + required: + - mountPath + - name + properties: + mountPath: + description: Path within the container + at which the volume should be + mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation + determines how mounts are propagated + from the host to container and + the other way around. When not + set, MountPropagationNone is + used. This field is beta in + 1.10. + type: string + name: + description: This must match the + Name of a Volume. + type: string + readOnly: + description: Mounted read-only + if true, read-write otherwise + (false or unspecified). Defaults + to false. + type: boolean + subPath: + description: Path within the volume + from which the container's volume + should be mounted. Defaults + to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within + the volume from which the container's + volume should be mounted. Behaves + similarly to SubPath but environment + variable references $(VAR_NAME) + are expanded using the container's + environment. Defaults to "" + (volume's root). SubPathExpr + and SubPath are mutually exclusive. + This field is beta in 1.15. + type: string + workingDir: + description: Container's working directory. + If not specified, the container runtime's + default will be used, which might + be configured in the container image. + Cannot be updated. + type: string + nodeName: + description: NodeName is a request to schedule + this pod onto a specific node. If it is + non-empty, the scheduler simply schedules + this pod onto that node, assuming that it + fits resource requirements. + type: string + nodeSelector: + description: 'NodeSelector is a selector which + must be true for the pod to fit on a node. + Selector which must match a node''s labels + for the pod to be scheduled on that node. + More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/' + type: object + additionalProperties: + type: string + overhead: + description: 'Overhead represents the resource + overhead associated with running a pod for + a given RuntimeClass. This field will be + autopopulated at admission time by the RuntimeClass + admission controller. If the RuntimeClass + admission controller is enabled, overhead + must not be set in Pod create requests. + The RuntimeClass admission controller will + reject Pod create requests which have the + overhead already set. If RuntimeClass is + configured and selected in the PodSpec, + Overhead will be set to the value defined + in the corresponding RuntimeClass, otherwise + it will remain unset and treated as zero. + More info: https://git.k8s.io/enhancements/keps/sig-node/20190226-pod-overhead.md + This field is alpha-level as of Kubernetes + v1.16, and is only honored by servers that + enable the PodOverhead feature.' + type: object + additionalProperties: + type: string + preemptionPolicy: + description: PreemptionPolicy is the Policy + for preempting pods with lower priority. + One of Never, PreemptLowerPriority. Defaults + to PreemptLowerPriority if unset. This field + is alpha-level and is only honored by servers + that enable the NonPreemptingPriority feature. + type: string + priority: + description: The priority value. Various system + components use this field to find the priority + of the pod. When Priority Admission Controller + is enabled, it prevents users from setting + this field. The admission controller populates + this field from PriorityClassName. The higher + the value, the higher the priority. + type: integer + format: int32 + priorityClassName: + description: If specified, indicates the pod's + priority. "system-node-critical" and "system-cluster-critical" + are two special keywords which indicate + the highest priorities with the former being + the highest priority. Any other name must + be defined by creating a PriorityClass object + with that name. If not specified, the pod + priority will be default or zero if there + is no default. + type: string + readinessGates: + description: 'If specified, all readiness + gates will be evaluated for pod readiness. + A pod is ready when all its containers are + ready AND all conditions specified in the + readiness gates have status equal to "True" + More info: https://git.k8s.io/enhancements/keps/sig-network/0007-pod-ready%2B%2B.md' + type: array + items: + description: PodReadinessGate contains the + reference to a pod condition + type: object + required: + - conditionType + properties: + conditionType: + description: ConditionType refers to + a condition in the pod's condition + list with matching type. + type: string + restartPolicy: + description: 'Restart policy for all containers + within the pod. One of Always, OnFailure, + Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy' + type: string + runtimeClassName: + description: 'RuntimeClassName refers to a + RuntimeClass object in the node.k8s.io group, + which should be used to run this pod. If + no RuntimeClass resource matches the named + class, the pod will not be run. If unset + or empty, the "legacy" RuntimeClass will + be used, which is an implicit class with + an empty definition that uses the default + runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/runtime-class.md + This is a beta feature as of Kubernetes + v1.14.' + type: string + schedulerName: + description: If specified, the pod will be + dispatched by specified scheduler. If not + specified, the pod will be dispatched by + default scheduler. + type: string + securityContext: + description: 'SecurityContext holds pod-level + security attributes and common container + settings. Optional: Defaults to empty. See + type description for default values of each + field.' + type: object + properties: + fsGroup: + description: "A special supplemental group + that applies to all containers in a + pod. Some volume types allow the Kubelet + to change the ownership of that volume + to be owned by the pod: \n 1. The owning + GID will be the FSGroup 2. The setgid + bit is set (new files created in the + volume will be owned by FSGroup) 3. + The permission bits are OR'd with rw-rw---- + \n If unset, the Kubelet will not modify + the ownership and permissions of any + volume." + type: integer + format: int64 + runAsGroup: + description: The GID to run the entrypoint + of the container process. Uses runtime + default if unset. May also be set in + SecurityContext. If set in both SecurityContext + and PodSecurityContext, the value specified + in SecurityContext takes precedence + for that container. + type: integer + format: int64 + runAsNonRoot: + description: Indicates that the container + must run as a non-root user. If true, + the Kubelet will validate the image + at runtime to ensure that it does not + run as UID 0 (root) and fail to start + the container if it does. If unset or + false, no such validation will be performed. + May also be set in SecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext + takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint + of the container process. Defaults to + user specified in image metadata if + unspecified. May also be set in SecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext + takes precedence for that container. + type: integer + format: int64 + seLinuxOptions: + description: The SELinux context to be + applied to all containers. If unspecified, + the container runtime will allocate + a random SELinux context for each container. May + also be set in SecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext + takes precedence for that container. + type: object + properties: + level: + description: Level is SELinux level + label that applies to the container. + type: string + role: + description: Role is a SELinux role + label that applies to the container. + type: string + type: + description: Type is a SELinux type + label that applies to the container. + type: string + user: + description: User is a SELinux user + label that applies to the container. + type: string + supplementalGroups: + description: A list of groups applied + to the first process run in each container, + in addition to the container's primary + GID. If unspecified, no groups will + be added to any container. + type: array + items: + type: integer + format: int64 + sysctls: + description: Sysctls hold a list of namespaced + sysctls used for the pod. Pods with + unsupported sysctls (by the container + runtime) might fail to launch. + type: array + items: + description: Sysctl defines a kernel + parameter to be set + type: object + required: + - name + - value + properties: + name: + description: Name of a property + to set + type: string + value: + description: Value of a property + to set + type: string + windowsOptions: + description: The Windows specific settings + applied to all containers. If unspecified, + the options within a container's SecurityContext + will be used. If set in both SecurityContext + and PodSecurityContext, the value specified + in SecurityContext takes precedence. + type: object + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is + where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA + credential spec named by the GMSACredentialSpecName + field. This field is alpha-level + and is only honored by servers that + enable the WindowsGMSA feature flag. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName + is the name of the GMSA credential + spec to use. This field is alpha-level + and is only honored by servers that + enable the WindowsGMSA feature flag. + type: string + runAsUserName: + description: The UserName in Windows + to run the entrypoint of the container + process. Defaults to the user specified + in image metadata if unspecified. + May also be set in PodSecurityContext. + If set in both SecurityContext and + PodSecurityContext, the value specified + in SecurityContext takes precedence. + This field is alpha-level and it + is only honored by servers that + enable the WindowsRunAsUserName + feature flag. + type: string + serviceAccount: + description: 'DeprecatedServiceAccount is + a depreciated alias for ServiceAccountName. + Deprecated: Use serviceAccountName instead.' + type: string + serviceAccountName: + description: 'ServiceAccountName is the name + of the ServiceAccount to use to run this + pod. More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/' + type: string + shareProcessNamespace: + description: 'Share a single process namespace + between all of the containers in a pod. + When this is set containers will be able + to view and signal processes from other + containers in the same pod, and the first + process in each container will not be assigned + PID 1. HostPID and ShareProcessNamespace + cannot both be set. Optional: Default to + false. This field is beta-level and may + be disabled with the PodShareProcessNamespace + feature.' + type: boolean + subdomain: + description: If specified, the fully qualified + Pod hostname will be "...svc.". If not + specified, the pod will not have a domainname + at all. + type: string + terminationGracePeriodSeconds: + description: Optional duration in seconds + the pod needs to terminate gracefully. May + be decreased in delete request. Value must + be non-negative integer. The value zero + indicates delete immediately. If this value + is nil, the default grace period will be + used instead. The grace period is the duration + in seconds after the processes running in + the pod are sent a termination signal and + the time when the processes are forcibly + halted with a kill signal. Set this value + longer than the expected cleanup time for + your process. Defaults to 30 seconds. + type: integer + format: int64 + tolerations: + description: If specified, the pod's tolerations. + type: array + items: + description: The pod this Toleration is + attached to tolerates any taint that matches + the triple using the + matching operator . + type: object + properties: + effect: + description: Effect indicates the taint + effect to match. Empty means match + all taint effects. When specified, + allowed values are NoSchedule, PreferNoSchedule + and NoExecute. + type: string + key: + description: Key is the taint key that + the toleration applies to. Empty means + match all taint keys. If the key is + empty, operator must be Exists; this + combination means to match all values + and all keys. + type: string + operator: + description: Operator represents a key's + relationship to the value. Valid operators + are Exists and Equal. Defaults to + Equal. Exists is equivalent to wildcard + for value, so that a pod can tolerate + all taints of a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents + the period of time the toleration + (which must be of effect NoExecute, + otherwise this field is ignored) tolerates + the taint. By default, it is not set, + which means tolerate the taint forever + (do not evict). Zero and negative + values will be treated as 0 (evict + immediately) by the system. + type: integer + format: int64 + value: + description: Value is the taint value + the toleration matches to. If the + operator is Exists, the value should + be empty, otherwise just a regular + string. + type: string + topologySpreadConstraints: + description: TopologySpreadConstraints describes + how a group of pods ought to spread across + topology domains. Scheduler will schedule + pods in a way which abides by the constraints. + This field is alpha-level and is only honored + by clusters that enables the EvenPodsSpread + feature. All topologySpreadConstraints are + ANDed. + type: array + items: + description: TopologySpreadConstraint specifies + how to spread matching pods among the + given topology. + type: object + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + properties: + labelSelector: + description: LabelSelector is used to + find matching pods. Pods that match + this label selector are counted to + determine the number of pods in their + corresponding topology domain. + type: object + properties: + matchExpressions: + description: matchExpressions is + a list of label selector requirements. + The requirements are ANDed. + type: array + items: + description: A label selector + requirement is a selector that + contains values, a key, and + an operator that relates the + key and values. + type: object + required: + - key + - operator + properties: + key: + description: key is the label + key that the selector applies + to. + type: string + operator: + description: operator represents + a key's relationship to + a set of values. Valid operators + are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an + array of string values. + If the operator is In or + NotIn, the values array + must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be + empty. This array is replaced + during a strategic merge + patch. + type: array + items: + type: string + matchLabels: + description: matchLabels is a map + of {key,value} pairs. A single + {key,value} in the matchLabels + map is equivalent to an element + of matchExpressions, whose key + field is "key", the operator is + "In", and the values array contains + only "value". The requirements + are ANDed. + type: object + additionalProperties: + type: string + maxSkew: + description: 'MaxSkew describes the + degree to which pods may be unevenly + distributed. It''s the maximum permitted + difference between the number of matching + pods in any two topology domains of + a given topology type. For example, + in a 3-zone cluster, MaxSkew is set + to 1, and pods with the same labelSelector + spread as 1/1/0: | zone1 | zone2 | + zone3 | | P | P | | + - if MaxSkew is 1, incoming pod can + only be scheduled to zone3 to become + 1/1/1; scheduling it onto zone1(zone2) + would make the ActualSkew(2-0) on + zone1(zone2) violate MaxSkew(1). - + if MaxSkew is 2, incoming pod can + be scheduled onto any zone. It''s + a required field. Default value is + 1 and 0 is not allowed.' + type: integer + format: int32 + topologyKey: + description: TopologyKey is the key + of node labels. Nodes that have a + label with this key and identical + values are considered to be in the + same topology. We consider each as a "bucket", and try to put + balanced number of pods into each + bucket. It's a required field. + type: string + whenUnsatisfiable: + description: 'WhenUnsatisfiable indicates + how to deal with a pod if it doesn''t + satisfy the spread constraint. - DoNotSchedule + (default) tells the scheduler not + to schedule it - ScheduleAnyway tells + the scheduler to still schedule it + It''s considered as "Unsatisfiable" + if and only if placing incoming pod + on any topology violates "MaxSkew". + For example, in a 3-zone cluster, + MaxSkew is set to 1, and pods with + the same labelSelector spread as 3/1/1: + | zone1 | zone2 | zone3 | | P P P + | P | P | If WhenUnsatisfiable + is set to DoNotSchedule, incoming + pod can only be scheduled to zone2(zone3) + to become 3/2/1(3/1/2) as ActualSkew(2-1) + on zone2(zone3) satisfies MaxSkew(1). + In other words, the cluster can still + be imbalanced, but scheduler won''t + make it *more* imbalanced. It''s a + required field.' + type: string + volumes: + description: 'List of volumes that can be + mounted by containers belonging to the pod. + More info: https://kubernetes.io/docs/concepts/storage/volumes' + type: array + items: + description: Volume represents a named volume + in a pod that may be accessed by any container + in the pod. + type: object + required: + - name + properties: + awsElasticBlockStore: + description: 'AWSElasticBlockStore represents + an AWS Disk resource that is attached + to a kubelet''s host machine and then + exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: object + required: + - volumeID + properties: + fsType: + description: 'Filesystem type of + the volume that you want to mount. + Tip: Ensure that the filesystem + type is supported by the host + operating system. Examples: "ext4", + "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. More + info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors + in the filesystem from compromising + the machine' + type: string + partition: + description: 'The partition in the + volume that you want to mount. + If omitted, the default is to + mount by volume name. Examples: + For volume /dev/sda1, you specify + the partition as "1". Similarly, + the volume partition for /dev/sda + is "0" (or you can leave the property + empty).' + type: integer + format: int32 + readOnly: + description: 'Specify "true" to + force and set the ReadOnly property + in VolumeMounts to "true". If + omitted, the default is "false". + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'Unique ID of the persistent + disk resource in AWS (Amazon EBS + volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + azureDisk: + description: AzureDisk represents an + Azure Data Disk mount on the host + and bind mount to the pod. + type: object + required: + - diskName + - diskURI + properties: + cachingMode: + description: 'Host Caching mode: + None, Read Only, Read Write.' + type: string + diskName: + description: The Name of the data + disk in the blob storage + type: string + diskURI: + description: The URI the data disk + in the blob storage + type: string + fsType: + description: Filesystem type to + mount. Must be a filesystem type + supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" + if unspecified. + type: string + kind: + description: 'Expected values Shared: + multiple blob disks per storage + account Dedicated: single blob + disk per storage account Managed: + azure managed data disk (only + in managed availability set). + defaults to shared' + type: string + readOnly: + description: Defaults to false (read/write). + ReadOnly here will force the ReadOnly + setting in VolumeMounts. + type: boolean + azureFile: + description: AzureFile represents an + Azure File Service mount on the host + and bind mount to the pod. + type: object + required: + - secretName + - shareName + properties: + readOnly: + description: Defaults to false (read/write). + ReadOnly here will force the ReadOnly + setting in VolumeMounts. + type: boolean + secretName: + description: the name of secret + that contains Azure Storage Account + Name and Key + type: string + shareName: + description: Share Name + type: string + cephfs: + description: CephFS represents a Ceph + FS mount on the host that shares a + pod's lifetime + type: object + required: + - monitors + properties: + monitors: + description: 'Required: Monitors + is a collection of Ceph monitors + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: array + items: + type: string + path: + description: 'Optional: Used as + the mounted root, rather than + the full Ceph tree, default is + /' + type: string + readOnly: + description: 'Optional: Defaults + to false (read/write). ReadOnly + here will force the ReadOnly setting + in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'Optional: SecretFile + is the path to key ring for User, + default is /etc/ceph/user.secret + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'Optional: SecretRef + is reference to the authentication + secret for User, default is empty. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: object + properties: + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + user: + description: 'Optional: User is + the rados user name, default is + admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + cinder: + description: 'Cinder represents a cinder + volume attached and mounted on kubelets + host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: object + required: + - volumeID + properties: + fsType: + description: 'Filesystem type to + mount. Must be a filesystem type + supported by the host operating + system. Examples: "ext4", "xfs", + "ntfs". Implicitly inferred to + be "ext4" if unspecified. More + info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + readOnly: + description: 'Optional: Defaults + to false (read/write). ReadOnly + here will force the ReadOnly setting + in VolumeMounts. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'Optional: points to + a secret object containing parameters + used to connect to OpenStack.' + type: object + properties: + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + volumeID: + description: 'volume id used to + identify the volume in cinder. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + configMap: + description: ConfigMap represents a + configMap that should populate this + volume + type: object + properties: + defaultMode: + description: 'Optional: mode bits + to use on created files by default. + Must be a value between 0 and + 0777. Defaults to 0644. Directories + within the path are not affected + by this setting. This might be + in conflict with other options + that affect the file mode, like + fsGroup, and the result can be + other mode bits set.' + type: integer + format: int32 + items: + description: If unspecified, each + key-value pair in the Data field + of the referenced ConfigMap will + be projected into the volume as + a file whose name is the key and + content is the value. If specified, + the listed keys will be projected + into the specified paths, and + unlisted keys will not be present. + If a key is specified which is + not present in the ConfigMap, + the volume setup will error unless + it is marked optional. Paths must + be relative and may not contain + the '..' path or start with '..'. + type: array + items: + description: Maps a string key + to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode + bits to use on this file, + must be a value between + 0 and 0777. If not specified, + the volume defaultMode will + be used. This might be in + conflict with other options + that affect the file mode, + like fsGroup, and the result + can be other mode bits set.' + type: integer + format: int32 + path: + description: The relative + path of the file to map + the key to. May not be an + absolute path. May not contain + the path element '..'. May + not start with the string + '..'. + type: string + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the + ConfigMap or its keys must be + defined + type: boolean + csi: + description: CSI (Container Storage + Interface) represents storage that + is handled by an external CSI driver + (Alpha feature). + type: object + required: + - driver + properties: + driver: + description: Driver is the name + of the CSI driver that handles + this volume. Consult with your + admin for the correct name as + registered in the cluster. + type: string + fsType: + description: Filesystem type to + mount. Ex. "ext4", "xfs", "ntfs". + If not provided, the empty value + is passed to the associated CSI + driver which will determine the + default filesystem to apply. + type: string + nodePublishSecretRef: + description: NodePublishSecretRef + is a reference to the secret object + containing sensitive information + to pass to the CSI driver to complete + the CSI NodePublishVolume and + NodeUnpublishVolume calls. This + field is optional, and may be + empty if no secret is required. + If the secret object contains + more than one secret, all secret + references are passed. + type: object + properties: + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + readOnly: + description: Specifies a read-only + configuration for the volume. + Defaults to false (read/write). + type: boolean + volumeAttributes: + description: VolumeAttributes stores + driver-specific properties that + are passed to the CSI driver. + Consult your driver's documentation + for supported values. + type: object + additionalProperties: + type: string + downwardAPI: + description: DownwardAPI represents + downward API about the pod that should + populate this volume + type: object + properties: + defaultMode: + description: 'Optional: mode bits + to use on created files by default. + Must be a value between 0 and + 0777. Defaults to 0644. Directories + within the path are not affected + by this setting. This might be + in conflict with other options + that affect the file mode, like + fsGroup, and the result can be + other mode bits set.' + type: integer + format: int32 + items: + description: Items is a list of + downward API volume file + type: array + items: + description: DownwardAPIVolumeFile + represents information to create + the file containing the pod + field + type: object + required: + - path + properties: + fieldRef: + description: 'Required: Selects + a field of the pod: only + annotations, labels, name + and namespace are supported.' + type: object + required: + - fieldPath + properties: + apiVersion: + description: Version of + the schema the FieldPath + is written in terms + of, defaults to "v1". + type: string + fieldPath: + description: Path of the + field to select in the + specified API version. + type: string + mode: + description: 'Optional: mode + bits to use on this file, + must be a value between + 0 and 0777. If not specified, + the volume defaultMode will + be used. This might be in + conflict with other options + that affect the file mode, + like fsGroup, and the result + can be other mode bits set.' + type: integer + format: int32 + path: + description: 'Required: Path + is the relative path name + of the file to be created. + Must not be absolute or + contain the ''..'' path. + Must be utf-8 encoded. The + first item of the relative + path must not start with + ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource + of the container: only resources + limits and requests (limits.cpu, + limits.memory, requests.cpu + and requests.memory) are + currently supported.' + type: object + required: + - resource + properties: + containerName: + description: 'Container + name: required for volumes, + optional for env vars' + type: string + divisor: + description: Specifies + the output format of + the exposed resources, + defaults to "1" + type: string + resource: + description: 'Required: + resource to select' + type: string + emptyDir: + description: 'EmptyDir represents a + temporary directory that shares a + pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: object + properties: + medium: + description: 'What type of storage + medium should back this directory. + The default is "" which means + to use the node''s default medium. + Must be an empty string (default) + or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: string + sizeLimit: + description: 'Total amount of local + storage required for this EmptyDir + volume. The size limit is also + applicable for memory medium. + The maximum usage on memory medium + EmptyDir would be the minimum + value between the SizeLimit specified + here and the sum of memory limits + of all containers in a pod. The + default is nil which means that + the limit is undefined. More info: + http://kubernetes.io/docs/user-guide/volumes#emptydir' + type: string + fc: + description: FC represents a Fibre Channel + resource that is attached to a kubelet's + host machine and then exposed to the + pod. + type: object + properties: + fsType: + description: 'Filesystem type to + mount. Must be a filesystem type + supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" + if unspecified. TODO: how do we + prevent errors in the filesystem + from compromising the machine' + type: string + lun: + description: 'Optional: FC target + lun number' + type: integer + format: int32 + readOnly: + description: 'Optional: Defaults + to false (read/write). ReadOnly + here will force the ReadOnly setting + in VolumeMounts.' + type: boolean + targetWWNs: + description: 'Optional: FC target + worldwide names (WWNs)' + type: array + items: + type: string + wwids: + description: 'Optional: FC volume + world wide identifiers (wwids) + Either wwids or combination of + targetWWNs and lun must be set, + but not both simultaneously.' + type: array + items: + type: string + flexVolume: + description: FlexVolume represents a + generic volume resource that is provisioned/attached + using an exec based plugin. + type: object + required: + - driver + properties: + driver: + description: Driver is the name + of the driver to use for this + volume. + type: string + fsType: + description: Filesystem type to + mount. Must be a filesystem type + supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". + The default filesystem depends + on FlexVolume script. + type: string + options: + description: 'Optional: Extra command + options if any.' + type: object + additionalProperties: + type: string + readOnly: + description: 'Optional: Defaults + to false (read/write). ReadOnly + here will force the ReadOnly setting + in VolumeMounts.' + type: boolean + secretRef: + description: 'Optional: SecretRef + is reference to the secret object + containing sensitive information + to pass to the plugin scripts. + This may be empty if no secret + object is specified. If the secret + object contains more than one + secret, all secrets are passed + to the plugin scripts.' + type: object + properties: + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + flocker: + description: Flocker represents a Flocker + volume attached to a kubelet's host + machine. This depends on the Flocker + control service being running + type: object + properties: + datasetName: + description: Name of the dataset + stored as metadata -> name on + the dataset for Flocker should + be considered as deprecated + type: string + datasetUUID: + description: UUID of the dataset. + This is unique identifier of a + Flocker dataset + type: string + gcePersistentDisk: + description: 'GCEPersistentDisk represents + a GCE Disk resource that is attached + to a kubelet''s host machine and then + exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: object + required: + - pdName + properties: + fsType: + description: 'Filesystem type of + the volume that you want to mount. + Tip: Ensure that the filesystem + type is supported by the host + operating system. Examples: "ext4", + "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. More + info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors + in the filesystem from compromising + the machine' + type: string + partition: + description: 'The partition in the + volume that you want to mount. + If omitted, the default is to + mount by volume name. Examples: + For volume /dev/sda1, you specify + the partition as "1". Similarly, + the volume partition for /dev/sda + is "0" (or you can leave the property + empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: integer + format: int32 + pdName: + description: 'Unique name of the + PD resource in GCE. Used to identify + the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'ReadOnly here will + force the ReadOnly setting in + VolumeMounts. Defaults to false. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + gitRepo: + description: 'GitRepo represents a git + repository at a particular revision. + DEPRECATED: GitRepo is deprecated. + To provision a container with a git + repo, mount an EmptyDir into an InitContainer + that clones the repo using git, then + mount the EmptyDir into the Pod''s + container.' + type: object + required: + - repository + properties: + directory: + description: Target directory name. + Must not contain or start with + '..'. If '.' is supplied, the + volume directory will be the git + repository. Otherwise, if specified, + the volume will contain the git + repository in the subdirectory + with the given name. + type: string + repository: + description: Repository URL + type: string + revision: + description: Commit hash for the + specified revision. + type: string + glusterfs: + description: 'Glusterfs represents a + Glusterfs mount on the host that shares + a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' + type: object + required: + - endpoints + - path + properties: + endpoints: + description: 'EndpointsName is the + endpoint name that details Glusterfs + topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'Path is the Glusterfs + volume path. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'ReadOnly here will + force the Glusterfs volume to + be mounted with read-only permissions. + Defaults to false. More info: + https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + hostPath: + description: 'HostPath represents a + pre-existing file or directory on + the host machine that is directly + exposed to the container. This is + generally used for system agents or + other privileged things that are allowed + to see the host machine. Most containers + will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + --- TODO(jonesdl) We need to restrict + who can use host directory mounts + and who can/can not mount host directories + as read/write.' + type: object + required: + - path + properties: + path: + description: 'Path of the directory + on the host. If the path is a + symlink, it will follow the link + to the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'Type for HostPath + Volume Defaults to "" More info: + https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + iscsi: + description: 'ISCSI represents an ISCSI + Disk resource that is attached to + a kubelet''s host machine and then + exposed to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + type: object + required: + - iqn + - lun + - targetPortal + properties: + chapAuthDiscovery: + description: whether support iSCSI + Discovery CHAP authentication + type: boolean + chapAuthSession: + description: whether support iSCSI + Session CHAP authentication + type: boolean + fsType: + description: 'Filesystem type of + the volume that you want to mount. + Tip: Ensure that the filesystem + type is supported by the host + operating system. Examples: "ext4", + "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. More + info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors + in the filesystem from compromising + the machine' + type: string + initiatorName: + description: Custom iSCSI Initiator + Name. If initiatorName is specified + with iscsiInterface simultaneously, + new iSCSI interface : will be created for the + connection. + type: string + iqn: + description: Target iSCSI Qualified + Name. + type: string + iscsiInterface: + description: iSCSI Interface Name + that uses an iSCSI transport. + Defaults to 'default' (tcp). + type: string + lun: + description: iSCSI Target Lun number. + type: integer + format: int32 + portals: + description: iSCSI Target Portal + List. The portal is either an + IP or ip_addr:port if the port + is other than default (typically + TCP ports 860 and 3260). + type: array + items: + type: string + readOnly: + description: ReadOnly here will + force the ReadOnly setting in + VolumeMounts. Defaults to false. + type: boolean + secretRef: + description: CHAP Secret for iSCSI + target and initiator authentication + type: object + properties: + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + targetPortal: + description: iSCSI Target Portal. + The Portal is either an IP or + ip_addr:port if the port is other + than default (typically TCP ports + 860 and 3260). + type: string + name: + description: 'Volume''s name. Must be + a DNS_LABEL and unique within the + pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + nfs: + description: 'NFS represents an NFS + mount on the host that shares a pod''s + lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: object + required: + - path + - server + properties: + path: + description: 'Path that is exported + by the NFS server. More info: + https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'ReadOnly here will + force the NFS export to be mounted + with read-only permissions. Defaults + to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'Server is the hostname + or IP address of the NFS server. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + persistentVolumeClaim: + description: 'PersistentVolumeClaimVolumeSource + represents a reference to a PersistentVolumeClaim + in the same namespace. More info: + https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: object + required: + - claimName + properties: + claimName: + description: 'ClaimName is the name + of a PersistentVolumeClaim in + the same namespace as the pod + using this volume. More info: + https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: Will force the ReadOnly + setting in VolumeMounts. Default + false. + type: boolean + photonPersistentDisk: + description: PhotonPersistentDisk represents + a PhotonController persistent disk + attached and mounted on kubelets host + machine + type: object + required: + - pdID + properties: + fsType: + description: Filesystem type to + mount. Must be a filesystem type + supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" + if unspecified. + type: string + pdID: + description: ID that identifies + Photon Controller persistent disk + type: string + portworxVolume: + description: PortworxVolume represents + a portworx volume attached and mounted + on kubelets host machine + type: object + required: + - volumeID + properties: + fsType: + description: FSType represents the + filesystem type to mount Must + be a filesystem type supported + by the host operating system. + Ex. "ext4", "xfs". Implicitly + inferred to be "ext4" if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). + ReadOnly here will force the ReadOnly + setting in VolumeMounts. + type: boolean + volumeID: + description: VolumeID uniquely identifies + a Portworx volume + type: string + projected: + description: Items for all in one resources + secrets, configmaps, and downward + API + type: object + required: + - sources + properties: + defaultMode: + description: Mode bits to use on + created files by default. Must + be a value between 0 and 0777. + Directories within the path are + not affected by this setting. + This might be in conflict with + other options that affect the + file mode, like fsGroup, and the + result can be other mode bits + set. + type: integer + format: int32 + sources: + description: list of volume projections + type: array + items: + description: Projection that may + be projected along with other + supported volume types + type: object + properties: + configMap: + description: information about + the configMap data to project + type: object + properties: + items: + description: If unspecified, + each key-value pair + in the Data field of + the referenced ConfigMap + will be projected into + the volume as a file + whose name is the key + and content is the value. + If specified, the listed + keys will be projected + into the specified paths, + and unlisted keys will + not be present. If a + key is specified which + is not present in the + ConfigMap, the volume + setup will error unless + it is marked optional. + Paths must be relative + and may not contain + the '..' path or start + with '..'. + type: array + items: + description: Maps a + string key to a path + within a volume. + type: object + required: + - key + - path + properties: + key: + description: The + key to project. + type: string + mode: + description: 'Optional: + mode bits to use + on this file, + must be a value + between 0 and + 0777. If not specified, + the volume defaultMode + will be used. + This might be + in conflict with + other options + that affect the + file mode, like + fsGroup, and the + result can be + other mode bits + set.' + type: integer + format: int32 + path: + description: The + relative path + of the file to + map the key to. + May not be an + absolute path. + May not contain + the path element + '..'. May not + start with the + string '..'. + type: string + name: + description: 'Name of + the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful + fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether + the ConfigMap or its + keys must be defined + type: boolean + downwardAPI: + description: information about + the downwardAPI data to + project + type: object + properties: + items: + description: Items is + a list of DownwardAPIVolume + file + type: array + items: + description: DownwardAPIVolumeFile + represents information + to create the file + containing the pod + field + type: object + required: + - path + properties: + fieldRef: + description: 'Required: + Selects a field + of the pod: only + annotations, labels, + name and namespace + are supported.' + type: object + required: + - fieldPath + properties: + apiVersion: + description: Version + of the schema + the FieldPath + is written + in terms of, + defaults to + "v1". + type: string + fieldPath: + description: Path + of the field + to select + in the specified + API version. + type: string + mode: + description: 'Optional: + mode bits to use + on this file, + must be a value + between 0 and + 0777. If not specified, + the volume defaultMode + will be used. + This might be + in conflict with + other options + that affect the + file mode, like + fsGroup, and the + result can be + other mode bits + set.' + type: integer + format: int32 + path: + description: 'Required: + Path is the relative + path name of the + file to be created. + Must not be absolute + or contain the + ''..'' path. Must + be utf-8 encoded. + The first item + of the relative + path must not + start with ''..''' + type: string + resourceFieldRef: + description: 'Selects + a resource of + the container: + only resources + limits and requests + (limits.cpu, limits.memory, + requests.cpu and + requests.memory) + are currently + supported.' + type: object + required: + - resource + properties: + containerName: + description: 'Container + name: required + for volumes, + optional for + env vars' + type: string + divisor: + description: Specifies + the output + format of + the exposed + resources, + defaults to + "1" + type: string + resource: + description: 'Required: + resource to + select' + type: string + secret: + description: information about + the secret data to project + type: object + properties: + items: + description: If unspecified, + each key-value pair + in the Data field of + the referenced Secret + will be projected into + the volume as a file + whose name is the key + and content is the value. + If specified, the listed + keys will be projected + into the specified paths, + and unlisted keys will + not be present. If a + key is specified which + is not present in the + Secret, the volume setup + will error unless it + is marked optional. + Paths must be relative + and may not contain + the '..' path or start + with '..'. + type: array + items: + description: Maps a + string key to a path + within a volume. + type: object + required: + - key + - path + properties: + key: + description: The + key to project. + type: string + mode: + description: 'Optional: + mode bits to use + on this file, + must be a value + between 0 and + 0777. If not specified, + the volume defaultMode + will be used. + This might be + in conflict with + other options + that affect the + file mode, like + fsGroup, and the + result can be + other mode bits + set.' + type: integer + format: int32 + path: + description: The + relative path + of the file to + map the key to. + May not be an + absolute path. + May not contain + the path element + '..'. May not + start with the + string '..'. + type: string + name: + description: 'Name of + the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful + fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether + the Secret or its key + must be defined + type: boolean + serviceAccountToken: + description: information about + the serviceAccountToken + data to project + type: object + required: + - path + properties: + audience: + description: Audience + is the intended audience + of the token. A recipient + of a token must identify + itself with an identifier + specified in the audience + of the token, and otherwise + should reject the token. + The audience defaults + to the identifier of + the apiserver. + type: string + expirationSeconds: + description: ExpirationSeconds + is the requested duration + of validity of the service + account token. As the + token approaches expiration, + the kubelet volume plugin + will proactively rotate + the service account + token. The kubelet will + start trying to rotate + the token if the token + is older than 80 percent + of its time to live + or if the token is older + than 24 hours.Defaults + to 1 hour and must be + at least 10 minutes. + type: integer + format: int64 + path: + description: Path is the + path relative to the + mount point of the file + to project the token + into. + type: string + quobyte: + description: Quobyte represents a Quobyte + mount on the host that shares a pod's + lifetime + type: object + required: + - registry + - volume + properties: + group: + description: Group to map volume + access to Default is no group + type: string + readOnly: + description: ReadOnly here will + force the Quobyte volume to be + mounted with read-only permissions. + Defaults to false. + type: boolean + registry: + description: Registry represents + a single or multiple Quobyte Registry + services specified as a string + as host:port pair (multiple entries + are separated with commas) which + acts as the central registry for + volumes + type: string + tenant: + description: Tenant owning the given + Quobyte volume in the Backend + Used with dynamically provisioned + Quobyte volumes, value is set + by the plugin + type: string + user: + description: User to map volume + access to Defaults to serivceaccount + user + type: string + volume: + description: Volume is a string + that references an already created + Quobyte volume by name. + type: string + rbd: + description: 'RBD represents a Rados + Block Device mount on the host that + shares a pod''s lifetime. More info: + https://examples.k8s.io/volumes/rbd/README.md' + type: object + required: + - image + - monitors + properties: + fsType: + description: 'Filesystem type of + the volume that you want to mount. + Tip: Ensure that the filesystem + type is supported by the host + operating system. Examples: "ext4", + "xfs", "ntfs". Implicitly inferred + to be "ext4" if unspecified. More + info: https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors + in the filesystem from compromising + the machine' + type: string + image: + description: 'The rados image name. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'Keyring is the path + to key ring for RBDUser. Default + is /etc/ceph/keyring. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'A collection of Ceph + monitors. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: array + items: + type: string + pool: + description: 'The rados pool name. + Default is rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'ReadOnly here will + force the ReadOnly setting in + VolumeMounts. Defaults to false. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'SecretRef is name + of the authentication secret for + RBDUser. If provided overrides + keyring. Default is nil. More + info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: object + properties: + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + user: + description: 'The rados user name. + Default is admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + scaleIO: + description: ScaleIO represents a ScaleIO + persistent volume attached and mounted + on Kubernetes nodes. + type: object + required: + - gateway + - secretRef + - system + properties: + fsType: + description: Filesystem type to + mount. Must be a filesystem type + supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". + Default is "xfs". + type: string + gateway: + description: The host address of + the ScaleIO API Gateway. + type: string + protectionDomain: + description: The name of the ScaleIO + Protection Domain for the configured + storage. + type: string + readOnly: + description: Defaults to false (read/write). + ReadOnly here will force the ReadOnly + setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef references + to the secret for ScaleIO user + and other sensitive information. + If this is not provided, Login + operation will fail. + type: object + properties: + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + sslEnabled: + description: Flag to enable/disable + SSL communication with Gateway, + default false + type: boolean + storageMode: + description: Indicates whether the + storage for a volume should be + ThickProvisioned or ThinProvisioned. + Default is ThinProvisioned. + type: string + storagePool: + description: The ScaleIO Storage + Pool associated with the protection + domain. + type: string + system: + description: The name of the storage + system as configured in ScaleIO. + type: string + volumeName: + description: The name of a volume + already created in the ScaleIO + system that is associated with + this volume source. + type: string + secret: + description: 'Secret represents a secret + that should populate this volume. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: object + properties: + defaultMode: + description: 'Optional: mode bits + to use on created files by default. + Must be a value between 0 and + 0777. Defaults to 0644. Directories + within the path are not affected + by this setting. This might be + in conflict with other options + that affect the file mode, like + fsGroup, and the result can be + other mode bits set.' + type: integer + format: int32 + items: + description: If unspecified, each + key-value pair in the Data field + of the referenced Secret will + be projected into the volume as + a file whose name is the key and + content is the value. If specified, + the listed keys will be projected + into the specified paths, and + unlisted keys will not be present. + If a key is specified which is + not present in the Secret, the + volume setup will error unless + it is marked optional. Paths must + be relative and may not contain + the '..' path or start with '..'. + type: array + items: + description: Maps a string key + to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode + bits to use on this file, + must be a value between + 0 and 0777. If not specified, + the volume defaultMode will + be used. This might be in + conflict with other options + that affect the file mode, + like fsGroup, and the result + can be other mode bits set.' + type: integer + format: int32 + path: + description: The relative + path of the file to map + the key to. May not be an + absolute path. May not contain + the path element '..'. May + not start with the string + '..'. + type: string + optional: + description: Specify whether the + Secret or its keys must be defined + type: boolean + secretName: + description: 'Name of the secret + in the pod''s namespace to use. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + storageos: + description: StorageOS represents a + StorageOS volume attached and mounted + on Kubernetes nodes. + type: object + properties: + fsType: + description: Filesystem type to + mount. Must be a filesystem type + supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" + if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). + ReadOnly here will force the ReadOnly + setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef specifies + the secret to use for obtaining + the StorageOS API credentials. If + not specified, default values + will be attempted. + type: object + properties: + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. + apiVersion, kind, uid?' + type: string + volumeName: + description: VolumeName is the human-readable + name of the StorageOS volume. Volume + names are only unique within a + namespace. + type: string + volumeNamespace: + description: VolumeNamespace specifies + the scope of the volume within + StorageOS. If no namespace is + specified then the Pod's namespace + will be used. This allows the + Kubernetes name scoping to be + mirrored within StorageOS for + tighter integration. Set VolumeName + to any name to override the default + behaviour. Set to "default" if + you are not using namespaces within + StorageOS. Namespaces that do + not pre-exist within StorageOS + will be created. + type: string + vsphereVolume: + description: VsphereVolume represents + a vSphere volume attached and mounted + on kubelets host machine + type: object + required: + - volumePath + properties: + fsType: + description: Filesystem type to + mount. Must be a filesystem type + supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" + if unspecified. + type: string + storagePolicyID: + description: Storage Policy Based + Management (SPBM) profile ID associated + with the StoragePolicyName. + type: string + storagePolicyName: + description: Storage Policy Based + Management (SPBM) profile name. + type: string + volumePath: + description: Path that identifies + vSphere volume vmdk + type: string + permissions: + type: array + items: + description: StrategyDeploymentPermissions describe the rbac + rules and service account needed by the install strategy + type: object + required: + - rules + - serviceAccountName + properties: + rules: + type: array + items: + description: PolicyRule holds information that describes + a policy rule, but does not contain information about + who the rule applies to or which namespace the rule + applies to. + type: object + required: + - verbs + properties: + apiGroups: + description: APIGroups is the name of the APIGroup + that contains the resources. If multiple API + groups are specified, any action requested against + one of the enumerated resources in any API group + will be allowed. + type: array + items: + type: string + nonResourceURLs: + description: NonResourceURLs is a set of partial + urls that a user should have access to. *s are + allowed, but only as the full, final step in the + path Since non-resource URLs are not namespaced, + this field is only applicable for ClusterRoles + referenced from a ClusterRoleBinding. Rules can + either apply to API resources (such as "pods" + or "secrets") or non-resource URL paths (such + as "/api"), but not both. + type: array + items: + type: string + resourceNames: + description: ResourceNames is an optional white + list of names that the rule applies to. An empty + set means that everything is allowed. + type: array + items: + type: string + resources: + description: Resources is a list of resources this + rule applies to. ResourceAll represents all resources. + type: array + items: + type: string + verbs: + description: Verbs is a list of Verbs that apply + to ALL the ResourceKinds and AttributeRestrictions + contained in this rule. VerbAll represents all + kinds. + type: array + items: + type: string + serviceAccountName: + type: string + strategy: + type: string + installModes: + description: InstallModes specify supported installation types + type: array + items: + description: InstallMode associates an InstallModeType with a flag + representing if the CSV supports it + type: object + required: + - supported + - type + properties: + supported: + type: boolean + type: + description: InstallModeType is a supported type of install mode + for CSV installation + type: string + keywords: + type: array + items: + type: string + labels: + description: Map of string keys and values that can be used to organize + and categorize (scope and select) objects. + type: object + additionalProperties: + type: string + links: + type: array + items: + type: object + properties: + name: + type: string + url: + type: string + maintainers: + type: array + items: + type: object + properties: + email: + type: string + name: + type: string + maturity: + type: string + minKubeVersion: + type: string + nativeAPIs: + type: array + items: + description: GroupVersionKind unambiguously identifies a kind. It + doesn't anonymously include GroupVersion to avoid automatic coersion. It + doesn't use a GroupVersion to avoid custom marshalling + type: object + required: + - group + - kind + - version + properties: + group: + type: string + kind: + type: string + version: + type: string + provider: + type: object + properties: + name: + type: string + url: + type: string + replaces: + description: The name of a CSV this one replaces. Should match the `metadata.Name` + field of the old CSV. + type: string + selector: + description: Label selector for related resources. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains + values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: operator represents a key's relationship to a + set of values. Valid operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string values. If the operator + is In or NotIn, the values array must be non-empty. If the + operator is Exists or DoesNotExist, the values array must + be empty. This array is replaced during a strategic merge + patch. + type: array + items: + type: string + matchLabels: + description: matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator is + "In", and the values array contains only "value". The requirements + are ANDed. + type: object + additionalProperties: + type: string + version: + description: OperatorVersion is a wrapper around semver.Version which + supports correct marshaling to YAML and JSON. + type: string status: + description: ClusterServiceVersionStatus represents information about the + status of a pod. Status may trail the actual state of a system. type: object - description: Status for a ClusterServiceVersion + properties: + certsLastUpdated: + description: Last time the owned APIService certs were updated + type: string + format: date-time + certsRotateAt: + description: Time the owned APIService certs will rotate next + type: string + format: date-time + conditions: + description: List of conditions, a history of state transitions + type: array + items: + description: Conditions appear in the status as a record of state + transitions on the ClusterServiceVersion + type: object + properties: + lastTransitionTime: + description: Last time the status transitioned from one status + to another. + type: string + format: date-time + lastUpdateTime: + description: Last time we updated the status + type: string + format: date-time + message: + description: A human readable message indicating details about + why the ClusterServiceVersion is in this condition. + type: string + phase: + description: Condition of the ClusterServiceVersion + type: string + reason: + description: A brief CamelCase message indicating details about + why the ClusterServiceVersion is in this state. e.g. 'RequirementsNotMet' + type: string + lastTransitionTime: + description: Last time the status transitioned from one status to another. + type: string + format: date-time + lastUpdateTime: + description: Last time we updated the status + type: string + format: date-time + message: + description: A human readable message indicating details about why the + ClusterServiceVersion is in this condition. + type: string + phase: + description: Current condition of the ClusterServiceVersion + type: string + reason: + description: A brief CamelCase message indicating details about why + the ClusterServiceVersion is in this state. e.g. 'RequirementsNotMet' + type: string + requirementStatus: + description: The status of each requirement for this CSV + type: array + items: + type: object + required: + - group + - kind + - message + - name + - status + - version + properties: + dependents: + type: array + items: + description: DependentStatus is the status for a dependent requirement + (to prevent infinite nesting) + type: object + required: + - group + - kind + - status + - version + properties: + group: + type: string + kind: + type: string + message: + type: string + status: + description: StatusReason is a camelcased reason for the + status of a RequirementStatus or DependentStatus + type: string + uuid: + type: string + version: + type: string + group: + type: string + kind: + type: string + message: + type: string + name: + type: string + status: + description: StatusReason is a camelcased reason for the status + of a RequirementStatus or DependentStatus + type: string + uuid: + type: string + version: + type: string diff --git a/manifests/0000_50_olm_04-installplan.crd.yaml b/manifests/0000_50_olm_04-installplan.crd.yaml index 28e49a9d90..765b181c0c 100644 --- a/manifests/0000_50_olm_04-installplan.crd.yaml +++ b/manifests/0000_50_olm_04-installplan.crd.yaml @@ -4,7 +4,8 @@ metadata: name: installplans.operators.coreos.com annotations: displayName: Install Plan - description: Represents a plan to install and resolve dependencies for Cluster Services + description: Represents a plan to install and resolve dependencies for Cluster + Services spec: group: operators.coreos.com version: v1alpha1 @@ -34,41 +35,171 @@ spec: - name: Approved type: boolean JSONPath: .spec.approved + preserveUnknownFields: false subresources: # status enables the status subresource. status: {} validation: openAPIV3Schema: - description: Represents a plan to install and resolve dependencies for Cluster Services. + description: InstallPlan defines the installation of a set of operators. + type: object + required: + - metadata + - spec properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object spec: + description: InstallPlanSpec defines a set of Application resources to be + installed type: object - description: Spec for an InstallPlan required: - - clusterServiceVersionNames - approval + - approved + - clusterServiceVersionNames properties: + approval: + description: Approval is the user approval policy for an InstallPlan. + type: string + approved: + type: boolean + clusterServiceVersionNames: + type: array + items: + type: string source: type: string - description: Name of the preferred CatalogSource sourceNamespace: type: string - description: Namespace that contains the preffered CatalogSource - clusterServiceVersionNames: + status: + description: "InstallPlanStatus represents the information about the status + of steps required to complete installation. \n Status may trail the actual + state of a system." + type: object + required: + - catalogSources + - phase + properties: + attenuatedServiceAccountRef: + description: AttenuatedServiceAccountRef references the service account + that is used to do scoped operator install. + type: object + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + catalogSources: type: array - description: A list of the names of the Cluster Services items: type: string - anyOf: - - properties: - approval: - enum: - - Manual - approved: - type: boolean - required: - - approved - - properties: - approval: - enum: - - Automatic + conditions: + type: array + items: + description: InstallPlanCondition represents the overall status of + the execution of an InstallPlan. + type: object + properties: + lastTransitionTime: + type: string + format: date-time + lastUpdateTime: + type: string + format: date-time + message: + type: string + reason: + description: ConditionReason is a camelcased reason for the state + transition. + type: string + status: + type: string + type: + description: InstallPlanConditionType describes the state of an + InstallPlan at a certain point as a whole. + type: string + phase: + description: InstallPlanPhase is the current status of a InstallPlan + as a whole. + type: string + plan: + type: array + items: + description: Step represents the status of an individual step in an + InstallPlan. + type: object + required: + - resolving + - resource + - status + properties: + resolving: + type: string + resource: + description: StepResource represents the status of a resource + to be tracked by an InstallPlan. + type: object + required: + - group + - kind + - name + - sourceName + - sourceNamespace + - version + properties: + group: + type: string + kind: + type: string + manifest: + type: string + name: + type: string + sourceName: + type: string + sourceNamespace: + type: string + version: + type: string + status: + description: StepStatus is the current status of a particular + resource an in InstallPlan + type: string diff --git a/manifests/0000_50_olm_05-subscription.crd.yaml b/manifests/0000_50_olm_05-subscription.crd.yaml index a44122870e..90d55ff1db 100644 --- a/manifests/0000_50_olm_05-subscription.crd.yaml +++ b/manifests/0000_50_olm_05-subscription.crd.yaml @@ -4,7 +4,8 @@ metadata: name: subscriptions.operators.coreos.com annotations: displayName: Subscription - description: Subscribes service catalog to a source and channel to recieve updates for packages. + description: Subscribes service catalog to a source and channel to recieve updates + for packages. spec: group: operators.coreos.com version: v1alpha1 @@ -36,167 +37,321 @@ spec: type: string description: The channel of updates to subscribe to JSONPath: .spec.channel + preserveUnknownFields: false subresources: # status enables the status subresource. status: {} validation: openAPIV3Schema: - description: Subscribes service catalog to a source and channel to recieve updates for packages. + description: Subscription keeps operators up to date by tracking changes to + Catalogs. + type: object + required: + - metadata + - spec properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object spec: + description: SubscriptionSpec defines an Application that can be installed type: object - description: Spec for a Subscription required: - - source - name + - source + - sourceNamespace properties: - source: - type: string - description: Name of a CatalogSource that defines where and how to find the channel - sourceNamespace: - type: string - description: The Kubernetes namespace where the CatalogSource used is located - name: - type: string - description: Name of the package that defines the application channel: type: string - description: Name of the channel to track - startingCSV: - type: string - description: Name of the AppType that this subscription tracks - installPlanApproval: - type: string - description: Approval mode for emitted InstallPlans - enum: - - Manual - - Automatic config: + description: SubscriptionConfig contains configuration specified for + a subscription. type: object - description: Operator Pod configuration, it is applied to the operator during install. It has higher precedence than the same configuration specified in a ClusterServiveVersion. properties: env: + description: Env is a list of environment variables to set in the + container. Cannot be updated. type: array - description: List of environment variables to set in the container items: + description: EnvVar represents an environment variable present + in a Container. type: object - description: EnvVar represents an environment variable present in a Container required: - - name + - name properties: name: + description: Name of the environment variable. Must be a C_IDENTIFIER. type: string - description: EnvVar represents an environment variable present in a Container value: + description: 'Variable references $(VAR_NAME) are expanded + using the previous defined environment variables in the + container and any service environment variables. If a variable + cannot be resolved, the reference in the input string will + be unchanged. The $(VAR_NAME) syntax can be escaped with + a double $$, ie: $$(VAR_NAME). Escaped references will never + be expanded, regardless of whether the variable exists or + not. Defaults to "".' type: string - description: Value of the environment variable specified valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. type: object - description: Source for the environment variable's value. Cannot be used if value is not empty properties: configMapKeyRef: + description: Selects a key of a ConfigMap. type: object - description: Selects a key of a ConfigMap required: - - key + - key properties: key: + description: The key to select. type: string - description: The key to select name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' type: string - description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names" optional: + description: Specify whether the ConfigMap or its + key must be defined type: boolean - description: Specify whether the ConfigMap or its key must be defined fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, metadata.labels, metadata.annotations, + spec.nodeName, spec.serviceAccountName, status.hostIP, + status.podIP.' type: object - description: "Selects a field of the pod: supports metadata.name, metadata.namespace, metadata.labels, metadata.annotations, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP" required: - - fieldPath + - fieldPath properties: apiVersion: + description: Version of the schema the FieldPath is + written in terms of, defaults to "v1". type: string - description: Version of the schema the FieldPath is written in terms of, defaults to v1 fieldPath: + description: Path of the field to select in the specified + API version. type: string - description: Path of the field to select in the specified API version resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.ephemeral-storage) are currently supported.' type: object - description: "Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported." required: - - resource + - resource properties: containerName: + description: 'Container name: required for volumes, + optional for env vars' type: string - description: "Container name: required for volumes, optional for env vars" divisor: + description: Specifies the output format of the exposed + resources, defaults to "1" type: string - description: Specifies the output format of the exposed resources, defaults to 1 resource: + description: 'Required: resource to select' type: string - description: "Required: resource to select" secretKeyRef: - type: object description: Selects a key of a secret in the pod's namespace + type: object required: - - key + - key properties: key: + description: The key of the secret to select from. Must + be a valid secret key. type: string - description: The key of the secret to select from. Must be a valid secret key name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' type: string - description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names" optional: + description: Specify whether the Secret or its key + must be defined type: boolean - description: Specify whether the Secret or its key must be defined envFrom: + description: EnvFrom is a list of sources to populate environment + variables in the container. The keys defined within a source must + be a C_IDENTIFIER. All invalid keys will be reported as an event + when the container is starting. When a key exists in multiple + sources, the value associated with the last source will take precedence. + Values defined by an Env with a duplicate key will take precedence. + Immutable. type: array - description: "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated" items: - type: object description: EnvFromSource represents the source of a set of ConfigMaps + type: object properties: configMapRef: + description: The ConfigMap to select from type: object - description: "ConfigMapEnvSource selects a ConfigMap to populate the environment variables with.\n\nThe contents of the target ConfigMap's Data field will represent the key-value pairs as environment variables" properties: name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' type: string - description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names" optional: - type: boolean description: Specify whether the ConfigMap must be defined + type: boolean prefix: + description: An optional identifier to prepend to each key + in the ConfigMap. Must be a C_IDENTIFIER. type: string - description: An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER secretRef: + description: The Secret to select from type: object - description: "SecretEnvSource selects a Secret to populate the environment variables with.\n\nThe contents of the target Secret's Data field will represent the key-value pairs as environment variables" properties: name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' type: string - description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names" optional: - type: boolean description: Specify whether the Secret must be defined - + type: boolean + nodeSelector: + description: 'NodeSelector is a selector which must be true for + the pod to fit on a node. Selector which must match a node''s + labels for the pod to be scheduled on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/' + type: object + additionalProperties: + type: string + resources: + description: 'Resources represents compute resources required by + this container. Immutable. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + properties: + limits: + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + additionalProperties: + type: string + requests: + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + additionalProperties: + type: string + selector: + description: Selector is the label selector for pods to be configured. + Existing ReplicaSets whose pods are selected by this will be the + ones affected by this deployment. It must match the pod template's + labels. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that + contains values, a key, and an operator that relates the + key and values. + type: object + required: + - key + - operator + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of string values. If the + operator is In or NotIn, the values array must be non-empty. + If the operator is Exists or DoesNotExist, the values + array must be empty. This array is replaced during a + strategic merge patch. + type: array + items: + type: string + matchLabels: + description: matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator + is "In", and the values array contains only "value". The requirements + are ANDed. + type: object + additionalProperties: + type: string + tolerations: + description: Tolerations are the pod's tolerations. + type: array + items: + description: The pod this Toleration is attached to tolerates + any taint that matches the triple using the + matching operator . + type: object + properties: + effect: + description: Effect indicates the taint effect to match. Empty + means match all taint effects. When specified, allowed values + are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, + operator must be Exists; this combination means to match + all values and all keys. + type: string + operator: + description: Operator represents a key's relationship to the + value. Valid operators are Exists and Equal. Defaults to + Equal. Exists is equivalent to wildcard for value, so that + a pod can tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of time + the toleration (which must be of effect NoExecute, otherwise + this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do + not evict). Zero and negative values will be treated as + 0 (evict immediately) by the system. + type: integer + format: int64 + value: + description: Value is the taint value the toleration matches + to. If the operator is Exists, the value should be empty, + otherwise just a regular string. + type: string volumeMounts: description: List of VolumeMounts to set in the container. + type: array items: description: VolumeMount describes a mounting of a Volume within a container. + type: object + required: + - mountPath + - name properties: mountPath: description: Path within the container at which the volume should be mounted. Must not contain ':'. type: string mountPropagation: - description: mountPropagation determines how mounts are - propagated from the host to container and the other way - around. When not set, MountPropagationNone is used. This - field is beta in 1.10. + description: mountPropagation determines how mounts are propagated + from the host to container and the other way around. When + not set, MountPropagationNone is used. This field is beta + in 1.10. type: string name: description: This must match the Name of a Volume. @@ -210,66 +365,68 @@ spec: volume should be mounted. Defaults to "" (volume's root). type: string subPathExpr: - description: Expanded path within the volume from which - the container's volume should be mounted. Behaves similarly + description: Expanded path within the volume from which the + container's volume should be mounted. Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. Defaults to "" (volume's root). SubPathExpr and SubPath are mutually exclusive. This field is beta in 1.15. type: string - required: - - mountPath - - name - type: object - type: array volumes: description: List of Volumes to set in the podSpec. + type: array items: - description: Volume represents a named volume in a pod that - may be accessed by any container in the pod. + description: Volume represents a named volume in a pod that may + be accessed by any container in the pod. + type: object + required: + - name properties: awsElasticBlockStore: description: 'AWSElasticBlockStore represents an AWS Disk - resource that is attached to a kubelet''s host machine - and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + resource that is attached to a kubelet''s host machine and + then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: object + required: + - volumeID properties: fsType: - description: 'Filesystem type of the volume that you - want to mount. Tip: Ensure that the filesystem type - is supported by the host operating system. Examples: - "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore - TODO: how do we prevent errors in the filesystem from - compromising the machine' + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", + "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors in the filesystem from + compromising the machine' type: string partition: description: 'The partition in the volume that you want - to mount. If omitted, the default is to mount by volume - name. Examples: For volume /dev/sda1, you specify - the partition as "1". Similarly, the volume partition - for /dev/sda is "0" (or you can leave the property - empty).' - format: int32 + to mount. If omitted, the default is to mount by volume + name. Examples: For volume /dev/sda1, you specify the + partition as "1". Similarly, the volume partition for + /dev/sda is "0" (or you can leave the property empty).' type: integer + format: int32 readOnly: description: 'Specify "true" to force and set the ReadOnly - property in VolumeMounts to "true". If omitted, the - default is "false". More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + property in VolumeMounts to "true". If omitted, the + default is "false". More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' type: boolean volumeID: description: 'Unique ID of the persistent disk resource - in AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + in AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' type: string - required: - - volumeID - type: object azureDisk: description: AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod. + type: object + required: + - diskName + - diskURI properties: cachingMode: description: 'Host Caching mode: None, Read Only, Read - Write.' + Write.' type: string diskName: description: The Name of the data disk in the blob storage @@ -280,181 +437,176 @@ spec: fsType: description: Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", - "xfs", "ntfs". Implicitly inferred to be "ext4" if - unspecified. + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. type: string kind: - description: 'Expected values Shared: multiple blob - disks per storage account Dedicated: single blob - disk per storage account Managed: azure managed data - disk (only in managed availability set). defaults - to shared' + description: 'Expected values Shared: multiple blob disks + per storage account Dedicated: single blob disk per + storage account Managed: azure managed data disk (only + in managed availability set). defaults to shared' type: string readOnly: description: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. type: boolean - required: - - diskName - - diskURI - type: object azureFile: - description: AzureFile represents an Azure File Service - mount on the host and bind mount to the pod. + description: AzureFile represents an Azure File Service mount + on the host and bind mount to the pod. + type: object + required: + - secretName + - shareName properties: readOnly: description: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. type: boolean secretName: - description: the name of secret that contains Azure - Storage Account Name and Key + description: the name of secret that contains Azure Storage + Account Name and Key type: string shareName: description: Share Name type: string - required: - - secretName - - shareName - type: object cephfs: description: CephFS represents a Ceph FS mount on the host that shares a pod's lifetime + type: object + required: + - monitors properties: monitors: - description: 'Required: Monitors is a collection of - Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + description: 'Required: Monitors is a collection of Ceph + monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: array items: type: string - type: array path: description: 'Optional: Used as the mounted root, rather - than the full Ceph tree, default is /' + than the full Ceph tree, default is /' type: string readOnly: description: 'Optional: Defaults to false (read/write). - ReadOnly here will force the ReadOnly setting in VolumeMounts. - More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + ReadOnly here will force the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' type: boolean secretFile: description: 'Optional: SecretFile is the path to key - ring for User, default is /etc/ceph/user.secret More - info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + ring for User, default is /etc/ceph/user.secret More + info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' type: string secretRef: description: 'Optional: SecretRef is reference to the - authentication secret for User, default is empty. - More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + authentication secret for User, default is empty. More + info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: object properties: name: description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + TODO: Add other useful fields. apiVersion, kind, + uid?' type: string - type: object user: - description: 'Optional: User is the rados user name, - default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + description: 'Optional: User is the rados user name, default + is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' type: string - required: - - monitors - type: object cinder: - description: 'Cinder represents a cinder volume attached - and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + description: 'Cinder represents a cinder volume attached and + mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: object + required: + - volumeID properties: fsType: description: 'Filesystem type to mount. Must be a filesystem - type supported by the host operating system. Examples: - "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' type: string readOnly: description: 'Optional: Defaults to false (read/write). - ReadOnly here will force the ReadOnly setting in VolumeMounts. - More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + ReadOnly here will force the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' type: boolean secretRef: description: 'Optional: points to a secret object containing - parameters used to connect to OpenStack.' + parameters used to connect to OpenStack.' + type: object properties: name: description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + TODO: Add other useful fields. apiVersion, kind, + uid?' type: string - type: object volumeID: - description: 'volume id used to identify the volume - in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + description: 'volume id used to identify the volume in + cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' type: string - required: - - volumeID - type: object configMap: description: ConfigMap represents a configMap that should populate this volume + type: object properties: defaultMode: - description: 'Optional: mode bits to use on created - files by default. Must be a value between 0 and 0777. - Defaults to 0644. Directories within the path are - not affected by this setting. This might be in conflict - with other options that affect the file mode, like - fsGroup, and the result can be other mode bits set.' - format: int32 + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. Directories within the path are not affected + by this setting. This might be in conflict with other + options that affect the file mode, like fsGroup, and + the result can be other mode bits set.' type: integer + format: int32 items: - description: If unspecified, each key-value pair in - the Data field of the referenced ConfigMap will be - projected into the volume as a file whose name is - the key and content is the value. If specified, the - listed keys will be projected into the specified paths, - and unlisted keys will not be present. If a key is - specified which is not present in the ConfigMap, the - volume setup will error unless it is marked optional. - Paths must be relative and may not contain the '..' - path or start with '..'. + description: If unspecified, each key-value pair in the + Data field of the referenced ConfigMap will be projected + into the volume as a file whose name is the key and + content is the value. If specified, the listed keys + will be projected into the specified paths, and unlisted + keys will not be present. If a key is specified which + is not present in the ConfigMap, the volume setup will + error unless it is marked optional. Paths must be relative + and may not contain the '..' path or start with '..'. + type: array items: - description: Maps a string key to a path within a - volume. + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path properties: key: description: The key to project. type: string mode: description: 'Optional: mode bits to use on this - file, must be a value between 0 and 0777. If - not specified, the volume defaultMode will be - used. This might be in conflict with other options - that affect the file mode, like fsGroup, and - the result can be other mode bits set.' - format: int32 + file, must be a value between 0 and 0777. If not + specified, the volume defaultMode will be used. + This might be in conflict with other options that + affect the file mode, like fsGroup, and the result + can be other mode bits set.' type: integer + format: int32 path: - description: The relative path of the file to - map the key to. May not be an absolute path. - May not contain the path element '..'. May not - start with the string '..'. + description: The relative path of the file to map + the key to. May not be an absolute path. May not + contain the path element '..'. May not start with + the string '..'. type: string - required: - - key - - path - type: object - type: array name: description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' + TODO: Add other useful fields. apiVersion, kind, uid?' type: string optional: description: Specify whether the ConfigMap or its keys must be defined type: boolean - type: object csi: description: CSI (Container Storage Interface) represents storage that is handled by an external CSI driver (Alpha feature). + type: object + required: + - driver properties: driver: description: Driver is the name of the CSI driver that @@ -463,63 +615,67 @@ spec: type: string fsType: description: Filesystem type to mount. Ex. "ext4", "xfs", - "ntfs". If not provided, the empty value is passed - to the associated CSI driver which will determine - the default filesystem to apply. + "ntfs". If not provided, the empty value is passed to + the associated CSI driver which will determine the default + filesystem to apply. type: string nodePublishSecretRef: - description: NodePublishSecretRef is a reference to - the secret object containing sensitive information - to pass to the CSI driver to complete the CSI NodePublishVolume + description: NodePublishSecretRef is a reference to the + secret object containing sensitive information to pass + to the CSI driver to complete the CSI NodePublishVolume and NodeUnpublishVolume calls. This field is optional, - and may be empty if no secret is required. If the - secret object contains more than one secret, all secret - references are passed. + and may be empty if no secret is required. If the secret + object contains more than one secret, all secret references + are passed. + type: object properties: name: description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + TODO: Add other useful fields. apiVersion, kind, + uid?' type: string - type: object readOnly: - description: Specifies a read-only configuration for - the volume. Defaults to false (read/write). + description: Specifies a read-only configuration for the + volume. Defaults to false (read/write). type: boolean volumeAttributes: + description: VolumeAttributes stores driver-specific properties + that are passed to the CSI driver. Consult your driver's + documentation for supported values. + type: object additionalProperties: type: string - description: VolumeAttributes stores driver-specific - properties that are passed to the CSI driver. Consult - your driver's documentation for supported values. - type: object - required: - - driver - type: object downwardAPI: description: DownwardAPI represents downward API about the pod that should populate this volume + type: object properties: defaultMode: - description: 'Optional: mode bits to use on created - files by default. Must be a value between 0 and 0777. - Defaults to 0644. Directories within the path are - not affected by this setting. This might be in conflict - with other options that affect the file mode, like - fsGroup, and the result can be other mode bits set.' - format: int32 + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. Directories within the path are not affected + by this setting. This might be in conflict with other + options that affect the file mode, like fsGroup, and + the result can be other mode bits set.' type: integer + format: int32 items: - description: Items is a list of downward API volume - file + description: Items is a list of downward API volume file + type: array items: description: DownwardAPIVolumeFile represents information to create the file containing the pod field + type: object + required: + - path properties: fieldRef: - description: 'Required: Selects a field of the - pod: only annotations, labels, name and namespace - are supported.' + description: 'Required: Selects a field of the pod: + only annotations, labels, name and namespace are + supported.' + type: object + required: + - fieldPath properties: apiVersion: description: Version of the schema the FieldPath @@ -529,34 +685,34 @@ spec: description: Path of the field to select in the specified API version. type: string - required: - - fieldPath - type: object mode: description: 'Optional: mode bits to use on this - file, must be a value between 0 and 0777. If - not specified, the volume defaultMode will be - used. This might be in conflict with other options - that affect the file mode, like fsGroup, and - the result can be other mode bits set.' - format: int32 + file, must be a value between 0 and 0777. If not + specified, the volume defaultMode will be used. + This might be in conflict with other options that + affect the file mode, like fsGroup, and the result + can be other mode bits set.' type: integer + format: int32 path: - description: 'Required: Path is the relative - path name of the file to be created. Must not - be absolute or contain the ''..'' path. Must - be utf-8 encoded. The first item of the relative - path must not start with ''..''' + description: 'Required: Path is the relative path + name of the file to be created. Must not be absolute + or contain the ''..'' path. Must be utf-8 encoded. + The first item of the relative path must not start + with ''..''' type: string resourceFieldRef: description: 'Selects a resource of the container: - only resources limits and requests (limits.cpu, - limits.memory, requests.cpu and requests.memory) - are currently supported.' + only resources limits and requests (limits.cpu, + limits.memory, requests.cpu and requests.memory) + are currently supported.' + type: object + required: + - resource properties: containerName: - description: 'Container name: required for - volumes, optional for env vars' + description: 'Container name: required for volumes, + optional for env vars' type: string divisor: description: Specifies the output format of @@ -565,74 +721,69 @@ spec: resource: description: 'Required: resource to select' type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object emptyDir: - description: 'EmptyDir represents a temporary directory - that shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + description: 'EmptyDir represents a temporary directory that + shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: object properties: medium: description: 'What type of storage medium should back - this directory. The default is "" which means to use - the node''s default medium. Must be an empty string - (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + this directory. The default is "" which means to use + the node''s default medium. Must be an empty string + (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' type: string sizeLimit: - description: 'Total amount of local storage required - for this EmptyDir volume. The size limit is also applicable - for memory medium. The maximum usage on memory medium - EmptyDir would be the minimum value between the SizeLimit - specified here and the sum of memory limits of all - containers in a pod. The default is nil which means - that the limit is undefined. More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' + description: 'Total amount of local storage required for + this EmptyDir volume. The size limit is also applicable + for memory medium. The maximum usage on memory medium + EmptyDir would be the minimum value between the SizeLimit + specified here and the sum of memory limits of all containers + in a pod. The default is nil which means that the limit + is undefined. More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' type: string - type: object fc: - description: FC represents a Fibre Channel resource that - is attached to a kubelet's host machine and then exposed - to the pod. + description: FC represents a Fibre Channel resource that is + attached to a kubelet's host machine and then exposed to + the pod. + type: object properties: fsType: description: 'Filesystem type to mount. Must be a filesystem - type supported by the host operating system. Ex. "ext4", - "xfs", "ntfs". Implicitly inferred to be "ext4" if - unspecified. TODO: how do we prevent errors in the - filesystem from compromising the machine' + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + TODO: how do we prevent errors in the filesystem from + compromising the machine' type: string lun: description: 'Optional: FC target lun number' - format: int32 type: integer + format: int32 readOnly: description: 'Optional: Defaults to false (read/write). - ReadOnly here will force the ReadOnly setting in VolumeMounts.' + ReadOnly here will force the ReadOnly setting in VolumeMounts.' type: boolean targetWWNs: description: 'Optional: FC target worldwide names (WWNs)' + type: array items: type: string - type: array wwids: description: 'Optional: FC volume world wide identifiers - (wwids) Either wwids or combination of targetWWNs - and lun must be set, but not both simultaneously.' + (wwids) Either wwids or combination of targetWWNs and + lun must be set, but not both simultaneously.' + type: array items: type: string - type: array - type: object flexVolume: description: FlexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin. + type: object + required: + - driver properties: driver: - description: Driver is the name of the driver to use - for this volume. + description: Driver is the name of the driver to use for + this volume. type: string fsType: description: Filesystem type to mount. Must be a filesystem @@ -641,94 +792,93 @@ spec: script. type: string options: - additionalProperties: - type: string description: 'Optional: Extra command options if any.' type: object + additionalProperties: + type: string readOnly: description: 'Optional: Defaults to false (read/write). - ReadOnly here will force the ReadOnly setting in VolumeMounts.' + ReadOnly here will force the ReadOnly setting in VolumeMounts.' type: boolean secretRef: description: 'Optional: SecretRef is reference to the - secret object containing sensitive information to - pass to the plugin scripts. This may be empty if no - secret object is specified. If the secret object contains - more than one secret, all secrets are passed to the - plugin scripts.' + secret object containing sensitive information to pass + to the plugin scripts. This may be empty if no secret + object is specified. If the secret object contains more + than one secret, all secrets are passed to the plugin + scripts.' + type: object properties: name: description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + TODO: Add other useful fields. apiVersion, kind, + uid?' type: string - type: object - required: - - driver - type: object flocker: description: Flocker represents a Flocker volume attached to a kubelet's host machine. This depends on the Flocker control service being running + type: object properties: datasetName: - description: Name of the dataset stored as metadata - -> name on the dataset for Flocker should be considered + description: Name of the dataset stored as metadata -> + name on the dataset for Flocker should be considered as deprecated type: string datasetUUID: description: UUID of the dataset. This is unique identifier of a Flocker dataset type: string - type: object gcePersistentDisk: description: 'GCEPersistentDisk represents a GCE Disk resource - that is attached to a kubelet''s host machine and then - exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: object + required: + - pdName properties: fsType: - description: 'Filesystem type of the volume that you - want to mount. Tip: Ensure that the filesystem type - is supported by the host operating system. Examples: - "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk - TODO: how do we prevent errors in the filesystem from - compromising the machine' + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", + "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors in the filesystem from + compromising the machine' type: string partition: description: 'The partition in the volume that you want - to mount. If omitted, the default is to mount by volume - name. Examples: For volume /dev/sda1, you specify - the partition as "1". Similarly, the volume partition - for /dev/sda is "0" (or you can leave the property - empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' - format: int32 + to mount. If omitted, the default is to mount by volume + name. Examples: For volume /dev/sda1, you specify the + partition as "1". Similarly, the volume partition for + /dev/sda is "0" (or you can leave the property empty). + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' type: integer + format: int32 pdName: - description: 'Unique name of the PD resource in GCE. - Used to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + description: 'Unique name of the PD resource in GCE. Used + to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' type: string readOnly: - description: 'ReadOnly here will force the ReadOnly - setting in VolumeMounts. Defaults to false. More info: - https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + description: 'ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' type: boolean - required: - - pdName - type: object gitRepo: description: 'GitRepo represents a git repository at a particular - revision. DEPRECATED: GitRepo is deprecated. To provision - a container with a git repo, mount an EmptyDir into an - InitContainer that clones the repo using git, then mount - the EmptyDir into the Pod''s container.' + revision. DEPRECATED: GitRepo is deprecated. To provision + a container with a git repo, mount an EmptyDir into an InitContainer + that clones the repo using git, then mount the EmptyDir + into the Pod''s container.' + type: object + required: + - repository properties: directory: - description: Target directory name. Must not contain - or start with '..'. If '.' is supplied, the volume - directory will be the git repository. Otherwise, - if specified, the volume will contain the git repository - in the subdirectory with the given name. + description: Target directory name. Must not contain or + start with '..'. If '.' is supplied, the volume directory + will be the git repository. Otherwise, if specified, + the volume will contain the git repository in the subdirectory + with the given name. type: string repository: description: Repository URL @@ -736,57 +886,57 @@ spec: revision: description: Commit hash for the specified revision. type: string - required: - - repository - type: object glusterfs: - description: 'Glusterfs represents a Glusterfs mount on - the host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' + description: 'Glusterfs represents a Glusterfs mount on the + host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' + type: object + required: + - endpoints + - path properties: endpoints: description: 'EndpointsName is the endpoint name that - details Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + details Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' type: string path: description: 'Path is the Glusterfs volume path. More - info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' type: string readOnly: - description: 'ReadOnly here will force the Glusterfs - volume to be mounted with read-only permissions. Defaults - to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + description: 'ReadOnly here will force the Glusterfs volume + to be mounted with read-only permissions. Defaults to + false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' type: boolean - required: - - endpoints - - path - type: object hostPath: - description: 'HostPath represents a pre-existing file or - directory on the host machine that is directly exposed - to the container. This is generally used for system agents - or other privileged things that are allowed to see the - host machine. Most containers will NOT need this. More - info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath - --- TODO(jonesdl) We need to restrict who can use host - directory mounts and who can/can not mount host directories - as read/write.' + description: 'HostPath represents a pre-existing file or directory + on the host machine that is directly exposed to the container. + This is generally used for system agents or other privileged + things that are allowed to see the host machine. Most containers + will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + --- TODO(jonesdl) We need to restrict who can use host directory + mounts and who can/can not mount host directories as read/write.' + type: object + required: + - path properties: path: - description: 'Path of the directory on the host. If - the path is a symlink, it will follow the link to - the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + description: 'Path of the directory on the host. If the + path is a symlink, it will follow the link to the real + path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' type: string type: description: 'Type for HostPath Volume Defaults to "" - More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' type: string - required: - - path - type: object iscsi: description: 'ISCSI represents an ISCSI Disk resource that - is attached to a kubelet''s host machine and then exposed - to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + type: object + required: + - iqn + - lun + - targetPortal properties: chapAuthDiscovery: description: whether support iSCSI Discovery CHAP authentication @@ -795,38 +945,38 @@ spec: description: whether support iSCSI Session CHAP authentication type: boolean fsType: - description: 'Filesystem type of the volume that you - want to mount. Tip: Ensure that the filesystem type - is supported by the host operating system. Examples: - "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi - TODO: how do we prevent errors in the filesystem from - compromising the machine' + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", + "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors in the filesystem from + compromising the machine' type: string initiatorName: description: Custom iSCSI Initiator Name. If initiatorName is specified with iscsiInterface simultaneously, new - iSCSI interface : will - be created for the connection. + iSCSI interface : will be + created for the connection. type: string iqn: description: Target iSCSI Qualified Name. type: string iscsiInterface: - description: iSCSI Interface Name that uses an iSCSI - transport. Defaults to 'default' (tcp). + description: iSCSI Interface Name that uses an iSCSI transport. + Defaults to 'default' (tcp). type: string lun: description: iSCSI Target Lun number. - format: int32 type: integer + format: int32 portals: - description: iSCSI Target Portal List. The portal is - either an IP or ip_addr:port if the port is other - than default (typically TCP ports 860 and 3260). + description: iSCSI Target Portal List. The portal is either + an IP or ip_addr:port if the port is other than default + (typically TCP ports 860 and 3260). + type: array items: type: string - type: array readOnly: description: ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. @@ -834,86 +984,82 @@ spec: secretRef: description: CHAP Secret for iSCSI target and initiator authentication + type: object properties: name: description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + TODO: Add other useful fields. apiVersion, kind, + uid?' type: string - type: object targetPortal: description: iSCSI Target Portal. The Portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260). type: string - required: - - iqn - - lun - - targetPortal - type: object name: description: 'Volume''s name. Must be a DNS_LABEL and unique - within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' type: string nfs: description: 'NFS represents an NFS mount on the host that - shares a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + shares a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: object + required: + - path + - server properties: path: description: 'Path that is exported by the NFS server. - More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' type: string readOnly: description: 'ReadOnly here will force the NFS export - to be mounted with read-only permissions. Defaults - to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + to be mounted with read-only permissions. Defaults to + false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' type: boolean server: description: 'Server is the hostname or IP address of - the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' type: string - required: - - path - - server - type: object persistentVolumeClaim: description: 'PersistentVolumeClaimVolumeSource represents - a reference to a PersistentVolumeClaim in the same namespace. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + a reference to a PersistentVolumeClaim in the same namespace. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: object + required: + - claimName properties: claimName: description: 'ClaimName is the name of a PersistentVolumeClaim - in the same namespace as the pod using this volume. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + in the same namespace as the pod using this volume. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' type: string readOnly: description: Will force the ReadOnly setting in VolumeMounts. Default false. type: boolean - required: - - claimName - type: object photonPersistentDisk: description: PhotonPersistentDisk represents a PhotonController - persistent disk attached and mounted on kubelets host - machine + persistent disk attached and mounted on kubelets host machine + type: object + required: + - pdID properties: fsType: description: Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", - "xfs", "ntfs". Implicitly inferred to be "ext4" if - unspecified. + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. type: string pdID: description: ID that identifies Photon Controller persistent disk type: string - required: - - pdID - type: object portworxVolume: - description: PortworxVolume represents a portworx volume - attached and mounted on kubelets host machine + description: PortworxVolume represents a portworx volume attached + and mounted on kubelets host machine + type: object + required: + - volumeID properties: fsType: description: FSType represents the filesystem type to @@ -926,105 +1072,113 @@ spec: here will force the ReadOnly setting in VolumeMounts. type: boolean volumeID: - description: VolumeID uniquely identifies a Portworx - volume + description: VolumeID uniquely identifies a Portworx volume type: string - required: - - volumeID - type: object projected: description: Items for all in one resources secrets, configmaps, and downward API + type: object + required: + - sources properties: defaultMode: description: Mode bits to use on created files by default. Must be a value between 0 and 0777. Directories within the path are not affected by this setting. This might - be in conflict with other options that affect the - file mode, like fsGroup, and the result can be other - mode bits set. - format: int32 + be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode + bits set. type: integer + format: int32 sources: description: list of volume projections + type: array items: description: Projection that may be projected along with other supported volume types + type: object properties: configMap: description: information about the configMap data to project + type: object properties: items: description: If unspecified, each key-value - pair in the Data field of the referenced - ConfigMap will be projected into the volume - as a file whose name is the key and content - is the value. If specified, the listed keys - will be projected into the specified paths, - and unlisted keys will not be present. If - a key is specified which is not present - in the ConfigMap, the volume setup will - error unless it is marked optional. Paths - must be relative and may not contain the - '..' path or start with '..'. + pair in the Data field of the referenced ConfigMap + will be projected into the volume as a file + whose name is the key and content is the value. + If specified, the listed keys will be projected + into the specified paths, and unlisted keys + will not be present. If a key is specified + which is not present in the ConfigMap, the + volume setup will error unless it is marked + optional. Paths must be relative and may not + contain the '..' path or start with '..'. + type: array items: - description: Maps a string key to a path - within a volume. + description: Maps a string key to a path within + a volume. + type: object + required: + - key + - path properties: key: description: The key to project. type: string mode: - description: 'Optional: mode bits to - use on this file, must be a value - between 0 and 0777. If not specified, - the volume defaultMode will be used. - This might be in conflict with other - options that affect the file mode, - like fsGroup, and the result can be - other mode bits set.' - format: int32 + description: 'Optional: mode bits to use + on this file, must be a value between + 0 and 0777. If not specified, the volume + defaultMode will be used. This might + be in conflict with other options that + affect the file mode, like fsGroup, + and the result can be other mode bits + set.' type: integer + format: int32 path: description: The relative path of the - file to map the key to. May not be - an absolute path. May not contain - the path element '..'. May not start - with the string '..'. + file to map the key to. May not be an + absolute path. May not contain the path + element '..'. May not start with the + string '..'. type: string - required: - - key - - path - type: object - type: array name: description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' type: string optional: - description: Specify whether the ConfigMap - or its keys must be defined + description: Specify whether the ConfigMap or + its keys must be defined type: boolean - type: object downwardAPI: - description: information about the downwardAPI - data to project + description: information about the downwardAPI data + to project + type: object properties: items: description: Items is a list of DownwardAPIVolume file + type: array items: description: DownwardAPIVolumeFile represents information to create the file containing the pod field + type: object + required: + - path properties: fieldRef: description: 'Required: Selects a field - of the pod: only annotations, labels, - name and namespace are supported.' + of the pod: only annotations, labels, + name and namespace are supported.' + type: object + required: + - fieldPath properties: apiVersion: description: Version of the schema @@ -1035,39 +1189,38 @@ spec: description: Path of the field to select in the specified API version. type: string - required: - - fieldPath - type: object mode: - description: 'Optional: mode bits to - use on this file, must be a value - between 0 and 0777. If not specified, - the volume defaultMode will be used. - This might be in conflict with other - options that affect the file mode, - like fsGroup, and the result can be - other mode bits set.' - format: int32 + description: 'Optional: mode bits to use + on this file, must be a value between + 0 and 0777. If not specified, the volume + defaultMode will be used. This might + be in conflict with other options that + affect the file mode, like fsGroup, + and the result can be other mode bits + set.' type: integer + format: int32 path: - description: 'Required: Path is the - relative path name of the file to - be created. Must not be absolute or - contain the ''..'' path. Must be utf-8 - encoded. The first item of the relative - path must not start with ''..''' + description: 'Required: Path is the relative + path name of the file to be created. + Must not be absolute or contain the + ''..'' path. Must be utf-8 encoded. + The first item of the relative path + must not start with ''..''' type: string resourceFieldRef: - description: 'Selects a resource of - the container: only resources limits - and requests (limits.cpu, limits.memory, - requests.cpu and requests.memory) - are currently supported.' + description: 'Selects a resource of the + container: only resources limits and + requests (limits.cpu, limits.memory, + requests.cpu and requests.memory) are + currently supported.' + type: object + required: + - resource properties: containerName: description: 'Container name: required - for volumes, optional for env - vars' + for volumes, optional for env vars' type: string divisor: description: Specifies the output @@ -1075,78 +1228,72 @@ spec: defaults to "1" type: string resource: - description: 'Required: resource - to select' + description: 'Required: resource to + select' type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object secret: - description: information about the secret data - to project + description: information about the secret data to + project + type: object properties: items: description: If unspecified, each key-value - pair in the Data field of the referenced - Secret will be projected into the volume - as a file whose name is the key and content - is the value. If specified, the listed keys - will be projected into the specified paths, - and unlisted keys will not be present. If - a key is specified which is not present - in the Secret, the volume setup will error - unless it is marked optional. Paths must - be relative and may not contain the '..' - path or start with '..'. + pair in the Data field of the referenced Secret + will be projected into the volume as a file + whose name is the key and content is the value. + If specified, the listed keys will be projected + into the specified paths, and unlisted keys + will not be present. If a key is specified + which is not present in the Secret, the volume + setup will error unless it is marked optional. + Paths must be relative and may not contain + the '..' path or start with '..'. + type: array items: - description: Maps a string key to a path - within a volume. + description: Maps a string key to a path within + a volume. + type: object + required: + - key + - path properties: key: description: The key to project. type: string mode: - description: 'Optional: mode bits to - use on this file, must be a value - between 0 and 0777. If not specified, - the volume defaultMode will be used. - This might be in conflict with other - options that affect the file mode, - like fsGroup, and the result can be - other mode bits set.' - format: int32 + description: 'Optional: mode bits to use + on this file, must be a value between + 0 and 0777. If not specified, the volume + defaultMode will be used. This might + be in conflict with other options that + affect the file mode, like fsGroup, + and the result can be other mode bits + set.' type: integer + format: int32 path: description: The relative path of the - file to map the key to. May not be - an absolute path. May not contain - the path element '..'. May not start - with the string '..'. + file to map the key to. May not be an + absolute path. May not contain the path + element '..'. May not start with the + string '..'. type: string - required: - - key - - path - type: object - type: array name: description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' type: string optional: - description: Specify whether the Secret or - its key must be defined + description: Specify whether the Secret or its + key must be defined type: boolean - type: object serviceAccountToken: description: information about the serviceAccountToken data to project + type: object + required: + - path properties: audience: description: Audience is the intended audience @@ -1162,29 +1309,24 @@ spec: token. As the token approaches expiration, the kubelet volume plugin will proactively rotate the service account token. The kubelet - will start trying to rotate the token if - the token is older than 80 percent of its - time to live or if the token is older than - 24 hours.Defaults to 1 hour and must be - at least 10 minutes. - format: int64 + will start trying to rotate the token if the + token is older than 80 percent of its time + to live or if the token is older than 24 hours.Defaults + to 1 hour and must be at least 10 minutes. type: integer + format: int64 path: - description: Path is the path relative to - the mount point of the file to project the - token into. + description: Path is the path relative to the + mount point of the file to project the token + into. type: string - required: - - path - type: object - type: object - type: array - required: - - sources - type: object quobyte: description: Quobyte represents a Quobyte mount on the host that shares a pod's lifetime + type: object + required: + - registry + - volume properties: group: description: Group to map volume access to Default is @@ -1192,18 +1334,18 @@ spec: type: string readOnly: description: ReadOnly here will force the Quobyte volume - to be mounted with read-only permissions. Defaults - to false. + to be mounted with read-only permissions. Defaults to + false. type: boolean registry: description: Registry represents a single or multiple - Quobyte Registry services specified as a string as - host:port pair (multiple entries are separated with - commas) which acts as the central registry for volumes + Quobyte Registry services specified as a string as host:port + pair (multiple entries are separated with commas) which + acts as the central registry for volumes type: string tenant: - description: Tenant owning the given Quobyte volume - in the Backend Used with dynamically provisioned Quobyte + description: Tenant owning the given Quobyte volume in + the Backend Used with dynamically provisioned Quobyte volumes, value is set by the plugin type: string user: @@ -1214,68 +1356,67 @@ spec: description: Volume is a string that references an already created Quobyte volume by name. type: string - required: - - registry - - volume - type: object rbd: - description: 'RBD represents a Rados Block Device mount - on the host that shares a pod''s lifetime. More info: - https://examples.k8s.io/volumes/rbd/README.md' + description: 'RBD represents a Rados Block Device mount on + the host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md' + type: object + required: + - image + - monitors properties: fsType: - description: 'Filesystem type of the volume that you - want to mount. Tip: Ensure that the filesystem type - is supported by the host operating system. Examples: - "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd - TODO: how do we prevent errors in the filesystem from - compromising the machine' + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", + "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors in the filesystem from + compromising the machine' type: string image: description: 'The rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' type: string keyring: description: 'Keyring is the path to key ring for RBDUser. - Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' type: string monitors: description: 'A collection of Ceph monitors. More info: - https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: array items: type: string - type: array pool: description: 'The rados pool name. Default is rbd. More - info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' type: string readOnly: - description: 'ReadOnly here will force the ReadOnly - setting in VolumeMounts. Defaults to false. More info: - https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + description: 'ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' type: boolean secretRef: description: 'SecretRef is name of the authentication - secret for RBDUser. If provided overrides keyring. - Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + secret for RBDUser. If provided overrides keyring. Default + is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: object properties: name: description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + TODO: Add other useful fields. apiVersion, kind, + uid?' type: string - type: object user: - description: 'The rados user name. Default is admin. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + description: 'The rados user name. Default is admin. More + info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' type: string - required: - - image - - monitors - type: object scaleIO: description: ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes. + type: object + required: + - gateway + - secretRef + - system properties: fsType: description: Filesystem type to mount. Must be a filesystem @@ -1294,16 +1435,16 @@ spec: here will force the ReadOnly setting in VolumeMounts. type: boolean secretRef: - description: SecretRef references to the secret for - ScaleIO user and other sensitive information. If this - is not provided, Login operation will fail. + description: SecretRef references to the secret for ScaleIO + user and other sensitive information. If this is not + provided, Login operation will fail. + type: object properties: name: description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + TODO: Add other useful fields. apiVersion, kind, + uid?' type: string - type: object sslEnabled: description: Flag to enable/disable SSL communication with Gateway, default false @@ -1322,84 +1463,76 @@ spec: in ScaleIO. type: string volumeName: - description: The name of a volume already created in - the ScaleIO system that is associated with this volume - source. + description: The name of a volume already created in the + ScaleIO system that is associated with this volume source. type: string - required: - - gateway - - secretRef - - system - type: object secret: description: 'Secret represents a secret that should populate - this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: object properties: defaultMode: - description: 'Optional: mode bits to use on created - files by default. Must be a value between 0 and 0777. - Defaults to 0644. Directories within the path are - not affected by this setting. This might be in conflict - with other options that affect the file mode, like - fsGroup, and the result can be other mode bits set.' - format: int32 + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. Directories within the path are not affected + by this setting. This might be in conflict with other + options that affect the file mode, like fsGroup, and + the result can be other mode bits set.' type: integer + format: int32 items: - description: If unspecified, each key-value pair in - the Data field of the referenced Secret will be projected + description: If unspecified, each key-value pair in the + Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the Secret, the volume setup will - error unless it is marked optional. Paths must be - relative and may not contain the '..' path or start - with '..'. + error unless it is marked optional. Paths must be relative + and may not contain the '..' path or start with '..'. + type: array items: - description: Maps a string key to a path within a - volume. + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path properties: key: description: The key to project. type: string mode: description: 'Optional: mode bits to use on this - file, must be a value between 0 and 0777. If - not specified, the volume defaultMode will be - used. This might be in conflict with other options - that affect the file mode, like fsGroup, and - the result can be other mode bits set.' - format: int32 + file, must be a value between 0 and 0777. If not + specified, the volume defaultMode will be used. + This might be in conflict with other options that + affect the file mode, like fsGroup, and the result + can be other mode bits set.' type: integer + format: int32 path: - description: The relative path of the file to - map the key to. May not be an absolute path. - May not contain the path element '..'. May not - start with the string '..'. + description: The relative path of the file to map + the key to. May not be an absolute path. May not + contain the path element '..'. May not start with + the string '..'. type: string - required: - - key - - path - type: object - type: array optional: - description: Specify whether the Secret or its keys - must be defined + description: Specify whether the Secret or its keys must + be defined type: boolean secretName: description: 'Name of the secret in the pod''s namespace - to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' type: string - type: object storageos: description: StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes. + type: object properties: fsType: description: Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", - "xfs", "ntfs". Implicitly inferred to be "ext4" if - unspecified. + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. type: string readOnly: description: Defaults to false (read/write). ReadOnly @@ -1409,55 +1542,237 @@ spec: description: SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted. + type: object properties: name: description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' + TODO: Add other useful fields. apiVersion, kind, + uid?' type: string - type: object volumeName: description: VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace. type: string volumeNamespace: - description: VolumeNamespace specifies the scope of - the volume within StorageOS. If no namespace is specified + description: VolumeNamespace specifies the scope of the + volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows - the Kubernetes name scoping to be mirrored within - StorageOS for tighter integration. Set VolumeName - to any name to override the default behaviour. Set - to "default" if you are not using namespaces within - StorageOS. Namespaces that do not pre-exist within - StorageOS will be created. + the Kubernetes name scoping to be mirrored within StorageOS + for tighter integration. Set VolumeName to any name + to override the default behaviour. Set to "default" + if you are not using namespaces within StorageOS. Namespaces + that do not pre-exist within StorageOS will be created. type: string - type: object vsphereVolume: description: VsphereVolume represents a vSphere volume attached and mounted on kubelets host machine + type: object + required: + - volumePath properties: fsType: description: Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", - "xfs", "ntfs". Implicitly inferred to be "ext4" if - unspecified. + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. type: string storagePolicyID: - description: Storage Policy Based Management (SPBM) - profile ID associated with the StoragePolicyName. + description: Storage Policy Based Management (SPBM) profile + ID associated with the StoragePolicyName. type: string storagePolicyName: - description: Storage Policy Based Management (SPBM) - profile name. + description: Storage Policy Based Management (SPBM) profile + name. type: string volumePath: description: Path that identifies vSphere volume vmdk type: string - required: - - volumePath - type: object - required: - - name + installPlanApproval: + description: Approval is the user approval policy for an InstallPlan. + type: string + name: + type: string + source: + type: string + sourceNamespace: + type: string + startingCSV: + type: string + status: + type: object + required: + - lastUpdated + properties: + catalogHealth: + description: CatalogHealth contains the Subscription's view of its relevant + CatalogSources' status. It is used to determine SubscriptionStatusConditions + related to CatalogSources. + type: array + items: + description: SubscriptionCatalogHealth describes the health of a CatalogSource + the Subscription knows about. + type: object + required: + - catalogSourceRef + - healthy + - lastUpdated + properties: + catalogSourceRef: + description: CatalogSourceRef is a reference to a CatalogSource. type: object - type: array + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead + of an entire object, this string should contain a valid + JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen + only to have some well-defined way of referencing a part + of an object. TODO: this design is not final and this field + is subject to change in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference + is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + healthy: + description: Healthy is true if the CatalogSource is healthy; + false otherwise. + type: boolean + lastUpdated: + description: LastUpdated represents the last time that the CatalogSourceHealth + changed + type: string + format: date-time + conditions: + description: Conditions is a list of the latest available observations + about a Subscription's current state. + type: array + items: + description: SubscriptionCondition represents the latest available + observations of a Subscription's state. + type: object + required: + - status + - type + properties: + lastHeartbeatTime: + description: LastHeartbeatTime is the last time we got an update + on a given condition + type: string + format: date-time + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transit from one status to another + type: string + format: date-time + message: + description: Message is a human-readable message indicating details + about last transition. + type: string + reason: + description: Reason is a one-word CamelCase reason for the condition's + last transition. + type: string + status: + description: Status is the status of the condition, one of True, + False, Unknown. + type: string + type: + description: Type is the type of Subscription condition. + type: string + currentCSV: + description: CurrentCSV is the CSV the Subscription is progressing to. + type: string + installPlanRef: + description: InstallPlanRef is a reference to the latest InstallPlan + that contains the Subscription's current CSV. + type: object + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + installedCSV: + description: InstalledCSV is the CSV currently installed by the Subscription. + type: string + installplan: + description: 'Install is a reference to the latest InstallPlan generated + for the Subscription. DEPRECATED: InstallPlanRef' + type: object + required: + - apiVersion + - kind + - name + - uuid + properties: + apiVersion: + type: string + kind: + type: string + name: + type: string + uuid: + description: UID is a type that holds unique ID values, including + UUIDs. Because we don't ONLY use UUIDs, this is an alias to string. Being + a type captures intent and helps make sure that UIDs and names + do not get conflated. + type: string + lastUpdated: + description: LastUpdated represents the last time that the Subscription + status was updated. + type: string + format: date-time + reason: + description: Reason is the reason the Subscription was transitioned + to its current state. + type: string + state: + description: State represents the current state of the Subscription + type: string diff --git a/manifests/0000_50_olm_06-catalogsource.crd.yaml b/manifests/0000_50_olm_06-catalogsource.crd.yaml index 688fcc5736..c9421ac5d9 100644 --- a/manifests/0000_50_olm_06-catalogsource.crd.yaml +++ b/manifests/0000_50_olm_06-catalogsource.crd.yaml @@ -38,91 +38,132 @@ spec: - name: Age type: date JSONPath: .metadata.creationTimestamp + preserveUnknownFields: false subresources: - # status enables the status subresource. + # status enables the status subresource. status: {} validation: openAPIV3Schema: - description: A source configured to find packages and updates. + description: CatalogSource is a repository of CSVs, CRDs, and operator packages. + type: object + required: + - metadata + - spec properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object spec: type: object - description: Spec for a catalog source. required: - sourceType properties: - sourceType: + address: + description: 'Address is a host that OLM can use to connect to a pre-existing + registry. Format: : Only used when SourceType + = SourceTypeGrpc. Ignored when the Image field is set.' type: string - description: The type of the source. `configmap` is the new name for `internal` - enum: - - internal # deprecated - - configmap - - grpc - configMap: + description: ConfigMap is the name of the ConfigMap to be used to back + a configmap-server registry. Only used when SourceType = SourceTypeConfigmap + or SourceTypeInternal. type: string - description: The name of a ConfigMap that holds the entries for an in-memory catalog. - - address: + description: type: string - description: An optional address. When set, directs OLM to connect to use a pre-existing registry server at this address. - - image: - type: string - description: An image that serves a grpc registry. Only valid for `grpc` sourceType. If both image and address are set, OLM does not use the address field. - displayName: + description: Metadata + type: string + icon: + type: object + required: + - base64data + - mediatype + properties: + base64data: + type: string + mediatype: + type: string + image: + description: Image is an operator-registry container image to instantiate + a registry-server with. Only used when SourceType = SourceTypeGrpc. + If present, the address field is ignored. type: string - description: Pretty name for display - publisher: type: string - description: The name of an entity that publishes this catalog - secrets: + description: Secrets represent set of secrets that can be used to access + the contents of the catalog. It is best to keep this list small, since + each will need to be tried for every catalog entry. type: array - description: A set of secrets that can be used to access the contents of the catalog. It is best to keep this list small, since each will need to be tried for every catalog entry. items: type: string - description: A name of a secret in the namespace where the CatalogSource is defined. + sourceType: + description: SourceType is the type of source + type: string status: type: object - description: The status of the CatalogSource properties: configMapReference: type: object - description: If sourceType is `internal` or `configmap`, then this holds a reference to the configmap associated with this CatalogSource. + required: + - name + - namespace properties: + lastUpdateTime: + type: string + format: date-time name: type: string - description: name of the configmap namespace: type: string - description: namespace of the configmap resourceVersion: type: string - description: resourceVersion of the configmap uid: + description: UID is a type that holds unique ID values, including + UUIDs. Because we don't ONLY use UUIDs, this is an alias to string. Being + a type captures intent and helps make sure that UIDs and names + do not get conflated. + type: string + connectionState: + type: object + required: + - lastObservedState + properties: + address: type: string - description: uid of the configmap + lastConnect: + type: string + format: date-time + lastObservedState: + type: string + message: + description: A human readable message indicating details about why the + ClusterServiceVersion is in this condition. + type: string + reason: + description: Reason is the reason the Subscription was transitioned + to its current state. + type: string registryService: type: object properties: + createdAt: + type: string + format: date-time + port: + type: string protocol: type: string - description: protocol of the registry service - enum: - - grpc serviceName: type: string - description: name of the registry service serviceNamespace: type: string - description: namespace of the registry service - port: - type: string - description: port of the registry service - lastSync: - type: string - description: the last time the catalog was updated. If this time is less than the last updated time on the object, the catalog will be re-cached. - diff --git a/manifests/0000_50_olm_10-operatorgroup.crd.yaml b/manifests/0000_50_olm_10-operatorgroup.crd.yaml index d0b44a4d93..3c50a96a03 100644 --- a/manifests/0000_50_olm_10-operatorgroup.crd.yaml +++ b/manifests/0000_50_olm_10-operatorgroup.crd.yaml @@ -6,94 +6,155 @@ spec: group: operators.coreos.com version: v1 versions: - - name: v1 - served: true - storage: true - - name: v1alpha2 - served: true - storage: false + - name: v1 + served: true + storage: true names: plural: operatorgroups singular: operatorgroup kind: OperatorGroup listKind: OperatorGroupList shortNames: - - og + - og categories: - - olm + - olm scope: Namespaced + preserveUnknownFields: false subresources: # status enables the status subresource. status: {} - validation: - openAPIV3Schema: - description: A grouping of namespaces for usage with an operator. + "validation": + "openAPIV3Schema": + description: OperatorGroup is the unit of multitenancy for OLM managed operators. + It constrains the installation of operators in its namespace to a specified + set of target namespaces. + type: object + required: + - metadata properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object spec: + description: OperatorGroupSpec is the spec for an OperatorGroup resource. type: object - description: Spec for an OperatorGroup. properties: selector: + description: Selector selects the OperatorGroup's target namespaces. type: object - description: Optional label selector to find resources associated with or managed by the operator - anyOf: - - properties: - matchLabels: - type: object - description: Label key:value pairs to match directly - required: - - matchLabels - - properties: - matchExpressions: - type: array - description: A set of expressions to match against the resource. - items: - allOf: - - type: object - required: - - key - - operator - - values - properties: - key: - type: string - description: the key to match - operator: - type: string - description: the operator for the expression - enum: - - In - - NotIn - - Exists - - DoesNotExist - values: - type: array - description: set of values for the expression - required: - - matchExpressions - targetNamespaces: - type: array - description: Optional list of target namespaces. If set, OLM will ignore selector. - items: - type: string - pattern: ^\S+$ + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that contains + values, a key, and an operator that relates the key and values. + type: object + required: + - key + - operator + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: operator represents a key's relationship to a + set of values. Valid operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string values. If the operator + is In or NotIn, the values array must be non-empty. If the + operator is Exists or DoesNotExist, the values array must + be empty. This array is replaced during a strategic merge + patch. + type: array + items: + type: string + matchLabels: + description: matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator is + "In", and the values array contains only "value". The requirements + are ANDed. + type: object + additionalProperties: + type: string serviceAccountName: + description: ServiceAccountName is the admin specified service account + which will be used to deploy operator(s) in this operator group. type: string staticProvidedAPIs: + description: Static tells OLM not to update the OperatorGroup's providedAPIs + annotation type: boolean - description: If true, OLM will not modify the OperatorGroup's providedAPIs annotation. + targetNamespaces: + description: TargetNamespaces is an explicit set of namespaces to target. + If it is set, Selector is ignored. + type: array + items: + type: string status: + description: OperatorGroupStatus is the status for an OperatorGroupResource. type: object - description: The status of the OperatorGroup. + required: + - lastUpdated properties: lastUpdated: - format: date-time + description: LastUpdated is a timestamp of the last time the OperatorGroup's + status was Updated. type: string + format: date-time namespaces: + description: Namespaces is the set of target namespaces for the OperatorGroup. + type: array items: type: string - type: array - required: - - lastUpdated - required: - - metadata + serviceAccountRef: + description: ServiceAccountRef references the service account object + specified. + type: object + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string From 4e5ffba50c10d25677444e63b62c9f0e5ff8001f Mon Sep 17 00:00:00 2001 From: awgreene Date: Wed, 19 Feb 2020 11:39:18 -0500 Subject: [PATCH 4/4] Reinstate v1alpha2 operatorgroup The v1alpha2 version of the operatorgroup crd in the 4.4 OpenShift release was not properly deprecated. Adding back that version to the crd spc. --- .../0000_50_olm_10-operatorgroup.crd.yaml | 274 +++++++++--------- .../0000_50_olm_10-operatorgroup.crd.yaml | 3 + 2 files changed, 142 insertions(+), 135 deletions(-) diff --git a/deploy/chart/templates/0000_50_olm_10-operatorgroup.crd.yaml b/deploy/chart/templates/0000_50_olm_10-operatorgroup.crd.yaml index 1fac9e1a8e..f093b603b4 100644 --- a/deploy/chart/templates/0000_50_olm_10-operatorgroup.crd.yaml +++ b/deploy/chart/templates/0000_50_olm_10-operatorgroup.crd.yaml @@ -9,6 +9,145 @@ spec: - name: v1 served: true storage: true + "schema": + "openAPIV3Schema": + description: OperatorGroup is the unit of multitenancy for OLM managed operators. + It constrains the installation of operators in its namespace to a specified + set of target namespaces. + type: object + required: + - metadata + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: OperatorGroupSpec is the spec for an OperatorGroup resource. + type: object + properties: + selector: + description: Selector selects the OperatorGroup's target namespaces. + type: object + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + type: array + items: + description: A label selector requirement is a selector that + contains values, a key, and an operator that relates the key + and values. + type: object + required: + - key + - operator + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: operator represents a key's relationship to + a set of values. Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of string values. If the + operator is In or NotIn, the values array must be non-empty. + If the operator is Exists or DoesNotExist, the values + array must be empty. This array is replaced during a strategic + merge patch. + type: array + items: + type: string + matchLabels: + description: matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator + is "In", and the values array contains only "value". The requirements + are ANDed. + type: object + additionalProperties: + type: string + serviceAccountName: + description: ServiceAccountName is the admin specified service account + which will be used to deploy operator(s) in this operator group. + type: string + staticProvidedAPIs: + description: Static tells OLM not to update the OperatorGroup's providedAPIs + annotation + type: boolean + targetNamespaces: + description: TargetNamespaces is an explicit set of namespaces to + target. If it is set, Selector is ignored. + type: array + items: + type: string + status: + description: OperatorGroupStatus is the status for an OperatorGroupResource. + type: object + required: + - lastUpdated + properties: + lastUpdated: + description: LastUpdated is a timestamp of the last time the OperatorGroup's + status was Updated. + type: string + format: date-time + namespaces: + description: Namespaces is the set of target namespaces for the OperatorGroup. + type: array + items: + type: string + serviceAccountRef: + description: ServiceAccountRef references the service account object + specified. + type: object + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of + an entire object, this string should contain a valid JSON/Go + field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen + only to have some well-defined way of referencing a part of + an object. TODO: this design is not final and this field is + subject to change in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference + is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + - name: v1alpha2 + served: true + storage: false names: plural: operatorgroups singular: operatorgroup @@ -22,138 +161,3 @@ spec: subresources: # status enables the status subresource. status: {} - "validation": - "openAPIV3Schema": - description: OperatorGroup is the unit of multitenancy for OLM managed operators. - It constrains the installation of operators in its namespace to a specified - set of target namespaces. - type: object - required: - - metadata - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: OperatorGroupSpec is the spec for an OperatorGroup resource. - type: object - properties: - selector: - description: Selector selects the OperatorGroup's target namespaces. - type: object - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. - The requirements are ANDed. - type: array - items: - description: A label selector requirement is a selector that contains - values, a key, and an operator that relates the key and values. - type: object - required: - - key - - operator - properties: - key: - description: key is the label key that the selector applies - to. - type: string - operator: - description: operator represents a key's relationship to a - set of values. Valid operators are In, NotIn, Exists and - DoesNotExist. - type: string - values: - description: values is an array of string values. If the operator - is In or NotIn, the values array must be non-empty. If the - operator is Exists or DoesNotExist, the values array must - be empty. This array is replaced during a strategic merge - patch. - type: array - items: - type: string - matchLabels: - description: matchLabels is a map of {key,value} pairs. A single - {key,value} in the matchLabels map is equivalent to an element - of matchExpressions, whose key field is "key", the operator is - "In", and the values array contains only "value". The requirements - are ANDed. - type: object - additionalProperties: - type: string - serviceAccountName: - description: ServiceAccountName is the admin specified service account - which will be used to deploy operator(s) in this operator group. - type: string - staticProvidedAPIs: - description: Static tells OLM not to update the OperatorGroup's providedAPIs - annotation - type: boolean - targetNamespaces: - description: TargetNamespaces is an explicit set of namespaces to target. - If it is set, Selector is ignored. - type: array - items: - type: string - status: - description: OperatorGroupStatus is the status for an OperatorGroupResource. - type: object - required: - - lastUpdated - properties: - lastUpdated: - description: LastUpdated is a timestamp of the last time the OperatorGroup's - status was Updated. - type: string - format: date-time - namespaces: - description: Namespaces is the set of target namespaces for the OperatorGroup. - type: array - items: - type: string - serviceAccountRef: - description: ServiceAccountRef references the service account object - specified. - type: object - properties: - apiVersion: - description: API version of the referent. - type: string - fieldPath: - description: 'If referring to a piece of an object instead of an - entire object, this string should contain a valid JSON/Go field - access statement, such as desiredState.manifest.containers[2]. - For example, if the object reference is to a container within - a pod, this would take on a value like: "spec.containers{name}" - (where "name" refers to the name of the container that triggered - the event) or if no container name is specified "spec.containers[2]" - (container with index 2 in this pod). This syntax is chosen only - to have some well-defined way of referencing a part of an object. - TODO: this design is not final and this field is subject to change - in the future.' - type: string - kind: - description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' - type: string - namespace: - description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' - type: string - resourceVersion: - description: 'Specific resourceVersion to which this reference is - made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' - type: string - uid: - description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' - type: string diff --git a/manifests/0000_50_olm_10-operatorgroup.crd.yaml b/manifests/0000_50_olm_10-operatorgroup.crd.yaml index 3c50a96a03..6bc82181f2 100644 --- a/manifests/0000_50_olm_10-operatorgroup.crd.yaml +++ b/manifests/0000_50_olm_10-operatorgroup.crd.yaml @@ -9,6 +9,9 @@ spec: - name: v1 served: true storage: true + - name: v1alpha2 + served: true + storage: false names: plural: operatorgroups singular: operatorgroup