diff --git a/go.mod b/go.mod index 633d916c04c..fd7052db347 100644 --- a/go.mod +++ b/go.mod @@ -81,7 +81,7 @@ require ( github.com/openshift/cluster-api-provider-baremetal v0.0.0-20220408122422-7a548effc26e github.com/openshift/cluster-api-provider-libvirt v0.2.1-0.20230308152226-83c0473d4429 github.com/openshift/cluster-api-provider-ovirt v0.1.1-0.20220323121149-e3f2850dd519 - github.com/openshift/hive/apis v0.0.0-20220222213051-def9088fdb5a + github.com/openshift/hive/apis v0.0.0-20231220215202-ad99b9e52d27 github.com/openshift/library-go v0.0.0-20240919205913-c96b82b3762b github.com/openshift/machine-api-provider-gcp v0.0.1-0.20231014045125-6096cc86f3ba github.com/openshift/machine-api-provider-ibmcloud v0.0.0-20231207164151-6b0b8ea7b16d @@ -216,6 +216,7 @@ require ( github.com/kr/fs v0.1.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect + github.com/lib/pq v1.10.9 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -243,7 +244,8 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0 // indirect github.com/opencontainers/runtime-spec v1.2.0 // indirect - github.com/openshift/custom-resource-status v1.1.2 // indirect + github.com/openshift/assisted-service v1.0.10-0.20230830164851-6573b5d7021d // indirect + github.com/openshift/custom-resource-status v1.1.3-0.20220503160415-f2fdb4999d87 // indirect github.com/openshift/machine-api-operator v0.2.1-0.20240930121047-57b7917e6140 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect @@ -258,6 +260,7 @@ require ( github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace // indirect github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect + github.com/thoas/go-funk v0.9.3 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xlab/treeprint v1.2.0 // indirect github.com/yosida95/uritemplate/v3 v3.0.2 // indirect @@ -311,11 +314,11 @@ replace ( replace sigs.k8s.io/cluster-api-provider-ibmcloud => sigs.k8s.io/cluster-api-provider-ibmcloud v0.9.0-alpha.0.0.20240913094112-c6bcd313bce0 -replace github.com/openshift/assisted-service/api => github.com/openshift/assisted-service/api v0.0.0-20230831114549-1922eda29cf8 +replace github.com/openshift/assisted-service/api => github.com/openshift/assisted-service/api v0.0.0-20241001055825-63e8b0d3ad63 -replace github.com/openshift/assisted-service/client => github.com/openshift/assisted-service/client v0.0.0-20230831114549-1922eda29cf8 +replace github.com/openshift/assisted-service/client => github.com/openshift/assisted-service/client v0.0.0-20241001055825-63e8b0d3ad63 -replace github.com/openshift/assisted-service/models => github.com/openshift/assisted-service/models v0.0.0-20230831114549-1922eda29cf8 +replace github.com/openshift/assisted-service/models => github.com/openshift/assisted-service/models v0.0.0-20241001055825-63e8b0d3ad63 // https://issues.redhat.com/browse/OCPBUGS-8119 // https://issues.redhat.com/browse/OCPBUGS-27507 diff --git a/go.sum b/go.sum index 177be045d5e..7e32f2f4047 100644 --- a/go.sum +++ b/go.sum @@ -649,6 +649,8 @@ github.com/lestrrat-go/httpcc v1.0.0/go.mod h1:tGS/u00Vh5N6FHNkExqGGNId8e0Big+++ github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc= github.com/lestrrat-go/jwx v1.2.20/go.mod h1:tLE1XszaFgd7zaS5wHe4NxA+XVhu7xgdRvDpNyi3kNM= github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/logrusorgru/aurora/v3 v3.0.0/go.mod h1:vsR12bk5grlLvLXAYrBsb5Oc/N+LxAlxggSjiwMnCUc= @@ -768,12 +770,14 @@ github.com/openshift/api v0.0.0-20241001152557-e415140e5d5f h1:ya1OmyZm3LIIxI3U9 github.com/openshift/api v0.0.0-20241001152557-e415140e5d5f/go.mod h1:Shkl4HanLwDiiBzakv+con/aMGnVE2MAGvoKp5oyYUo= github.com/openshift/assisted-image-service v0.0.0-20240607085136-02df2e56dde6 h1:U6ve+dnHlHhAELoxX+rdFOHVhoaYl0l9qtxwYtsO6C0= github.com/openshift/assisted-image-service v0.0.0-20240607085136-02df2e56dde6/go.mod h1:o2H5VwQhUD8P6XsK6dRmKpCCJqVvv12KJQZBXmcCXCU= -github.com/openshift/assisted-service/api v0.0.0-20230831114549-1922eda29cf8 h1:+fZLKbycDo4JeLwPGVSAgf2XPaJGLM341l9ZfrrlxG0= -github.com/openshift/assisted-service/api v0.0.0-20230831114549-1922eda29cf8/go.mod h1:PmMnbVno5ocinfELDdKOaatvT5ucJLrau+fjHQNeCiY= -github.com/openshift/assisted-service/client v0.0.0-20230831114549-1922eda29cf8 h1:9NpCGby6O44BlWqWCbd8wcN4QwBebR8McQGrjTwhlzQ= -github.com/openshift/assisted-service/client v0.0.0-20230831114549-1922eda29cf8/go.mod h1:k0S4WqxFuMBJYyinCgHjtWoEnE6/R2OyakW60MUHPYQ= -github.com/openshift/assisted-service/models v0.0.0-20230831114549-1922eda29cf8 h1:lQO/vwmLX4kSmZitusnqdq9KbuW402DWkcmR0CJfgBI= -github.com/openshift/assisted-service/models v0.0.0-20230831114549-1922eda29cf8/go.mod h1:ImkG1jQgM72erhLaZtWqEn0g0Y8AYUOxLR/HgvlPi2Y= +github.com/openshift/assisted-service v1.0.10-0.20230830164851-6573b5d7021d h1:CKw2Y4EdaFsMoqAdr2Tq0nlYTaaXmCRdP0gOu7pN64U= +github.com/openshift/assisted-service v1.0.10-0.20230830164851-6573b5d7021d/go.mod h1:1J9kimemAdpLZVb3yLDEGmzwK0DXZ4/x6POeEog8A+4= +github.com/openshift/assisted-service/api v0.0.0-20241001055825-63e8b0d3ad63 h1:ErPj1LW5RatF+Z9vSdAiXUHMuWdJM1kBbQA6rRixo1E= +github.com/openshift/assisted-service/api v0.0.0-20241001055825-63e8b0d3ad63/go.mod h1:Jhy3+Azp3IkLDCe4ET54nQondYKa+BlHOsurSgZsD4s= +github.com/openshift/assisted-service/client v0.0.0-20241001055825-63e8b0d3ad63 h1:wYkC5wQIGjTORD3CVSNTs3dLaly9Vge/GrluayUMe+g= +github.com/openshift/assisted-service/client v0.0.0-20241001055825-63e8b0d3ad63/go.mod h1:y+GNZgra/iQDYlacSjgscim8ldrSOMtV3jQaSKC3APc= +github.com/openshift/assisted-service/models v0.0.0-20241001055825-63e8b0d3ad63 h1:192yBdPaJccBdFhBVKgt4Nirm6X5IA+yhh54CnGd/n8= +github.com/openshift/assisted-service/models v0.0.0-20241001055825-63e8b0d3ad63/go.mod h1:bx9NsPeBkhn7az/qFHo3hW8wFqFj4BmT3g1R7wR9gcw= github.com/openshift/baremetal-operator/apis v0.0.0-20231128154154-6736c9b9c6c8 h1:f5nZJ4mB9Rf1epInmKoqDJGdoHyqZwHCiaE/OH4Yx84= github.com/openshift/baremetal-operator/apis v0.0.0-20231128154154-6736c9b9c6c8/go.mod h1:CvKrrnAcvvtrZIc9y9WaqWmJhK0AJ9sWnh+VP4d7jcM= github.com/openshift/baremetal-operator/pkg/hardwareutils v0.0.0-20231128154154-6736c9b9c6c8 h1:38vY9w7dXqB7tI9g1GCUnpahNDyBbp9Yylq+BQ154YE= @@ -794,10 +798,10 @@ github.com/openshift/cluster-api-provider-ovirt v0.1.1-0.20220323121149-e3f2850d github.com/openshift/cluster-api-provider-ovirt v0.1.1-0.20220323121149-e3f2850dd519/go.mod h1:C7unCUThP8eqT4xQfbvg3oIDn2S9TYtb0wbBoH/SR2U= github.com/openshift/cluster-control-plane-machine-set-operator v0.0.0-20240909043600-373ac49835bf h1:mfMmaD9+vZIZQq3MGXsS/AGHXekj4wIn3zc1Cs1EY8M= github.com/openshift/cluster-control-plane-machine-set-operator v0.0.0-20240909043600-373ac49835bf/go.mod h1:2fZsjZ3QSPkoMUc8QntXfeBb8AnvW+WIYwwQX8vmgvQ= -github.com/openshift/custom-resource-status v1.1.2 h1:C3DL44LEbvlbItfd8mT5jWrqPfHnSOQoQf/sypqA6A4= -github.com/openshift/custom-resource-status v1.1.2/go.mod h1:DB/Mf2oTeiAmVVX1gN+NEqweonAPY0TKUwADizj8+ZA= -github.com/openshift/hive/apis v0.0.0-20220222213051-def9088fdb5a h1:E+XPJs/aVvYsrlJzo2ED38ZTR2RTNUlFMmOaFAAdMZg= -github.com/openshift/hive/apis v0.0.0-20220222213051-def9088fdb5a/go.mod h1:E1bgquRiwfugdArdecPbpYIrAdve5kTzMaJb0+8jMXI= +github.com/openshift/custom-resource-status v1.1.3-0.20220503160415-f2fdb4999d87 h1:cHyxR+Y8rAMT6m1jQCaYGRwikqahI0OjjUDhFNf3ySQ= +github.com/openshift/custom-resource-status v1.1.3-0.20220503160415-f2fdb4999d87/go.mod h1:DB/Mf2oTeiAmVVX1gN+NEqweonAPY0TKUwADizj8+ZA= +github.com/openshift/hive/apis v0.0.0-20231220215202-ad99b9e52d27 h1:9C86viyQl7HE9yg7Gctgx803Oq6DbrCUAberyMVcWDE= +github.com/openshift/hive/apis v0.0.0-20231220215202-ad99b9e52d27/go.mod h1:RRH8lt09SAiPECNdsbh7Gun0lkcRWi1nYKq6tDp5WxQ= github.com/openshift/library-go v0.0.0-20240919205913-c96b82b3762b h1:y2DduJug7UZqTu0QTkRPAu73nskuUbFA66fmgxVf/fI= github.com/openshift/library-go v0.0.0-20240919205913-c96b82b3762b/go.mod h1:f8QcnrooSwGa96xI4UaKbKGJZskhTCGeimXKyc4t/ZU= github.com/openshift/machine-api-operator v0.2.1-0.20240930121047-57b7917e6140 h1:VKn644y4Ra94L8YZ3JT8ZuOZMEspZt8yoIjHeaRM3pA= @@ -902,6 +906,8 @@ github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtse github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/thedevsaddam/retry v0.0.0-20200324223450-9769a859cc6d h1:U8ZUZKRBI+Xgo3QWD/alCQ2vIysyl0Efx8yCJWPdaGQ= github.com/thedevsaddam/retry v0.0.0-20200324223450-9769a859cc6d/go.mod h1:2rz2mY+1qEXG47loLDkV+ZJHGFwmhax5rOTpP+5aR80= +github.com/thoas/go-funk v0.9.3 h1:7+nAEx3kn5ZJcnDm2Bh23N2yOtweO14bi//dvRtgLpw= +github.com/thoas/go-funk v0.9.3/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= diff --git a/pkg/asset/agent/common.go b/pkg/asset/agent/common.go index 9bc49f70f91..578a9432473 100644 --- a/pkg/asset/agent/common.go +++ b/pkg/asset/agent/common.go @@ -11,6 +11,11 @@ import ( "github.com/openshift/installer/pkg/types/vsphere" ) +const ( + // ExternalPlatformNameOci is the name of the external platform for OCP. + ExternalPlatformNameOci = "oci" +) + // SupportedInstallerPlatforms lists the supported platforms for agent installer. func SupportedInstallerPlatforms() []string { return []string{baremetal.Name, vsphere.Name, none.Name, external.Name} diff --git a/pkg/asset/agent/image/agentartifacts.go b/pkg/asset/agent/image/agentartifacts.go index c7707c843c6..b1003f0ff6e 100644 --- a/pkg/asset/agent/image/agentartifacts.go +++ b/pkg/asset/agent/image/agentartifacts.go @@ -9,8 +9,8 @@ import ( "strings" "github.com/openshift/assisted-image-service/pkg/isoeditor" - "github.com/openshift/assisted-service/models" "github.com/openshift/installer/pkg/asset" + "github.com/openshift/installer/pkg/asset/agent" config "github.com/openshift/installer/pkg/asset/agent/agentconfig" "github.com/openshift/installer/pkg/asset/agent/manifests" "github.com/openshift/installer/pkg/asset/agent/mirror" @@ -76,7 +76,7 @@ func (a *AgentArtifacts) Generate(_ context.Context, dependencies asset.Parents) } var agentTuiFiles []string - if agentClusterInstall.GetExternalPlatformName() != string(models.PlatformTypeOci) { + if agentClusterInstall.GetExternalPlatformName() != agent.ExternalPlatformNameOci { agentTuiFiles, err = a.fetchAgentTuiFiles(agentManifests.ClusterImageSet.Spec.ReleaseImage, agentManifests.GetPullSecretData(), registriesConf.MirrorConfig) if err != nil { return err diff --git a/pkg/asset/agent/image/kargs.go b/pkg/asset/agent/image/kargs.go index 67fdc80c852..fb82c6596f9 100644 --- a/pkg/asset/agent/image/kargs.go +++ b/pkg/asset/agent/image/kargs.go @@ -7,8 +7,8 @@ import ( "github.com/sirupsen/logrus" hiveext "github.com/openshift/assisted-service/api/hiveextension/v1beta1" - "github.com/openshift/assisted-service/models" "github.com/openshift/installer/pkg/asset" + "github.com/openshift/installer/pkg/asset/agent" "github.com/openshift/installer/pkg/asset/agent/joiner" "github.com/openshift/installer/pkg/asset/agent/manifests" "github.com/openshift/installer/pkg/asset/agent/workflow" @@ -40,8 +40,8 @@ func (a *Kargs) Generate(_ context.Context, dependencies asset.Parents) error { case workflow.AgentWorkflowTypeInstall: a.fips = agentClusterInstall.FIPSEnabled() // Add kernel args for external oci platform - if agentClusterInstall.GetExternalPlatformName() == string(models.PlatformTypeOci) { - logrus.Debugf("Added kernel args to enable serial console for %s %s platform", hiveext.ExternalPlatformType, string(models.PlatformTypeOci)) + if agentClusterInstall.GetExternalPlatformName() == agent.ExternalPlatformNameOci { + logrus.Debugf("Added kernel args to enable serial console for %s %s platform", hiveext.ExternalPlatformType, agent.ExternalPlatformNameOci) a.consoleArgs = " console=ttyS0" } diff --git a/pkg/asset/agent/image/kargs_test.go b/pkg/asset/agent/image/kargs_test.go index 5e6aed81b83..a99639a3624 100644 --- a/pkg/asset/agent/image/kargs_test.go +++ b/pkg/asset/agent/image/kargs_test.go @@ -9,6 +9,7 @@ import ( "github.com/openshift/assisted-service/api/hiveextension/v1beta1" "github.com/openshift/installer/pkg/asset" + "github.com/openshift/installer/pkg/asset/agent" "github.com/openshift/installer/pkg/asset/agent/joiner" "github.com/openshift/installer/pkg/asset/agent/manifests" "github.com/openshift/installer/pkg/asset/agent/workflow" @@ -54,7 +55,7 @@ func TestKargs_Generate(t *testing.T) { }, Spec: v1beta1.AgentClusterInstallSpec{ ExternalPlatformSpec: &v1beta1.ExternalPlatformSpec{ - PlatformName: "oci", + PlatformName: agent.ExternalPlatformNameOci, }, }, }, diff --git a/pkg/asset/agent/installconfig.go b/pkg/asset/agent/installconfig.go index e25fd07002e..daef6b824af 100644 --- a/pkg/asset/agent/installconfig.go +++ b/pkg/asset/agent/installconfig.go @@ -10,7 +10,6 @@ import ( "k8s.io/apimachinery/pkg/util/validation/field" configv1 "github.com/openshift/api/config/v1" - "github.com/openshift/assisted-service/models" "github.com/openshift/installer/pkg/asset" "github.com/openshift/installer/pkg/asset/installconfig" "github.com/openshift/installer/pkg/asset/releaseimage" @@ -133,10 +132,12 @@ func (a *OptionalInstallConfig) validatePlatformsByName(installConfig *types.Ins var allErrs field.ErrorList if installConfig.Platform.Name() == external.Name { - if installConfig.Platform.External.PlatformName == string(models.PlatformTypeOci) && + if installConfig.Platform.External.PlatformName == ExternalPlatformNameOci && installConfig.Platform.External.CloudControllerManager != external.CloudControllerManagerTypeExternal { fieldPath := field.NewPath("Platform", "External", "CloudControllerManager") - allErrs = append(allErrs, field.Invalid(fieldPath, installConfig.Platform.External.CloudControllerManager, fmt.Sprintf("When using external %s platform, %s must be set to %s", string(models.PlatformTypeOci), fieldPath, external.CloudControllerManagerTypeExternal))) + allErrs = append(allErrs, field.Invalid(fieldPath, installConfig.Platform.External.CloudControllerManager, + fmt.Sprintf("When using external %s platform, %s must be set to %s", + ExternalPlatformNameOci, fieldPath, external.CloudControllerManagerTypeExternal))) } } diff --git a/pkg/asset/agent/manifests/agentclusterinstall_test.go b/pkg/asset/agent/manifests/agentclusterinstall_test.go index 0ee91dcacb6..e1d4ed39b9a 100644 --- a/pkg/asset/agent/manifests/agentclusterinstall_test.go +++ b/pkg/asset/agent/manifests/agentclusterinstall_test.go @@ -14,7 +14,6 @@ import ( configv1 "github.com/openshift/api/config/v1" hiveext "github.com/openshift/assisted-service/api/hiveextension/v1beta1" - "github.com/openshift/assisted-service/models" hivev1 "github.com/openshift/hive/apis/hive/v1" "github.com/openshift/installer/pkg/asset" "github.com/openshift/installer/pkg/asset/agent" @@ -104,7 +103,7 @@ func TestAgentClusterInstall_Generate(t *testing.T) { installConfigWExternalOCIPlatform := getValidOptionalInstallConfig() installConfigWExternalOCIPlatform.Config.Platform = types.Platform{ External: &externaltype.Platform{ - PlatformName: string(models.PlatformTypeOci), + PlatformName: agent.ExternalPlatformNameOci, CloudControllerManager: externaltype.CloudControllerManagerTypeExternal, }, } @@ -118,7 +117,7 @@ func TestAgentClusterInstall_Generate(t *testing.T) { goodExternalOCIPlatformACI.Spec.Networking.UserManagedNetworking = &val goodExternalOCIPlatformACI.Spec.PlatformType = hiveext.ExternalPlatformType goodExternalOCIPlatformACI.Spec.ExternalPlatformSpec = &hiveext.ExternalPlatformSpec{ - PlatformName: string(models.PlatformTypeOci), + PlatformName: agent.ExternalPlatformNameOci, } goodExternalOCIPlatformACI.SetAnnotations(map[string]string{ installConfigOverrides: `{"platform":{"external":{"platformName":"oci","cloudControllerManager":"External"}}}`, @@ -405,7 +404,7 @@ spec: IngressVIP: "192.168.111.4", PlatformType: hiveext.ExternalPlatformType, ExternalPlatformSpec: &hiveext.ExternalPlatformSpec{ - PlatformName: string(models.PlatformTypeOci), + PlatformName: agent.ExternalPlatformNameOci, }, ClusterDeploymentRef: corev1.LocalObjectReference{ Name: "ostest", diff --git a/pkg/asset/imagebased/configimage/clusterconfiguration.go b/pkg/asset/imagebased/configimage/clusterconfiguration.go index e4ea3075427..b54581dbb94 100644 --- a/pkg/asset/imagebased/configimage/clusterconfiguration.go +++ b/pkg/asset/imagebased/configimage/clusterconfiguration.go @@ -90,6 +90,12 @@ func (cc *ClusterConfiguration) Generate(_ context.Context, dependencies asset.P if installConfig.Config == nil || imageBasedConfig.Config == nil { return cc.finish() } + if imageBasedConfig.Config.ClusterID != "" { + clusterID.UUID = imageBasedConfig.Config.ClusterID + } + if imageBasedConfig.Config.InfraID != "" { + clusterID.InfraID = imageBasedConfig.Config.InfraID + } cc.Config = &imagebased.SeedReconfiguration{ APIVersion: imagebased.SeedReconfigurationVersion, @@ -101,11 +107,14 @@ func (cc *ClusterConfiguration) Generate(_ context.Context, dependencies asset.P KubeadminPasswordHash: pwdHash, Proxy: installConfig.Config.Proxy, PullSecret: installConfig.Config.PullSecret, - RawNMStateConfig: imageBasedConfig.Config.NetworkConfig.String(), ReleaseRegistry: imageBasedConfig.Config.ReleaseRegistry, SSHKey: installConfig.Config.SSHKey, } + if imageBasedConfig.Config.NetworkConfig != nil { + cc.Config.RawNMStateConfig = imageBasedConfig.Config.NetworkConfig.String() + } + if len(imageBasedConfig.Config.AdditionalNTPSources) > 0 { cc.Config.ChronyConfig = chronyConfWithAdditionalNTPSources(imageBasedConfig.Config.AdditionalNTPSources) } @@ -139,7 +148,9 @@ func (cc *ClusterConfiguration) Generate(_ context.Context, dependencies asset.P } // validation for the length of the MachineNetwork is performed in the InstallConfig - cc.Config.MachineNetwork = installConfig.Config.Networking.MachineNetwork[0].CIDR.String() + if len(installConfig.Config.Networking.MachineNetwork) > 0 { + cc.Config.MachineNetwork = installConfig.Config.Networking.MachineNetwork[0].CIDR.String() + } clusterConfigurationData, err := json.Marshal(cc.Config) if err != nil { diff --git a/pkg/asset/imagebased/configimage/clusterconfiguration_test.go b/pkg/asset/imagebased/configimage/clusterconfiguration_test.go index 4fa84bee18a..678721270d7 100644 --- a/pkg/asset/imagebased/configimage/clusterconfiguration_test.go +++ b/pkg/asset/imagebased/configimage/clusterconfiguration_test.go @@ -64,6 +64,22 @@ func TestClusterConfiguration_Generate(t *testing.T) { expectedConfig: clusterConfiguration().build().Config, }, + { + name: "valid configuration with cluster and infra ID", + dependencies: []asset.Asset{ + clusterID(), + kubeadminPassword(), + lbCertKey(), + localhostCertKey(), + serviceNetworkCertKey(), + adminKubeConfigCertKey(), + ingressCertKey(), + installConfig().build(), + imageBasedConfigWithSetClusterID("some-id", "some-infra-id"), + }, + + expectedConfig: clusterConfigurationWithProvidedIDs("some-id", "some-infra-id").build().Config, + }, { name: "valid configuration with proxy", dependencies: []asset.Asset{ @@ -340,6 +356,13 @@ func clusterConfiguration() *ClusterConfigurationBuilder { return ccb } +func clusterConfigurationWithProvidedIDs(clusterID string, infraID string) *ClusterConfigurationBuilder { + ccb := clusterConfiguration() + ccb.ClusterConfiguration.Config.ClusterID = clusterID + ccb.ClusterConfiguration.Config.InfraID = infraID + return ccb +} + func clusterID() *ClusterID { return &ClusterID{ installconfig.ClusterID{ diff --git a/pkg/asset/imagebased/configimage/imagebased_config.go b/pkg/asset/imagebased/configimage/imagebased_config.go index df348920cf5..bf8182317ae 100644 --- a/pkg/asset/imagebased/configimage/imagebased_config.go +++ b/pkg/asset/imagebased/configimage/imagebased_config.go @@ -155,7 +155,7 @@ func (i *ImageBasedConfig) validateNetworkConfig() field.ErrorList { var allErrs field.ErrorList // empty NetworkConfig is fine - if i.Config.NetworkConfig.String() == "" { + if i.Config.NetworkConfig == nil || i.Config.NetworkConfig.String() == "" { return nil } diff --git a/pkg/asset/imagebased/configimage/installconfig.go b/pkg/asset/imagebased/configimage/installconfig.go index f1ae87641e9..7b055dfe6bf 100644 --- a/pkg/asset/imagebased/configimage/installconfig.go +++ b/pkg/asset/imagebased/configimage/installconfig.go @@ -2,15 +2,21 @@ package configimage import ( "context" + "encoding/json" "fmt" + "os" + "strings" "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/util/validation/field" + "sigs.k8s.io/yaml" configv1 "github.com/openshift/api/config/v1" "github.com/openshift/installer/pkg/asset" "github.com/openshift/installer/pkg/asset/installconfig" "github.com/openshift/installer/pkg/types" + "github.com/openshift/installer/pkg/types/conversion" + "github.com/openshift/installer/pkg/types/defaults" "github.com/openshift/installer/pkg/types/none" "github.com/openshift/installer/pkg/types/validation" ) @@ -43,11 +49,50 @@ func (i *InstallConfig) Generate(_ context.Context, parents asset.Parents) error return nil } +// loadFromFile method to load the install-config.yaml file. +// Default one adds many default values that are not needed for image-based install config and can break our logic such as machine network for example. +func (i *InstallConfig) loadFromFile(f asset.FileFetcher) (found bool, err error) { + file, err := f.FetchByName(InstallConfigFilename) + if err != nil { + if os.IsNotExist(err) { + return false, nil + } + return false, fmt.Errorf("%s, err: %w", asset.InstallConfigError, err) + } + + config := &types.InstallConfig{} + if err := yaml.UnmarshalStrict(file.Data, config, yaml.DisallowUnknownFields); err != nil { + err = fmt.Errorf("failed to unmarshal %s, err: %w", InstallConfigFilename, err) + if !strings.Contains(err.Error(), "unknown field") { + return false, fmt.Errorf("%s, err: %w", asset.InstallConfigError, err) + } + err = fmt.Errorf("failed to parse first occurrence of unknown field, err: %w", err) + logrus.Warnf(err.Error()) + logrus.Info("Attempting to unmarshal while ignoring unknown keys because strict unmarshaling failed") + if err = yaml.Unmarshal(file.Data, config); err != nil { + err = fmt.Errorf("failed to unmarshal %s, err: %w", InstallConfigFilename, err) + return false, fmt.Errorf("%s, err: %w", asset.InstallConfigError, err) + } + } + i.Config = config + + // Upconvert any deprecated fields + if err := conversion.ConvertInstallConfig(i.Config); err != nil { + return false, fmt.Errorf("%s, failed to upconvert install config: %w", asset.InstallConfigError, err) + } + + return true, nil +} + // Load returns the installconfig from disk. func (i *InstallConfig) Load(f asset.FileFetcher) (bool, error) { - found, err := i.LoadFromFile(f) + found, err := i.loadFromFile(f) if found && err == nil { - if err := i.validateInstallConfig(i.Config).ToAggregate(); err != nil { + installConfig := &types.InstallConfig{} + if err := deepCopy(i.Config, installConfig); err != nil { + return false, fmt.Errorf("invalid install-config configuration: %w", err) + } + if err := i.validateInstallConfig(installConfig).ToAggregate(); err != nil { return false, fmt.Errorf("invalid install-config configuration: %w", err) } if err := i.RecordFile(); err != nil { @@ -57,9 +102,13 @@ func (i *InstallConfig) Load(f asset.FileFetcher) (bool, error) { return found, err } +// in order to avoid the validation errors, we need to set the defaults and validate the configuration +// though those defaults are not used in the image-based install config. func (i *InstallConfig) validateInstallConfig(installConfig *types.InstallConfig) field.ErrorList { var allErrs field.ErrorList - if err := validation.ValidateInstallConfig(i.Config, true); err != nil { + + defaults.SetInstallConfigDefaults(installConfig) + if err := validation.ValidateInstallConfig(installConfig, true); err != nil { allErrs = append(allErrs, err...) } @@ -76,7 +125,6 @@ func (i *InstallConfig) validateInstallConfig(installConfig *types.InstallConfig if err := i.validateSNOConfiguration(installConfig); err != nil { allErrs = append(allErrs, err...) } - return allErrs } @@ -117,7 +165,7 @@ func (i *InstallConfig) validateSNOConfiguration(installConfig *types.InstallCon } machineNetworksCount := len(installConfig.Networking.MachineNetwork) - if machineNetworksCount != 1 { + if machineNetworksCount > 1 { fieldPath = field.NewPath("Networking", "MachineNetwork") allErrs = append(allErrs, field.TooMany(fieldPath, machineNetworksCount, 1)) } @@ -171,3 +219,11 @@ func warnUnusedConfig(installConfig *types.InstallConfig) { logrus.Warnf(fmt.Sprintf("%s is ignored", fieldPath)) } } + +func deepCopy(src, dst interface{}) error { + bytes, err := json.Marshal(src) + if err != nil { + return err + } + return json.Unmarshal(bytes, dst) +} diff --git a/pkg/asset/imagebased/configimage/installconfig_test.go b/pkg/asset/imagebased/configimage/installconfig_test.go index 8e03c634c15..5f2f16d0638 100644 --- a/pkg/asset/imagebased/configimage/installconfig_test.go +++ b/pkg/asset/imagebased/configimage/installconfig_test.go @@ -167,19 +167,68 @@ pullSecret: "{\"auths\":{\"example.com\":{\"auth\":\"c3VwZXItc2VjcmV0Cg==\"}}}" ObjectMeta: metav1.ObjectMeta{ Name: "test-cluster", }, - AdditionalTrustBundlePolicy: types.PolicyProxyOnly, - BaseDomain: "test-domain", + BaseDomain: "test-domain", Networking: &types.Networking{ - MachineNetwork: []types.MachineNetworkEntry{ - {CIDR: *ipnet.MustParseCIDR("10.0.0.0/16")}, + NetworkType: "OVNKubernetes", + }, + ControlPlane: &types.MachinePool{ + Name: "master", + Replicas: ptr.To[int64](1), + Hyperthreading: types.HyperthreadingEnabled, + Architecture: types.ArchitectureAMD64, + }, + Compute: []types.MachinePool{ + { + Name: "worker", + Replicas: ptr.To[int64](0), + Hyperthreading: types.HyperthreadingEnabled, + Architecture: types.ArchitectureAMD64, }, - NetworkType: "OVNKubernetes", - ServiceNetwork: []ipnet.IPNet{*ipnet.MustParseCIDR("172.30.0.0/16")}, - ClusterNetwork: []types.ClusterNetworkEntry{ - { - CIDR: *ipnet.MustParseCIDR("10.128.0.0/14"), - HostPrefix: 23, - }, + }, + Platform: types.Platform{None: &none.Platform{}}, + PullSecret: `{"auths":{"example.com":{"auth":"c3VwZXItc2VjcmV0Cg=="}}}`, + }, + }, + { + name: "valid configuration for none platform for sno with machine network", + data: ` +apiVersion: v1 +metadata: + name: test-cluster +baseDomain: test-domain +networking: + networkType: OVNKubernetes + machineNetwork: + - cidr: 10.0.0.0/24 +compute: + - architecture: amd64 + hyperthreading: Enabled + name: worker + platform: {} + replicas: 0 +controlPlane: + architecture: amd64 + hyperthreading: Enabled + name: master + platform: {} + replicas: 1 +platform: + none : {} +pullSecret: "{\"auths\":{\"example.com\":{\"auth\":\"c3VwZXItc2VjcmV0Cg==\"}}}" +`, + expectedFound: true, + expectedConfig: &types.InstallConfig{ + TypeMeta: metav1.TypeMeta{ + APIVersion: types.InstallConfigVersion, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-cluster", + }, + BaseDomain: "test-domain", + Networking: &types.Networking{ + NetworkType: "OVNKubernetes", + MachineNetwork: []types.MachineNetworkEntry{ + {CIDR: *ipnet.MustParseCIDR("10.0.0.0/24")}, }, }, ControlPlane: &types.MachinePool{ @@ -198,7 +247,6 @@ pullSecret: "{\"auths\":{\"example.com\":{\"auth\":\"c3VwZXItc2VjcmV0Cg==\"}}}" }, Platform: types.Platform{None: &none.Platform{}}, PullSecret: `{"auths":{"example.com":{"auth":"c3VwZXItc2VjcmV0Cg=="}}}`, - Publish: types.ExternalPublishingStrategy, }, }, } diff --git a/pkg/asset/imagebased/configimage/util_test.go b/pkg/asset/imagebased/configimage/util_test.go index 9a5b201a4a6..9f1acaf7f23 100644 --- a/pkg/asset/imagebased/configimage/util_test.go +++ b/pkg/asset/imagebased/configimage/util_test.go @@ -103,7 +103,7 @@ func imageBasedConfig() *ImageBasedConfig { }, Hostname: "somehostname", ReleaseRegistry: "quay.io", - NetworkConfig: aiv1beta1.NetConfig{ + NetworkConfig: &aiv1beta1.NetConfig{ Raw: unmarshalJSON([]byte(rawNMStateConfig)), }, }, @@ -111,6 +111,13 @@ func imageBasedConfig() *ImageBasedConfig { return ibConfig } +func imageBasedConfigWithSetClusterID(clusterID string, infraID string) *ImageBasedConfig { + ibConfig := imageBasedConfig() + ibConfig.Config.ClusterID = clusterID + ibConfig.Config.InfraID = infraID + return ibConfig +} + func unmarshalJSON(b []byte) []byte { output, err := yaml.JSONToYAML(b) if err != nil { diff --git a/pkg/asset/imagebased/image/ignition.go b/pkg/asset/imagebased/image/ignition.go index b8f8840b97d..67cee4bb31e 100644 --- a/pkg/asset/imagebased/image/ignition.go +++ b/pkg/asset/imagebased/image/ignition.go @@ -110,7 +110,10 @@ func (i *Ignition) Generate(_ context.Context, dependencies asset.Parents) error Proxy: ibiConfig.Proxy, PullSecret: ibiConfig.PullSecret, IBIConfiguration: string(ibiConfigJSON), - NetworkConfig: ibiConfig.NetworkConfig.String(), + } + + if ibiConfig.NetworkConfig != nil { + ibiTemplateData.NetworkConfig = ibiConfig.NetworkConfig.String() } if len(registriesConf.Data) > 0 { @@ -139,7 +142,7 @@ func (i *Ignition) Generate(_ context.Context, dependencies asset.Parents) error } enabledServices := defaultEnabledServices() - if ibiConfig.NetworkConfig.String() != "" { + if ibiConfig.NetworkConfig != nil && ibiConfig.NetworkConfig.String() != "" { enabledServices = append(enabledServices, "network-config.service") } if err := bootstrap.AddSystemdUnits(config, "imagebased/systemd/units", ibiTemplateData, enabledServices); err != nil { diff --git a/pkg/asset/imagebased/image/ignition_test.go b/pkg/asset/imagebased/image/ignition_test.go index c3309296f91..54d87bf5681 100644 --- a/pkg/asset/imagebased/image/ignition_test.go +++ b/pkg/asset/imagebased/image/ignition_test.go @@ -85,7 +85,7 @@ func TestIgnition_Generate(t *testing.T) { }, }). additionalTrustBundle(testCert). - networkConfig(aiv1beta1.NetConfig{Raw: []byte(testNetworkConfig)}). + networkConfig(&aiv1beta1.NetConfig{Raw: []byte(testNetworkConfig)}). ignitionConfigOverride(ignitionConfigOverride). build(), }, diff --git a/pkg/asset/imagebased/image/imagebased_config.go b/pkg/asset/imagebased/image/imagebased_config.go index 7e02b3425d5..687955d0bab 100644 --- a/pkg/asset/imagebased/image/imagebased_config.go +++ b/pkg/asset/imagebased/image/imagebased_config.go @@ -272,7 +272,7 @@ func (i *ImageBasedInstallationConfig) validateNetworkConfig() field.ErrorList { var allErrs field.ErrorList // empty NetworkConfig is fine - if i.Config.NetworkConfig.String() == "" { + if i.Config.NetworkConfig == nil || i.Config.NetworkConfig.String() == "" { return nil } diff --git a/pkg/asset/imagebased/image/imagebased_config_test.go b/pkg/asset/imagebased/image/imagebased_config_test.go index d5c39d778d0..3bbac6f83e5 100644 --- a/pkg/asset/imagebased/image/imagebased_config_test.go +++ b/pkg/asset/imagebased/image/imagebased_config_test.go @@ -217,6 +217,21 @@ networkConfig: expectedFound: false, expectedError: "networkConfig: Invalid value: invalid: config\n: failed to execute 'nmstatectl gc', error: InvalidArgument: Invalid YAML string: unknown field `invalid`", }, + { + name: "empty networkConfig", + data: ` +apiVersion: v1beta1 +metadata: + name: image-based-installation-config +pullSecret: "{\"auths\":{\"example.com\":{\"auth\":\"c3VwZXItc2VjcmV0Cg==\"}}}" +seedVersion: 4.16.0 +seedImage: quay.io/openshift-kni/seed-image:4.16.0 +installationDisk: /dev/vda +networkConfig: null +`, + expectedFound: true, + expectedError: "", + }, { name: "invalid-imageDigestSources", data: ` @@ -347,7 +362,7 @@ func ibiConfig() *ImageBasedInstallationConfigBuilder { Shutdown: false, SSHKey: "", PullSecret: "{\"auths\":{\"example.com\":{\"auth\":\"c3VwZXItc2VjcmV0Cg==\"}}}", - NetworkConfig: aiv1beta1.NetConfig{ + NetworkConfig: &aiv1beta1.NetConfig{ Raw: unmarshalJSON([]byte(rawNMStateConfig)), }, }, @@ -363,7 +378,7 @@ func (icb *ImageBasedInstallationConfigBuilder) additionalTrustBundle(atb string return icb } -func (icb *ImageBasedInstallationConfigBuilder) networkConfig(nc aiv1beta1.NetConfig) *ImageBasedInstallationConfigBuilder { +func (icb *ImageBasedInstallationConfigBuilder) networkConfig(nc *aiv1beta1.NetConfig) *ImageBasedInstallationConfigBuilder { icb.InstallationConfig.NetworkConfig = nc return icb } diff --git a/pkg/types/imagebased/imagebased_config_types.go b/pkg/types/imagebased/imagebased_config_types.go index e1751c27b69..64cedcd1129 100644 --- a/pkg/types/imagebased/imagebased_config_types.go +++ b/pkg/types/imagebased/imagebased_config_types.go @@ -28,11 +28,21 @@ type Config struct { // NetworkConfig is a YAML manifest that can be processed by nmstate, using custom // marshaling/unmarshaling that will allow to populate nmstate config as plain yaml. // +optional - NetworkConfig aiv1beta1.NetConfig `json:"networkConfig,omitempty"` + NetworkConfig *aiv1beta1.NetConfig `json:"networkConfig,omitempty"` // ReleaseRegistry is the container registry used to host the release image of the seed cluster. // +optional ReleaseRegistry string `json:"releaseRegistry,omitempty"` + + // ClusterID is the desired cluster ID. + // Will be generated by the installer in case it is not provided. + // +optional + ClusterID string `json:"cluster_id,omitempty"` + + // InfraID is the desired infra ID. + // Will be generated by the installer in case it is not provided. + // +optional + InfraID string `json:"infra_id,omitempty"` } // InstallationConfig is the API for specifying configuration for the @@ -84,7 +94,7 @@ type InstallationConfig struct { // custom marshaling/unmarshaling that will allow to populate nmstate config // as plain yaml. // +optional - NetworkConfig aiv1beta1.NetConfig `json:"networkConfig,omitempty"` + NetworkConfig *aiv1beta1.NetConfig `json:"networkConfig,omitempty"` // Proxy defines the proxy settings for the cluster. // If unset, the cluster will not be configured to use a proxy. diff --git a/vendor/github.com/lib/pq/.gitignore b/vendor/github.com/lib/pq/.gitignore new file mode 100644 index 00000000000..3243952a4d6 --- /dev/null +++ b/vendor/github.com/lib/pq/.gitignore @@ -0,0 +1,6 @@ +.db +*.test +*~ +*.swp +.idea +.vscode \ No newline at end of file diff --git a/vendor/github.com/lib/pq/LICENSE.md b/vendor/github.com/lib/pq/LICENSE.md new file mode 100644 index 00000000000..5773904a30e --- /dev/null +++ b/vendor/github.com/lib/pq/LICENSE.md @@ -0,0 +1,8 @@ +Copyright (c) 2011-2013, 'pq' Contributors +Portions Copyright (C) 2011 Blake Mizerany + +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/lib/pq/README.md b/vendor/github.com/lib/pq/README.md new file mode 100644 index 00000000000..126ee5d35d1 --- /dev/null +++ b/vendor/github.com/lib/pq/README.md @@ -0,0 +1,36 @@ +# pq - A pure Go postgres driver for Go's database/sql package + +[![GoDoc](https://godoc.org/github.com/lib/pq?status.svg)](https://pkg.go.dev/github.com/lib/pq?tab=doc) + +## Install + + go get github.com/lib/pq + +## Features + +* SSL +* Handles bad connections for `database/sql` +* Scan `time.Time` correctly (i.e. `timestamp[tz]`, `time[tz]`, `date`) +* Scan binary blobs correctly (i.e. `bytea`) +* Package for `hstore` support +* COPY FROM support +* pq.ParseURL for converting urls to connection strings for sql.Open. +* Many libpq compatible environment variables +* Unix socket support +* Notifications: `LISTEN`/`NOTIFY` +* pgpass support +* GSS (Kerberos) auth + +## Tests + +`go test` is used for testing. See [TESTS.md](TESTS.md) for more details. + +## Status + +This package is currently in maintenance mode, which means: +1. It generally does not accept new features. +2. It does accept bug fixes and version compatability changes provided by the community. +3. Maintainers usually do not resolve reported issues. +4. Community members are encouraged to help each other with reported issues. + +For users that require new features or reliable resolution of reported bugs, we recommend using [pgx](https://github.com/jackc/pgx) which is under active development. diff --git a/vendor/github.com/lib/pq/TESTS.md b/vendor/github.com/lib/pq/TESTS.md new file mode 100644 index 00000000000..f05021115be --- /dev/null +++ b/vendor/github.com/lib/pq/TESTS.md @@ -0,0 +1,33 @@ +# Tests + +## Running Tests + +`go test` is used for testing. A running PostgreSQL +server is required, with the ability to log in. The +database to connect to test with is "pqgotest," on +"localhost" but these can be overridden using [environment +variables](https://www.postgresql.org/docs/9.3/static/libpq-envars.html). + +Example: + + PGHOST=/run/postgresql go test + +## Benchmarks + +A benchmark suite can be run as part of the tests: + + go test -bench . + +## Example setup (Docker) + +Run a postgres container: + +``` +docker run --expose 5432:5432 postgres +``` + +Run tests: + +``` +PGHOST=localhost PGPORT=5432 PGUSER=postgres PGSSLMODE=disable PGDATABASE=postgres go test +``` diff --git a/vendor/github.com/lib/pq/array.go b/vendor/github.com/lib/pq/array.go new file mode 100644 index 00000000000..39c8f7e2e03 --- /dev/null +++ b/vendor/github.com/lib/pq/array.go @@ -0,0 +1,895 @@ +package pq + +import ( + "bytes" + "database/sql" + "database/sql/driver" + "encoding/hex" + "fmt" + "reflect" + "strconv" + "strings" +) + +var typeByteSlice = reflect.TypeOf([]byte{}) +var typeDriverValuer = reflect.TypeOf((*driver.Valuer)(nil)).Elem() +var typeSQLScanner = reflect.TypeOf((*sql.Scanner)(nil)).Elem() + +// Array returns the optimal driver.Valuer and sql.Scanner for an array or +// slice of any dimension. +// +// For example: +// db.Query(`SELECT * FROM t WHERE id = ANY($1)`, pq.Array([]int{235, 401})) +// +// var x []sql.NullInt64 +// db.QueryRow(`SELECT ARRAY[235, 401]`).Scan(pq.Array(&x)) +// +// Scanning multi-dimensional arrays is not supported. Arrays where the lower +// bound is not one (such as `[0:0]={1}') are not supported. +func Array(a interface{}) interface { + driver.Valuer + sql.Scanner +} { + switch a := a.(type) { + case []bool: + return (*BoolArray)(&a) + case []float64: + return (*Float64Array)(&a) + case []float32: + return (*Float32Array)(&a) + case []int64: + return (*Int64Array)(&a) + case []int32: + return (*Int32Array)(&a) + case []string: + return (*StringArray)(&a) + case [][]byte: + return (*ByteaArray)(&a) + + case *[]bool: + return (*BoolArray)(a) + case *[]float64: + return (*Float64Array)(a) + case *[]float32: + return (*Float32Array)(a) + case *[]int64: + return (*Int64Array)(a) + case *[]int32: + return (*Int32Array)(a) + case *[]string: + return (*StringArray)(a) + case *[][]byte: + return (*ByteaArray)(a) + } + + return GenericArray{a} +} + +// ArrayDelimiter may be optionally implemented by driver.Valuer or sql.Scanner +// to override the array delimiter used by GenericArray. +type ArrayDelimiter interface { + // ArrayDelimiter returns the delimiter character(s) for this element's type. + ArrayDelimiter() string +} + +// BoolArray represents a one-dimensional array of the PostgreSQL boolean type. +type BoolArray []bool + +// Scan implements the sql.Scanner interface. +func (a *BoolArray) Scan(src interface{}) error { + switch src := src.(type) { + case []byte: + return a.scanBytes(src) + case string: + return a.scanBytes([]byte(src)) + case nil: + *a = nil + return nil + } + + return fmt.Errorf("pq: cannot convert %T to BoolArray", src) +} + +func (a *BoolArray) scanBytes(src []byte) error { + elems, err := scanLinearArray(src, []byte{','}, "BoolArray") + if err != nil { + return err + } + if *a != nil && len(elems) == 0 { + *a = (*a)[:0] + } else { + b := make(BoolArray, len(elems)) + for i, v := range elems { + if len(v) != 1 { + return fmt.Errorf("pq: could not parse boolean array index %d: invalid boolean %q", i, v) + } + switch v[0] { + case 't': + b[i] = true + case 'f': + b[i] = false + default: + return fmt.Errorf("pq: could not parse boolean array index %d: invalid boolean %q", i, v) + } + } + *a = b + } + return nil +} + +// Value implements the driver.Valuer interface. +func (a BoolArray) Value() (driver.Value, error) { + if a == nil { + return nil, nil + } + + if n := len(a); n > 0 { + // There will be exactly two curly brackets, N bytes of values, + // and N-1 bytes of delimiters. + b := make([]byte, 1+2*n) + + for i := 0; i < n; i++ { + b[2*i] = ',' + if a[i] { + b[1+2*i] = 't' + } else { + b[1+2*i] = 'f' + } + } + + b[0] = '{' + b[2*n] = '}' + + return string(b), nil + } + + return "{}", nil +} + +// ByteaArray represents a one-dimensional array of the PostgreSQL bytea type. +type ByteaArray [][]byte + +// Scan implements the sql.Scanner interface. +func (a *ByteaArray) Scan(src interface{}) error { + switch src := src.(type) { + case []byte: + return a.scanBytes(src) + case string: + return a.scanBytes([]byte(src)) + case nil: + *a = nil + return nil + } + + return fmt.Errorf("pq: cannot convert %T to ByteaArray", src) +} + +func (a *ByteaArray) scanBytes(src []byte) error { + elems, err := scanLinearArray(src, []byte{','}, "ByteaArray") + if err != nil { + return err + } + if *a != nil && len(elems) == 0 { + *a = (*a)[:0] + } else { + b := make(ByteaArray, len(elems)) + for i, v := range elems { + b[i], err = parseBytea(v) + if err != nil { + return fmt.Errorf("could not parse bytea array index %d: %s", i, err.Error()) + } + } + *a = b + } + return nil +} + +// Value implements the driver.Valuer interface. It uses the "hex" format which +// is only supported on PostgreSQL 9.0 or newer. +func (a ByteaArray) Value() (driver.Value, error) { + if a == nil { + return nil, nil + } + + if n := len(a); n > 0 { + // There will be at least two curly brackets, 2*N bytes of quotes, + // 3*N bytes of hex formatting, and N-1 bytes of delimiters. + size := 1 + 6*n + for _, x := range a { + size += hex.EncodedLen(len(x)) + } + + b := make([]byte, size) + + for i, s := 0, b; i < n; i++ { + o := copy(s, `,"\\x`) + o += hex.Encode(s[o:], a[i]) + s[o] = '"' + s = s[o+1:] + } + + b[0] = '{' + b[size-1] = '}' + + return string(b), nil + } + + return "{}", nil +} + +// Float64Array represents a one-dimensional array of the PostgreSQL double +// precision type. +type Float64Array []float64 + +// Scan implements the sql.Scanner interface. +func (a *Float64Array) Scan(src interface{}) error { + switch src := src.(type) { + case []byte: + return a.scanBytes(src) + case string: + return a.scanBytes([]byte(src)) + case nil: + *a = nil + return nil + } + + return fmt.Errorf("pq: cannot convert %T to Float64Array", src) +} + +func (a *Float64Array) scanBytes(src []byte) error { + elems, err := scanLinearArray(src, []byte{','}, "Float64Array") + if err != nil { + return err + } + if *a != nil && len(elems) == 0 { + *a = (*a)[:0] + } else { + b := make(Float64Array, len(elems)) + for i, v := range elems { + if b[i], err = strconv.ParseFloat(string(v), 64); err != nil { + return fmt.Errorf("pq: parsing array element index %d: %v", i, err) + } + } + *a = b + } + return nil +} + +// Value implements the driver.Valuer interface. +func (a Float64Array) Value() (driver.Value, error) { + if a == nil { + return nil, nil + } + + if n := len(a); n > 0 { + // There will be at least two curly brackets, N bytes of values, + // and N-1 bytes of delimiters. + b := make([]byte, 1, 1+2*n) + b[0] = '{' + + b = strconv.AppendFloat(b, a[0], 'f', -1, 64) + for i := 1; i < n; i++ { + b = append(b, ',') + b = strconv.AppendFloat(b, a[i], 'f', -1, 64) + } + + return string(append(b, '}')), nil + } + + return "{}", nil +} + +// Float32Array represents a one-dimensional array of the PostgreSQL double +// precision type. +type Float32Array []float32 + +// Scan implements the sql.Scanner interface. +func (a *Float32Array) Scan(src interface{}) error { + switch src := src.(type) { + case []byte: + return a.scanBytes(src) + case string: + return a.scanBytes([]byte(src)) + case nil: + *a = nil + return nil + } + + return fmt.Errorf("pq: cannot convert %T to Float32Array", src) +} + +func (a *Float32Array) scanBytes(src []byte) error { + elems, err := scanLinearArray(src, []byte{','}, "Float32Array") + if err != nil { + return err + } + if *a != nil && len(elems) == 0 { + *a = (*a)[:0] + } else { + b := make(Float32Array, len(elems)) + for i, v := range elems { + var x float64 + if x, err = strconv.ParseFloat(string(v), 32); err != nil { + return fmt.Errorf("pq: parsing array element index %d: %v", i, err) + } + b[i] = float32(x) + } + *a = b + } + return nil +} + +// Value implements the driver.Valuer interface. +func (a Float32Array) Value() (driver.Value, error) { + if a == nil { + return nil, nil + } + + if n := len(a); n > 0 { + // There will be at least two curly brackets, N bytes of values, + // and N-1 bytes of delimiters. + b := make([]byte, 1, 1+2*n) + b[0] = '{' + + b = strconv.AppendFloat(b, float64(a[0]), 'f', -1, 32) + for i := 1; i < n; i++ { + b = append(b, ',') + b = strconv.AppendFloat(b, float64(a[i]), 'f', -1, 32) + } + + return string(append(b, '}')), nil + } + + return "{}", nil +} + +// GenericArray implements the driver.Valuer and sql.Scanner interfaces for +// an array or slice of any dimension. +type GenericArray struct{ A interface{} } + +func (GenericArray) evaluateDestination(rt reflect.Type) (reflect.Type, func([]byte, reflect.Value) error, string) { + var assign func([]byte, reflect.Value) error + var del = "," + + // TODO calculate the assign function for other types + // TODO repeat this section on the element type of arrays or slices (multidimensional) + { + if reflect.PtrTo(rt).Implements(typeSQLScanner) { + // dest is always addressable because it is an element of a slice. + assign = func(src []byte, dest reflect.Value) (err error) { + ss := dest.Addr().Interface().(sql.Scanner) + if src == nil { + err = ss.Scan(nil) + } else { + err = ss.Scan(src) + } + return + } + goto FoundType + } + + assign = func([]byte, reflect.Value) error { + return fmt.Errorf("pq: scanning to %s is not implemented; only sql.Scanner", rt) + } + } + +FoundType: + + if ad, ok := reflect.Zero(rt).Interface().(ArrayDelimiter); ok { + del = ad.ArrayDelimiter() + } + + return rt, assign, del +} + +// Scan implements the sql.Scanner interface. +func (a GenericArray) Scan(src interface{}) error { + dpv := reflect.ValueOf(a.A) + switch { + case dpv.Kind() != reflect.Ptr: + return fmt.Errorf("pq: destination %T is not a pointer to array or slice", a.A) + case dpv.IsNil(): + return fmt.Errorf("pq: destination %T is nil", a.A) + } + + dv := dpv.Elem() + switch dv.Kind() { + case reflect.Slice: + case reflect.Array: + default: + return fmt.Errorf("pq: destination %T is not a pointer to array or slice", a.A) + } + + switch src := src.(type) { + case []byte: + return a.scanBytes(src, dv) + case string: + return a.scanBytes([]byte(src), dv) + case nil: + if dv.Kind() == reflect.Slice { + dv.Set(reflect.Zero(dv.Type())) + return nil + } + } + + return fmt.Errorf("pq: cannot convert %T to %s", src, dv.Type()) +} + +func (a GenericArray) scanBytes(src []byte, dv reflect.Value) error { + dtype, assign, del := a.evaluateDestination(dv.Type().Elem()) + dims, elems, err := parseArray(src, []byte(del)) + if err != nil { + return err + } + + // TODO allow multidimensional + + if len(dims) > 1 { + return fmt.Errorf("pq: scanning from multidimensional ARRAY%s is not implemented", + strings.Replace(fmt.Sprint(dims), " ", "][", -1)) + } + + // Treat a zero-dimensional array like an array with a single dimension of zero. + if len(dims) == 0 { + dims = append(dims, 0) + } + + for i, rt := 0, dv.Type(); i < len(dims); i, rt = i+1, rt.Elem() { + switch rt.Kind() { + case reflect.Slice: + case reflect.Array: + if rt.Len() != dims[i] { + return fmt.Errorf("pq: cannot convert ARRAY%s to %s", + strings.Replace(fmt.Sprint(dims), " ", "][", -1), dv.Type()) + } + default: + // TODO handle multidimensional + } + } + + values := reflect.MakeSlice(reflect.SliceOf(dtype), len(elems), len(elems)) + for i, e := range elems { + if err := assign(e, values.Index(i)); err != nil { + return fmt.Errorf("pq: parsing array element index %d: %v", i, err) + } + } + + // TODO handle multidimensional + + switch dv.Kind() { + case reflect.Slice: + dv.Set(values.Slice(0, dims[0])) + case reflect.Array: + for i := 0; i < dims[0]; i++ { + dv.Index(i).Set(values.Index(i)) + } + } + + return nil +} + +// Value implements the driver.Valuer interface. +func (a GenericArray) Value() (driver.Value, error) { + if a.A == nil { + return nil, nil + } + + rv := reflect.ValueOf(a.A) + + switch rv.Kind() { + case reflect.Slice: + if rv.IsNil() { + return nil, nil + } + case reflect.Array: + default: + return nil, fmt.Errorf("pq: Unable to convert %T to array", a.A) + } + + if n := rv.Len(); n > 0 { + // There will be at least two curly brackets, N bytes of values, + // and N-1 bytes of delimiters. + b := make([]byte, 0, 1+2*n) + + b, _, err := appendArray(b, rv, n) + return string(b), err + } + + return "{}", nil +} + +// Int64Array represents a one-dimensional array of the PostgreSQL integer types. +type Int64Array []int64 + +// Scan implements the sql.Scanner interface. +func (a *Int64Array) Scan(src interface{}) error { + switch src := src.(type) { + case []byte: + return a.scanBytes(src) + case string: + return a.scanBytes([]byte(src)) + case nil: + *a = nil + return nil + } + + return fmt.Errorf("pq: cannot convert %T to Int64Array", src) +} + +func (a *Int64Array) scanBytes(src []byte) error { + elems, err := scanLinearArray(src, []byte{','}, "Int64Array") + if err != nil { + return err + } + if *a != nil && len(elems) == 0 { + *a = (*a)[:0] + } else { + b := make(Int64Array, len(elems)) + for i, v := range elems { + if b[i], err = strconv.ParseInt(string(v), 10, 64); err != nil { + return fmt.Errorf("pq: parsing array element index %d: %v", i, err) + } + } + *a = b + } + return nil +} + +// Value implements the driver.Valuer interface. +func (a Int64Array) Value() (driver.Value, error) { + if a == nil { + return nil, nil + } + + if n := len(a); n > 0 { + // There will be at least two curly brackets, N bytes of values, + // and N-1 bytes of delimiters. + b := make([]byte, 1, 1+2*n) + b[0] = '{' + + b = strconv.AppendInt(b, a[0], 10) + for i := 1; i < n; i++ { + b = append(b, ',') + b = strconv.AppendInt(b, a[i], 10) + } + + return string(append(b, '}')), nil + } + + return "{}", nil +} + +// Int32Array represents a one-dimensional array of the PostgreSQL integer types. +type Int32Array []int32 + +// Scan implements the sql.Scanner interface. +func (a *Int32Array) Scan(src interface{}) error { + switch src := src.(type) { + case []byte: + return a.scanBytes(src) + case string: + return a.scanBytes([]byte(src)) + case nil: + *a = nil + return nil + } + + return fmt.Errorf("pq: cannot convert %T to Int32Array", src) +} + +func (a *Int32Array) scanBytes(src []byte) error { + elems, err := scanLinearArray(src, []byte{','}, "Int32Array") + if err != nil { + return err + } + if *a != nil && len(elems) == 0 { + *a = (*a)[:0] + } else { + b := make(Int32Array, len(elems)) + for i, v := range elems { + x, err := strconv.ParseInt(string(v), 10, 32) + if err != nil { + return fmt.Errorf("pq: parsing array element index %d: %v", i, err) + } + b[i] = int32(x) + } + *a = b + } + return nil +} + +// Value implements the driver.Valuer interface. +func (a Int32Array) Value() (driver.Value, error) { + if a == nil { + return nil, nil + } + + if n := len(a); n > 0 { + // There will be at least two curly brackets, N bytes of values, + // and N-1 bytes of delimiters. + b := make([]byte, 1, 1+2*n) + b[0] = '{' + + b = strconv.AppendInt(b, int64(a[0]), 10) + for i := 1; i < n; i++ { + b = append(b, ',') + b = strconv.AppendInt(b, int64(a[i]), 10) + } + + return string(append(b, '}')), nil + } + + return "{}", nil +} + +// StringArray represents a one-dimensional array of the PostgreSQL character types. +type StringArray []string + +// Scan implements the sql.Scanner interface. +func (a *StringArray) Scan(src interface{}) error { + switch src := src.(type) { + case []byte: + return a.scanBytes(src) + case string: + return a.scanBytes([]byte(src)) + case nil: + *a = nil + return nil + } + + return fmt.Errorf("pq: cannot convert %T to StringArray", src) +} + +func (a *StringArray) scanBytes(src []byte) error { + elems, err := scanLinearArray(src, []byte{','}, "StringArray") + if err != nil { + return err + } + if *a != nil && len(elems) == 0 { + *a = (*a)[:0] + } else { + b := make(StringArray, len(elems)) + for i, v := range elems { + if b[i] = string(v); v == nil { + return fmt.Errorf("pq: parsing array element index %d: cannot convert nil to string", i) + } + } + *a = b + } + return nil +} + +// Value implements the driver.Valuer interface. +func (a StringArray) Value() (driver.Value, error) { + if a == nil { + return nil, nil + } + + if n := len(a); n > 0 { + // There will be at least two curly brackets, 2*N bytes of quotes, + // and N-1 bytes of delimiters. + b := make([]byte, 1, 1+3*n) + b[0] = '{' + + b = appendArrayQuotedBytes(b, []byte(a[0])) + for i := 1; i < n; i++ { + b = append(b, ',') + b = appendArrayQuotedBytes(b, []byte(a[i])) + } + + return string(append(b, '}')), nil + } + + return "{}", nil +} + +// appendArray appends rv to the buffer, returning the extended buffer and +// the delimiter used between elements. +// +// It panics when n <= 0 or rv's Kind is not reflect.Array nor reflect.Slice. +func appendArray(b []byte, rv reflect.Value, n int) ([]byte, string, error) { + var del string + var err error + + b = append(b, '{') + + if b, del, err = appendArrayElement(b, rv.Index(0)); err != nil { + return b, del, err + } + + for i := 1; i < n; i++ { + b = append(b, del...) + if b, del, err = appendArrayElement(b, rv.Index(i)); err != nil { + return b, del, err + } + } + + return append(b, '}'), del, nil +} + +// appendArrayElement appends rv to the buffer, returning the extended buffer +// and the delimiter to use before the next element. +// +// When rv's Kind is neither reflect.Array nor reflect.Slice, it is converted +// using driver.DefaultParameterConverter and the resulting []byte or string +// is double-quoted. +// +// See http://www.postgresql.org/docs/current/static/arrays.html#ARRAYS-IO +func appendArrayElement(b []byte, rv reflect.Value) ([]byte, string, error) { + if k := rv.Kind(); k == reflect.Array || k == reflect.Slice { + if t := rv.Type(); t != typeByteSlice && !t.Implements(typeDriverValuer) { + if n := rv.Len(); n > 0 { + return appendArray(b, rv, n) + } + + return b, "", nil + } + } + + var del = "," + var err error + var iv interface{} = rv.Interface() + + if ad, ok := iv.(ArrayDelimiter); ok { + del = ad.ArrayDelimiter() + } + + if iv, err = driver.DefaultParameterConverter.ConvertValue(iv); err != nil { + return b, del, err + } + + switch v := iv.(type) { + case nil: + return append(b, "NULL"...), del, nil + case []byte: + return appendArrayQuotedBytes(b, v), del, nil + case string: + return appendArrayQuotedBytes(b, []byte(v)), del, nil + } + + b, err = appendValue(b, iv) + return b, del, err +} + +func appendArrayQuotedBytes(b, v []byte) []byte { + b = append(b, '"') + for { + i := bytes.IndexAny(v, `"\`) + if i < 0 { + b = append(b, v...) + break + } + if i > 0 { + b = append(b, v[:i]...) + } + b = append(b, '\\', v[i]) + v = v[i+1:] + } + return append(b, '"') +} + +func appendValue(b []byte, v driver.Value) ([]byte, error) { + return append(b, encode(nil, v, 0)...), nil +} + +// parseArray extracts the dimensions and elements of an array represented in +// text format. Only representations emitted by the backend are supported. +// Notably, whitespace around brackets and delimiters is significant, and NULL +// is case-sensitive. +// +// See http://www.postgresql.org/docs/current/static/arrays.html#ARRAYS-IO +func parseArray(src, del []byte) (dims []int, elems [][]byte, err error) { + var depth, i int + + if len(src) < 1 || src[0] != '{' { + return nil, nil, fmt.Errorf("pq: unable to parse array; expected %q at offset %d", '{', 0) + } + +Open: + for i < len(src) { + switch src[i] { + case '{': + depth++ + i++ + case '}': + elems = make([][]byte, 0) + goto Close + default: + break Open + } + } + dims = make([]int, i) + +Element: + for i < len(src) { + switch src[i] { + case '{': + if depth == len(dims) { + break Element + } + depth++ + dims[depth-1] = 0 + i++ + case '"': + var elem = []byte{} + var escape bool + for i++; i < len(src); i++ { + if escape { + elem = append(elem, src[i]) + escape = false + } else { + switch src[i] { + default: + elem = append(elem, src[i]) + case '\\': + escape = true + case '"': + elems = append(elems, elem) + i++ + break Element + } + } + } + default: + for start := i; i < len(src); i++ { + if bytes.HasPrefix(src[i:], del) || src[i] == '}' { + elem := src[start:i] + if len(elem) == 0 { + return nil, nil, fmt.Errorf("pq: unable to parse array; unexpected %q at offset %d", src[i], i) + } + if bytes.Equal(elem, []byte("NULL")) { + elem = nil + } + elems = append(elems, elem) + break Element + } + } + } + } + + for i < len(src) { + if bytes.HasPrefix(src[i:], del) && depth > 0 { + dims[depth-1]++ + i += len(del) + goto Element + } else if src[i] == '}' && depth > 0 { + dims[depth-1]++ + depth-- + i++ + } else { + return nil, nil, fmt.Errorf("pq: unable to parse array; unexpected %q at offset %d", src[i], i) + } + } + +Close: + for i < len(src) { + if src[i] == '}' && depth > 0 { + depth-- + i++ + } else { + return nil, nil, fmt.Errorf("pq: unable to parse array; unexpected %q at offset %d", src[i], i) + } + } + if depth > 0 { + err = fmt.Errorf("pq: unable to parse array; expected %q at offset %d", '}', i) + } + if err == nil { + for _, d := range dims { + if (len(elems) % d) != 0 { + err = fmt.Errorf("pq: multidimensional arrays must have elements with matching dimensions") + } + } + } + return +} + +func scanLinearArray(src, del []byte, typ string) (elems [][]byte, err error) { + dims, elems, err := parseArray(src, del) + if err != nil { + return nil, err + } + if len(dims) > 1 { + return nil, fmt.Errorf("pq: cannot convert ARRAY%s to %s", strings.Replace(fmt.Sprint(dims), " ", "][", -1), typ) + } + return elems, err +} diff --git a/vendor/github.com/lib/pq/buf.go b/vendor/github.com/lib/pq/buf.go new file mode 100644 index 00000000000..4b0a0a8f7e9 --- /dev/null +++ b/vendor/github.com/lib/pq/buf.go @@ -0,0 +1,91 @@ +package pq + +import ( + "bytes" + "encoding/binary" + + "github.com/lib/pq/oid" +) + +type readBuf []byte + +func (b *readBuf) int32() (n int) { + n = int(int32(binary.BigEndian.Uint32(*b))) + *b = (*b)[4:] + return +} + +func (b *readBuf) oid() (n oid.Oid) { + n = oid.Oid(binary.BigEndian.Uint32(*b)) + *b = (*b)[4:] + return +} + +// N.B: this is actually an unsigned 16-bit integer, unlike int32 +func (b *readBuf) int16() (n int) { + n = int(binary.BigEndian.Uint16(*b)) + *b = (*b)[2:] + return +} + +func (b *readBuf) string() string { + i := bytes.IndexByte(*b, 0) + if i < 0 { + errorf("invalid message format; expected string terminator") + } + s := (*b)[:i] + *b = (*b)[i+1:] + return string(s) +} + +func (b *readBuf) next(n int) (v []byte) { + v = (*b)[:n] + *b = (*b)[n:] + return +} + +func (b *readBuf) byte() byte { + return b.next(1)[0] +} + +type writeBuf struct { + buf []byte + pos int +} + +func (b *writeBuf) int32(n int) { + x := make([]byte, 4) + binary.BigEndian.PutUint32(x, uint32(n)) + b.buf = append(b.buf, x...) +} + +func (b *writeBuf) int16(n int) { + x := make([]byte, 2) + binary.BigEndian.PutUint16(x, uint16(n)) + b.buf = append(b.buf, x...) +} + +func (b *writeBuf) string(s string) { + b.buf = append(append(b.buf, s...), '\000') +} + +func (b *writeBuf) byte(c byte) { + b.buf = append(b.buf, c) +} + +func (b *writeBuf) bytes(v []byte) { + b.buf = append(b.buf, v...) +} + +func (b *writeBuf) wrap() []byte { + p := b.buf[b.pos:] + binary.BigEndian.PutUint32(p, uint32(len(p))) + return b.buf +} + +func (b *writeBuf) next(c byte) { + p := b.buf[b.pos:] + binary.BigEndian.PutUint32(p, uint32(len(p))) + b.pos = len(b.buf) + 1 + b.buf = append(b.buf, c, 0, 0, 0, 0) +} diff --git a/vendor/github.com/lib/pq/conn.go b/vendor/github.com/lib/pq/conn.go new file mode 100644 index 00000000000..da4ff9de603 --- /dev/null +++ b/vendor/github.com/lib/pq/conn.go @@ -0,0 +1,2112 @@ +package pq + +import ( + "bufio" + "bytes" + "context" + "crypto/md5" + "crypto/sha256" + "database/sql" + "database/sql/driver" + "encoding/binary" + "errors" + "fmt" + "io" + "net" + "os" + "os/user" + "path" + "path/filepath" + "strconv" + "strings" + "sync" + "time" + "unicode" + + "github.com/lib/pq/oid" + "github.com/lib/pq/scram" +) + +// Common error types +var ( + ErrNotSupported = errors.New("pq: Unsupported command") + ErrInFailedTransaction = errors.New("pq: Could not complete operation in a failed transaction") + ErrSSLNotSupported = errors.New("pq: SSL is not enabled on the server") + ErrSSLKeyUnknownOwnership = errors.New("pq: Could not get owner information for private key, may not be properly protected") + ErrSSLKeyHasWorldPermissions = errors.New("pq: Private key has world access. Permissions should be u=rw,g=r (0640) if owned by root, or u=rw (0600), or less") + + ErrCouldNotDetectUsername = errors.New("pq: Could not detect default username. Please provide one explicitly") + + errUnexpectedReady = errors.New("unexpected ReadyForQuery") + errNoRowsAffected = errors.New("no RowsAffected available after the empty statement") + errNoLastInsertID = errors.New("no LastInsertId available after the empty statement") +) + +// Compile time validation that our types implement the expected interfaces +var ( + _ driver.Driver = Driver{} +) + +// Driver is the Postgres database driver. +type Driver struct{} + +// Open opens a new connection to the database. name is a connection string. +// Most users should only use it through database/sql package from the standard +// library. +func (d Driver) Open(name string) (driver.Conn, error) { + return Open(name) +} + +func init() { + sql.Register("postgres", &Driver{}) +} + +type parameterStatus struct { + // server version in the same format as server_version_num, or 0 if + // unavailable + serverVersion int + + // the current location based on the TimeZone value of the session, if + // available + currentLocation *time.Location +} + +type transactionStatus byte + +const ( + txnStatusIdle transactionStatus = 'I' + txnStatusIdleInTransaction transactionStatus = 'T' + txnStatusInFailedTransaction transactionStatus = 'E' +) + +func (s transactionStatus) String() string { + switch s { + case txnStatusIdle: + return "idle" + case txnStatusIdleInTransaction: + return "idle in transaction" + case txnStatusInFailedTransaction: + return "in a failed transaction" + default: + errorf("unknown transactionStatus %d", s) + } + + panic("not reached") +} + +// Dialer is the dialer interface. It can be used to obtain more control over +// how pq creates network connections. +type Dialer interface { + Dial(network, address string) (net.Conn, error) + DialTimeout(network, address string, timeout time.Duration) (net.Conn, error) +} + +// DialerContext is the context-aware dialer interface. +type DialerContext interface { + DialContext(ctx context.Context, network, address string) (net.Conn, error) +} + +type defaultDialer struct { + d net.Dialer +} + +func (d defaultDialer) Dial(network, address string) (net.Conn, error) { + return d.d.Dial(network, address) +} +func (d defaultDialer) DialTimeout( + network, address string, timeout time.Duration, +) (net.Conn, error) { + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + return d.DialContext(ctx, network, address) +} +func (d defaultDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) { + return d.d.DialContext(ctx, network, address) +} + +type conn struct { + c net.Conn + buf *bufio.Reader + namei int + scratch [512]byte + txnStatus transactionStatus + txnFinish func() + + // Save connection arguments to use during CancelRequest. + dialer Dialer + opts values + + // Cancellation key data for use with CancelRequest messages. + processID int + secretKey int + + parameterStatus parameterStatus + + saveMessageType byte + saveMessageBuffer []byte + + // If an error is set, this connection is bad and all public-facing + // functions should return the appropriate error by calling get() + // (ErrBadConn) or getForNext(). + err syncErr + + // If set, this connection should never use the binary format when + // receiving query results from prepared statements. Only provided for + // debugging. + disablePreparedBinaryResult bool + + // Whether to always send []byte parameters over as binary. Enables single + // round-trip mode for non-prepared Query calls. + binaryParameters bool + + // If true this connection is in the middle of a COPY + inCopy bool + + // If not nil, notices will be synchronously sent here + noticeHandler func(*Error) + + // If not nil, notifications will be synchronously sent here + notificationHandler func(*Notification) + + // GSSAPI context + gss GSS +} + +type syncErr struct { + err error + sync.Mutex +} + +// Return ErrBadConn if connection is bad. +func (e *syncErr) get() error { + e.Lock() + defer e.Unlock() + if e.err != nil { + return driver.ErrBadConn + } + return nil +} + +// Return the error set on the connection. Currently only used by rows.Next. +func (e *syncErr) getForNext() error { + e.Lock() + defer e.Unlock() + return e.err +} + +// Set error, only if it isn't set yet. +func (e *syncErr) set(err error) { + if err == nil { + panic("attempt to set nil err") + } + e.Lock() + defer e.Unlock() + if e.err == nil { + e.err = err + } +} + +// Handle driver-side settings in parsed connection string. +func (cn *conn) handleDriverSettings(o values) (err error) { + boolSetting := func(key string, val *bool) error { + if value, ok := o[key]; ok { + if value == "yes" { + *val = true + } else if value == "no" { + *val = false + } else { + return fmt.Errorf("unrecognized value %q for %s", value, key) + } + } + return nil + } + + err = boolSetting("disable_prepared_binary_result", &cn.disablePreparedBinaryResult) + if err != nil { + return err + } + return boolSetting("binary_parameters", &cn.binaryParameters) +} + +func (cn *conn) handlePgpass(o values) { + // if a password was supplied, do not process .pgpass + if _, ok := o["password"]; ok { + return + } + filename := os.Getenv("PGPASSFILE") + if filename == "" { + // XXX this code doesn't work on Windows where the default filename is + // XXX %APPDATA%\postgresql\pgpass.conf + // Prefer $HOME over user.Current due to glibc bug: golang.org/issue/13470 + userHome := os.Getenv("HOME") + if userHome == "" { + user, err := user.Current() + if err != nil { + return + } + userHome = user.HomeDir + } + filename = filepath.Join(userHome, ".pgpass") + } + fileinfo, err := os.Stat(filename) + if err != nil { + return + } + mode := fileinfo.Mode() + if mode&(0x77) != 0 { + // XXX should warn about incorrect .pgpass permissions as psql does + return + } + file, err := os.Open(filename) + if err != nil { + return + } + defer file.Close() + scanner := bufio.NewScanner(io.Reader(file)) + // From: https://github.com/tg/pgpass/blob/master/reader.go + for scanner.Scan() { + if scanText(scanner.Text(), o) { + break + } + } +} + +// GetFields is a helper function for scanText. +func getFields(s string) []string { + fs := make([]string, 0, 5) + f := make([]rune, 0, len(s)) + + var esc bool + for _, c := range s { + switch { + case esc: + f = append(f, c) + esc = false + case c == '\\': + esc = true + case c == ':': + fs = append(fs, string(f)) + f = f[:0] + default: + f = append(f, c) + } + } + return append(fs, string(f)) +} + +// ScanText assists HandlePgpass in it's objective. +func scanText(line string, o values) bool { + hostname := o["host"] + ntw, _ := network(o) + port := o["port"] + db := o["dbname"] + username := o["user"] + if len(line) == 0 || line[0] == '#' { + return false + } + split := getFields(line) + if len(split) != 5 { + return false + } + if (split[0] == "*" || split[0] == hostname || (split[0] == "localhost" && (hostname == "" || ntw == "unix"))) && (split[1] == "*" || split[1] == port) && (split[2] == "*" || split[2] == db) && (split[3] == "*" || split[3] == username) { + o["password"] = split[4] + return true + } + return false +} + +func (cn *conn) writeBuf(b byte) *writeBuf { + cn.scratch[0] = b + return &writeBuf{ + buf: cn.scratch[:5], + pos: 1, + } +} + +// Open opens a new connection to the database. dsn is a connection string. +// Most users should only use it through database/sql package from the standard +// library. +func Open(dsn string) (_ driver.Conn, err error) { + return DialOpen(defaultDialer{}, dsn) +} + +// DialOpen opens a new connection to the database using a dialer. +func DialOpen(d Dialer, dsn string) (_ driver.Conn, err error) { + c, err := NewConnector(dsn) + if err != nil { + return nil, err + } + c.Dialer(d) + return c.open(context.Background()) +} + +func (c *Connector) open(ctx context.Context) (cn *conn, err error) { + // Handle any panics during connection initialization. Note that we + // specifically do *not* want to use errRecover(), as that would turn any + // connection errors into ErrBadConns, hiding the real error message from + // the user. + defer errRecoverNoErrBadConn(&err) + + // Create a new values map (copy). This makes it so maps in different + // connections do not reference the same underlying data structure, so it + // is safe for multiple connections to concurrently write to their opts. + o := make(values) + for k, v := range c.opts { + o[k] = v + } + + cn = &conn{ + opts: o, + dialer: c.dialer, + } + err = cn.handleDriverSettings(o) + if err != nil { + return nil, err + } + cn.handlePgpass(o) + + cn.c, err = dial(ctx, c.dialer, o) + if err != nil { + return nil, err + } + + err = cn.ssl(o) + if err != nil { + if cn.c != nil { + cn.c.Close() + } + return nil, err + } + + // cn.startup panics on error. Make sure we don't leak cn.c. + panicking := true + defer func() { + if panicking { + cn.c.Close() + } + }() + + cn.buf = bufio.NewReader(cn.c) + cn.startup(o) + + // reset the deadline, in case one was set (see dial) + if timeout, ok := o["connect_timeout"]; ok && timeout != "0" { + err = cn.c.SetDeadline(time.Time{}) + } + panicking = false + return cn, err +} + +func dial(ctx context.Context, d Dialer, o values) (net.Conn, error) { + network, address := network(o) + + // Zero or not specified means wait indefinitely. + if timeout, ok := o["connect_timeout"]; ok && timeout != "0" { + seconds, err := strconv.ParseInt(timeout, 10, 0) + if err != nil { + return nil, fmt.Errorf("invalid value for parameter connect_timeout: %s", err) + } + duration := time.Duration(seconds) * time.Second + + // connect_timeout should apply to the entire connection establishment + // procedure, so we both use a timeout for the TCP connection + // establishment and set a deadline for doing the initial handshake. + // The deadline is then reset after startup() is done. + deadline := time.Now().Add(duration) + var conn net.Conn + if dctx, ok := d.(DialerContext); ok { + ctx, cancel := context.WithTimeout(ctx, duration) + defer cancel() + conn, err = dctx.DialContext(ctx, network, address) + } else { + conn, err = d.DialTimeout(network, address, duration) + } + if err != nil { + return nil, err + } + err = conn.SetDeadline(deadline) + return conn, err + } + if dctx, ok := d.(DialerContext); ok { + return dctx.DialContext(ctx, network, address) + } + return d.Dial(network, address) +} + +func network(o values) (string, string) { + host := o["host"] + + if strings.HasPrefix(host, "/") { + sockPath := path.Join(host, ".s.PGSQL."+o["port"]) + return "unix", sockPath + } + + return "tcp", net.JoinHostPort(host, o["port"]) +} + +type values map[string]string + +// scanner implements a tokenizer for libpq-style option strings. +type scanner struct { + s []rune + i int +} + +// newScanner returns a new scanner initialized with the option string s. +func newScanner(s string) *scanner { + return &scanner{[]rune(s), 0} +} + +// Next returns the next rune. +// It returns 0, false if the end of the text has been reached. +func (s *scanner) Next() (rune, bool) { + if s.i >= len(s.s) { + return 0, false + } + r := s.s[s.i] + s.i++ + return r, true +} + +// SkipSpaces returns the next non-whitespace rune. +// It returns 0, false if the end of the text has been reached. +func (s *scanner) SkipSpaces() (rune, bool) { + r, ok := s.Next() + for unicode.IsSpace(r) && ok { + r, ok = s.Next() + } + return r, ok +} + +// parseOpts parses the options from name and adds them to the values. +// +// The parsing code is based on conninfo_parse from libpq's fe-connect.c +func parseOpts(name string, o values) error { + s := newScanner(name) + + for { + var ( + keyRunes, valRunes []rune + r rune + ok bool + ) + + if r, ok = s.SkipSpaces(); !ok { + break + } + + // Scan the key + for !unicode.IsSpace(r) && r != '=' { + keyRunes = append(keyRunes, r) + if r, ok = s.Next(); !ok { + break + } + } + + // Skip any whitespace if we're not at the = yet + if r != '=' { + r, ok = s.SkipSpaces() + } + + // The current character should be = + if r != '=' || !ok { + return fmt.Errorf(`missing "=" after %q in connection info string"`, string(keyRunes)) + } + + // Skip any whitespace after the = + if r, ok = s.SkipSpaces(); !ok { + // If we reach the end here, the last value is just an empty string as per libpq. + o[string(keyRunes)] = "" + break + } + + if r != '\'' { + for !unicode.IsSpace(r) { + if r == '\\' { + if r, ok = s.Next(); !ok { + return fmt.Errorf(`missing character after backslash`) + } + } + valRunes = append(valRunes, r) + + if r, ok = s.Next(); !ok { + break + } + } + } else { + quote: + for { + if r, ok = s.Next(); !ok { + return fmt.Errorf(`unterminated quoted string literal in connection string`) + } + switch r { + case '\'': + break quote + case '\\': + r, _ = s.Next() + fallthrough + default: + valRunes = append(valRunes, r) + } + } + } + + o[string(keyRunes)] = string(valRunes) + } + + return nil +} + +func (cn *conn) isInTransaction() bool { + return cn.txnStatus == txnStatusIdleInTransaction || + cn.txnStatus == txnStatusInFailedTransaction +} + +func (cn *conn) checkIsInTransaction(intxn bool) { + if cn.isInTransaction() != intxn { + cn.err.set(driver.ErrBadConn) + errorf("unexpected transaction status %v", cn.txnStatus) + } +} + +func (cn *conn) Begin() (_ driver.Tx, err error) { + return cn.begin("") +} + +func (cn *conn) begin(mode string) (_ driver.Tx, err error) { + if err := cn.err.get(); err != nil { + return nil, err + } + defer cn.errRecover(&err) + + cn.checkIsInTransaction(false) + _, commandTag, err := cn.simpleExec("BEGIN" + mode) + if err != nil { + return nil, err + } + if commandTag != "BEGIN" { + cn.err.set(driver.ErrBadConn) + return nil, fmt.Errorf("unexpected command tag %s", commandTag) + } + if cn.txnStatus != txnStatusIdleInTransaction { + cn.err.set(driver.ErrBadConn) + return nil, fmt.Errorf("unexpected transaction status %v", cn.txnStatus) + } + return cn, nil +} + +func (cn *conn) closeTxn() { + if finish := cn.txnFinish; finish != nil { + finish() + } +} + +func (cn *conn) Commit() (err error) { + defer cn.closeTxn() + if err := cn.err.get(); err != nil { + return err + } + defer cn.errRecover(&err) + + cn.checkIsInTransaction(true) + // We don't want the client to think that everything is okay if it tries + // to commit a failed transaction. However, no matter what we return, + // database/sql will release this connection back into the free connection + // pool so we have to abort the current transaction here. Note that you + // would get the same behaviour if you issued a COMMIT in a failed + // transaction, so it's also the least surprising thing to do here. + if cn.txnStatus == txnStatusInFailedTransaction { + if err := cn.rollback(); err != nil { + return err + } + return ErrInFailedTransaction + } + + _, commandTag, err := cn.simpleExec("COMMIT") + if err != nil { + if cn.isInTransaction() { + cn.err.set(driver.ErrBadConn) + } + return err + } + if commandTag != "COMMIT" { + cn.err.set(driver.ErrBadConn) + return fmt.Errorf("unexpected command tag %s", commandTag) + } + cn.checkIsInTransaction(false) + return nil +} + +func (cn *conn) Rollback() (err error) { + defer cn.closeTxn() + if err := cn.err.get(); err != nil { + return err + } + defer cn.errRecover(&err) + return cn.rollback() +} + +func (cn *conn) rollback() (err error) { + cn.checkIsInTransaction(true) + _, commandTag, err := cn.simpleExec("ROLLBACK") + if err != nil { + if cn.isInTransaction() { + cn.err.set(driver.ErrBadConn) + } + return err + } + if commandTag != "ROLLBACK" { + return fmt.Errorf("unexpected command tag %s", commandTag) + } + cn.checkIsInTransaction(false) + return nil +} + +func (cn *conn) gname() string { + cn.namei++ + return strconv.FormatInt(int64(cn.namei), 10) +} + +func (cn *conn) simpleExec(q string) (res driver.Result, commandTag string, err error) { + b := cn.writeBuf('Q') + b.string(q) + cn.send(b) + + for { + t, r := cn.recv1() + switch t { + case 'C': + res, commandTag = cn.parseComplete(r.string()) + case 'Z': + cn.processReadyForQuery(r) + if res == nil && err == nil { + err = errUnexpectedReady + } + // done + return + case 'E': + err = parseError(r) + case 'I': + res = emptyRows + case 'T', 'D': + // ignore any results + default: + cn.err.set(driver.ErrBadConn) + errorf("unknown response for simple query: %q", t) + } + } +} + +func (cn *conn) simpleQuery(q string) (res *rows, err error) { + defer cn.errRecover(&err) + + b := cn.writeBuf('Q') + b.string(q) + cn.send(b) + + for { + t, r := cn.recv1() + switch t { + case 'C', 'I': + // We allow queries which don't return any results through Query as + // well as Exec. We still have to give database/sql a rows object + // the user can close, though, to avoid connections from being + // leaked. A "rows" with done=true works fine for that purpose. + if err != nil { + cn.err.set(driver.ErrBadConn) + errorf("unexpected message %q in simple query execution", t) + } + if res == nil { + res = &rows{ + cn: cn, + } + } + // Set the result and tag to the last command complete if there wasn't a + // query already run. Although queries usually return from here and cede + // control to Next, a query with zero results does not. + if t == 'C' { + res.result, res.tag = cn.parseComplete(r.string()) + if res.colNames != nil { + return + } + } + res.done = true + case 'Z': + cn.processReadyForQuery(r) + // done + return + case 'E': + res = nil + err = parseError(r) + case 'D': + if res == nil { + cn.err.set(driver.ErrBadConn) + errorf("unexpected DataRow in simple query execution") + } + // the query didn't fail; kick off to Next + cn.saveMessage(t, r) + return + case 'T': + // res might be non-nil here if we received a previous + // CommandComplete, but that's fine; just overwrite it + res = &rows{cn: cn} + res.rowsHeader = parsePortalRowDescribe(r) + + // To work around a bug in QueryRow in Go 1.2 and earlier, wait + // until the first DataRow has been received. + default: + cn.err.set(driver.ErrBadConn) + errorf("unknown response for simple query: %q", t) + } + } +} + +type noRows struct{} + +var emptyRows noRows + +var _ driver.Result = noRows{} + +func (noRows) LastInsertId() (int64, error) { + return 0, errNoLastInsertID +} + +func (noRows) RowsAffected() (int64, error) { + return 0, errNoRowsAffected +} + +// Decides which column formats to use for a prepared statement. The input is +// an array of type oids, one element per result column. +func decideColumnFormats( + colTyps []fieldDesc, forceText bool, +) (colFmts []format, colFmtData []byte) { + if len(colTyps) == 0 { + return nil, colFmtDataAllText + } + + colFmts = make([]format, len(colTyps)) + if forceText { + return colFmts, colFmtDataAllText + } + + allBinary := true + allText := true + for i, t := range colTyps { + switch t.OID { + // This is the list of types to use binary mode for when receiving them + // through a prepared statement. If a type appears in this list, it + // must also be implemented in binaryDecode in encode.go. + case oid.T_bytea: + fallthrough + case oid.T_int8: + fallthrough + case oid.T_int4: + fallthrough + case oid.T_int2: + fallthrough + case oid.T_uuid: + colFmts[i] = formatBinary + allText = false + + default: + allBinary = false + } + } + + if allBinary { + return colFmts, colFmtDataAllBinary + } else if allText { + return colFmts, colFmtDataAllText + } else { + colFmtData = make([]byte, 2+len(colFmts)*2) + binary.BigEndian.PutUint16(colFmtData, uint16(len(colFmts))) + for i, v := range colFmts { + binary.BigEndian.PutUint16(colFmtData[2+i*2:], uint16(v)) + } + return colFmts, colFmtData + } +} + +func (cn *conn) prepareTo(q, stmtName string) *stmt { + st := &stmt{cn: cn, name: stmtName} + + b := cn.writeBuf('P') + b.string(st.name) + b.string(q) + b.int16(0) + + b.next('D') + b.byte('S') + b.string(st.name) + + b.next('S') + cn.send(b) + + cn.readParseResponse() + st.paramTyps, st.colNames, st.colTyps = cn.readStatementDescribeResponse() + st.colFmts, st.colFmtData = decideColumnFormats(st.colTyps, cn.disablePreparedBinaryResult) + cn.readReadyForQuery() + return st +} + +func (cn *conn) Prepare(q string) (_ driver.Stmt, err error) { + if err := cn.err.get(); err != nil { + return nil, err + } + defer cn.errRecover(&err) + + if len(q) >= 4 && strings.EqualFold(q[:4], "COPY") { + s, err := cn.prepareCopyIn(q) + if err == nil { + cn.inCopy = true + } + return s, err + } + return cn.prepareTo(q, cn.gname()), nil +} + +func (cn *conn) Close() (err error) { + // Skip cn.bad return here because we always want to close a connection. + defer cn.errRecover(&err) + + // Ensure that cn.c.Close is always run. Since error handling is done with + // panics and cn.errRecover, the Close must be in a defer. + defer func() { + cerr := cn.c.Close() + if err == nil { + err = cerr + } + }() + + // Don't go through send(); ListenerConn relies on us not scribbling on the + // scratch buffer of this connection. + return cn.sendSimpleMessage('X') +} + +// Implement the "Queryer" interface +func (cn *conn) Query(query string, args []driver.Value) (driver.Rows, error) { + return cn.query(query, args) +} + +func (cn *conn) query(query string, args []driver.Value) (_ *rows, err error) { + if err := cn.err.get(); err != nil { + return nil, err + } + if cn.inCopy { + return nil, errCopyInProgress + } + defer cn.errRecover(&err) + + // Check to see if we can use the "simpleQuery" interface, which is + // *much* faster than going through prepare/exec + if len(args) == 0 { + return cn.simpleQuery(query) + } + + if cn.binaryParameters { + cn.sendBinaryModeQuery(query, args) + + cn.readParseResponse() + cn.readBindResponse() + rows := &rows{cn: cn} + rows.rowsHeader = cn.readPortalDescribeResponse() + cn.postExecuteWorkaround() + return rows, nil + } + st := cn.prepareTo(query, "") + st.exec(args) + return &rows{ + cn: cn, + rowsHeader: st.rowsHeader, + }, nil +} + +// Implement the optional "Execer" interface for one-shot queries +func (cn *conn) Exec(query string, args []driver.Value) (res driver.Result, err error) { + if err := cn.err.get(); err != nil { + return nil, err + } + defer cn.errRecover(&err) + + // Check to see if we can use the "simpleExec" interface, which is + // *much* faster than going through prepare/exec + if len(args) == 0 { + // ignore commandTag, our caller doesn't care + r, _, err := cn.simpleExec(query) + return r, err + } + + if cn.binaryParameters { + cn.sendBinaryModeQuery(query, args) + + cn.readParseResponse() + cn.readBindResponse() + cn.readPortalDescribeResponse() + cn.postExecuteWorkaround() + res, _, err = cn.readExecuteResponse("Execute") + return res, err + } + // Use the unnamed statement to defer planning until bind + // time, or else value-based selectivity estimates cannot be + // used. + st := cn.prepareTo(query, "") + r, err := st.Exec(args) + if err != nil { + panic(err) + } + return r, err +} + +type safeRetryError struct { + Err error +} + +func (se *safeRetryError) Error() string { + return se.Err.Error() +} + +func (cn *conn) send(m *writeBuf) { + n, err := cn.c.Write(m.wrap()) + if err != nil { + if n == 0 { + err = &safeRetryError{Err: err} + } + panic(err) + } +} + +func (cn *conn) sendStartupPacket(m *writeBuf) error { + _, err := cn.c.Write((m.wrap())[1:]) + return err +} + +// Send a message of type typ to the server on the other end of cn. The +// message should have no payload. This method does not use the scratch +// buffer. +func (cn *conn) sendSimpleMessage(typ byte) (err error) { + _, err = cn.c.Write([]byte{typ, '\x00', '\x00', '\x00', '\x04'}) + return err +} + +// saveMessage memorizes a message and its buffer in the conn struct. +// recvMessage will then return these values on the next call to it. This +// method is useful in cases where you have to see what the next message is +// going to be (e.g. to see whether it's an error or not) but you can't handle +// the message yourself. +func (cn *conn) saveMessage(typ byte, buf *readBuf) { + if cn.saveMessageType != 0 { + cn.err.set(driver.ErrBadConn) + errorf("unexpected saveMessageType %d", cn.saveMessageType) + } + cn.saveMessageType = typ + cn.saveMessageBuffer = *buf +} + +// recvMessage receives any message from the backend, or returns an error if +// a problem occurred while reading the message. +func (cn *conn) recvMessage(r *readBuf) (byte, error) { + // workaround for a QueryRow bug, see exec + if cn.saveMessageType != 0 { + t := cn.saveMessageType + *r = cn.saveMessageBuffer + cn.saveMessageType = 0 + cn.saveMessageBuffer = nil + return t, nil + } + + x := cn.scratch[:5] + _, err := io.ReadFull(cn.buf, x) + if err != nil { + return 0, err + } + + // read the type and length of the message that follows + t := x[0] + n := int(binary.BigEndian.Uint32(x[1:])) - 4 + var y []byte + if n <= len(cn.scratch) { + y = cn.scratch[:n] + } else { + y = make([]byte, n) + } + _, err = io.ReadFull(cn.buf, y) + if err != nil { + return 0, err + } + *r = y + return t, nil +} + +// recv receives a message from the backend, but if an error happened while +// reading the message or the received message was an ErrorResponse, it panics. +// NoticeResponses are ignored. This function should generally be used only +// during the startup sequence. +func (cn *conn) recv() (t byte, r *readBuf) { + for { + var err error + r = &readBuf{} + t, err = cn.recvMessage(r) + if err != nil { + panic(err) + } + switch t { + case 'E': + panic(parseError(r)) + case 'N': + if n := cn.noticeHandler; n != nil { + n(parseError(r)) + } + case 'A': + if n := cn.notificationHandler; n != nil { + n(recvNotification(r)) + } + default: + return + } + } +} + +// recv1Buf is exactly equivalent to recv1, except it uses a buffer supplied by +// the caller to avoid an allocation. +func (cn *conn) recv1Buf(r *readBuf) byte { + for { + t, err := cn.recvMessage(r) + if err != nil { + panic(err) + } + + switch t { + case 'A': + if n := cn.notificationHandler; n != nil { + n(recvNotification(r)) + } + case 'N': + if n := cn.noticeHandler; n != nil { + n(parseError(r)) + } + case 'S': + cn.processParameterStatus(r) + default: + return t + } + } +} + +// recv1 receives a message from the backend, panicking if an error occurs +// while attempting to read it. All asynchronous messages are ignored, with +// the exception of ErrorResponse. +func (cn *conn) recv1() (t byte, r *readBuf) { + r = &readBuf{} + t = cn.recv1Buf(r) + return t, r +} + +func (cn *conn) ssl(o values) error { + upgrade, err := ssl(o) + if err != nil { + return err + } + + if upgrade == nil { + // Nothing to do + return nil + } + + w := cn.writeBuf(0) + w.int32(80877103) + if err = cn.sendStartupPacket(w); err != nil { + return err + } + + b := cn.scratch[:1] + _, err = io.ReadFull(cn.c, b) + if err != nil { + return err + } + + if b[0] != 'S' { + return ErrSSLNotSupported + } + + cn.c, err = upgrade(cn.c) + return err +} + +// isDriverSetting returns true iff a setting is purely for configuring the +// driver's options and should not be sent to the server in the connection +// startup packet. +func isDriverSetting(key string) bool { + switch key { + case "host", "port": + return true + case "password": + return true + case "sslmode", "sslcert", "sslkey", "sslrootcert", "sslinline", "sslsni": + return true + case "fallback_application_name": + return true + case "connect_timeout": + return true + case "disable_prepared_binary_result": + return true + case "binary_parameters": + return true + case "krbsrvname": + return true + case "krbspn": + return true + default: + return false + } +} + +func (cn *conn) startup(o values) { + w := cn.writeBuf(0) + w.int32(196608) + // Send the backend the name of the database we want to connect to, and the + // user we want to connect as. Additionally, we send over any run-time + // parameters potentially included in the connection string. If the server + // doesn't recognize any of them, it will reply with an error. + for k, v := range o { + if isDriverSetting(k) { + // skip options which can't be run-time parameters + continue + } + // The protocol requires us to supply the database name as "database" + // instead of "dbname". + if k == "dbname" { + k = "database" + } + w.string(k) + w.string(v) + } + w.string("") + if err := cn.sendStartupPacket(w); err != nil { + panic(err) + } + + for { + t, r := cn.recv() + switch t { + case 'K': + cn.processBackendKeyData(r) + case 'S': + cn.processParameterStatus(r) + case 'R': + cn.auth(r, o) + case 'Z': + cn.processReadyForQuery(r) + return + default: + errorf("unknown response for startup: %q", t) + } + } +} + +func (cn *conn) auth(r *readBuf, o values) { + switch code := r.int32(); code { + case 0: + // OK + case 3: + w := cn.writeBuf('p') + w.string(o["password"]) + cn.send(w) + + t, r := cn.recv() + if t != 'R' { + errorf("unexpected password response: %q", t) + } + + if r.int32() != 0 { + errorf("unexpected authentication response: %q", t) + } + case 5: + s := string(r.next(4)) + w := cn.writeBuf('p') + w.string("md5" + md5s(md5s(o["password"]+o["user"])+s)) + cn.send(w) + + t, r := cn.recv() + if t != 'R' { + errorf("unexpected password response: %q", t) + } + + if r.int32() != 0 { + errorf("unexpected authentication response: %q", t) + } + case 7: // GSSAPI, startup + if newGss == nil { + errorf("kerberos error: no GSSAPI provider registered (import github.com/lib/pq/auth/kerberos if you need Kerberos support)") + } + cli, err := newGss() + if err != nil { + errorf("kerberos error: %s", err.Error()) + } + + var token []byte + + if spn, ok := o["krbspn"]; ok { + // Use the supplied SPN if provided.. + token, err = cli.GetInitTokenFromSpn(spn) + } else { + // Allow the kerberos service name to be overridden + service := "postgres" + if val, ok := o["krbsrvname"]; ok { + service = val + } + + token, err = cli.GetInitToken(o["host"], service) + } + + if err != nil { + errorf("failed to get Kerberos ticket: %q", err) + } + + w := cn.writeBuf('p') + w.bytes(token) + cn.send(w) + + // Store for GSSAPI continue message + cn.gss = cli + + case 8: // GSSAPI continue + + if cn.gss == nil { + errorf("GSSAPI protocol error") + } + + b := []byte(*r) + + done, tokOut, err := cn.gss.Continue(b) + if err == nil && !done { + w := cn.writeBuf('p') + w.bytes(tokOut) + cn.send(w) + } + + // Errors fall through and read the more detailed message + // from the server.. + + case 10: + sc := scram.NewClient(sha256.New, o["user"], o["password"]) + sc.Step(nil) + if sc.Err() != nil { + errorf("SCRAM-SHA-256 error: %s", sc.Err().Error()) + } + scOut := sc.Out() + + w := cn.writeBuf('p') + w.string("SCRAM-SHA-256") + w.int32(len(scOut)) + w.bytes(scOut) + cn.send(w) + + t, r := cn.recv() + if t != 'R' { + errorf("unexpected password response: %q", t) + } + + if r.int32() != 11 { + errorf("unexpected authentication response: %q", t) + } + + nextStep := r.next(len(*r)) + sc.Step(nextStep) + if sc.Err() != nil { + errorf("SCRAM-SHA-256 error: %s", sc.Err().Error()) + } + + scOut = sc.Out() + w = cn.writeBuf('p') + w.bytes(scOut) + cn.send(w) + + t, r = cn.recv() + if t != 'R' { + errorf("unexpected password response: %q", t) + } + + if r.int32() != 12 { + errorf("unexpected authentication response: %q", t) + } + + nextStep = r.next(len(*r)) + sc.Step(nextStep) + if sc.Err() != nil { + errorf("SCRAM-SHA-256 error: %s", sc.Err().Error()) + } + + default: + errorf("unknown authentication response: %d", code) + } +} + +type format int + +const formatText format = 0 +const formatBinary format = 1 + +// One result-column format code with the value 1 (i.e. all binary). +var colFmtDataAllBinary = []byte{0, 1, 0, 1} + +// No result-column format codes (i.e. all text). +var colFmtDataAllText = []byte{0, 0} + +type stmt struct { + cn *conn + name string + rowsHeader + colFmtData []byte + paramTyps []oid.Oid + closed bool +} + +func (st *stmt) Close() (err error) { + if st.closed { + return nil + } + if err := st.cn.err.get(); err != nil { + return err + } + defer st.cn.errRecover(&err) + + w := st.cn.writeBuf('C') + w.byte('S') + w.string(st.name) + st.cn.send(w) + + st.cn.send(st.cn.writeBuf('S')) + + t, _ := st.cn.recv1() + if t != '3' { + st.cn.err.set(driver.ErrBadConn) + errorf("unexpected close response: %q", t) + } + st.closed = true + + t, r := st.cn.recv1() + if t != 'Z' { + st.cn.err.set(driver.ErrBadConn) + errorf("expected ready for query, but got: %q", t) + } + st.cn.processReadyForQuery(r) + + return nil +} + +func (st *stmt) Query(v []driver.Value) (r driver.Rows, err error) { + return st.query(v) +} + +func (st *stmt) query(v []driver.Value) (r *rows, err error) { + if err := st.cn.err.get(); err != nil { + return nil, err + } + defer st.cn.errRecover(&err) + + st.exec(v) + return &rows{ + cn: st.cn, + rowsHeader: st.rowsHeader, + }, nil +} + +func (st *stmt) Exec(v []driver.Value) (res driver.Result, err error) { + if err := st.cn.err.get(); err != nil { + return nil, err + } + defer st.cn.errRecover(&err) + + st.exec(v) + res, _, err = st.cn.readExecuteResponse("simple query") + return res, err +} + +func (st *stmt) exec(v []driver.Value) { + if len(v) >= 65536 { + errorf("got %d parameters but PostgreSQL only supports 65535 parameters", len(v)) + } + if len(v) != len(st.paramTyps) { + errorf("got %d parameters but the statement requires %d", len(v), len(st.paramTyps)) + } + + cn := st.cn + w := cn.writeBuf('B') + w.byte(0) // unnamed portal + w.string(st.name) + + if cn.binaryParameters { + cn.sendBinaryParameters(w, v) + } else { + w.int16(0) + w.int16(len(v)) + for i, x := range v { + if x == nil { + w.int32(-1) + } else { + b := encode(&cn.parameterStatus, x, st.paramTyps[i]) + w.int32(len(b)) + w.bytes(b) + } + } + } + w.bytes(st.colFmtData) + + w.next('E') + w.byte(0) + w.int32(0) + + w.next('S') + cn.send(w) + + cn.readBindResponse() + cn.postExecuteWorkaround() + +} + +func (st *stmt) NumInput() int { + return len(st.paramTyps) +} + +// parseComplete parses the "command tag" from a CommandComplete message, and +// returns the number of rows affected (if applicable) and a string +// identifying only the command that was executed, e.g. "ALTER TABLE". If the +// command tag could not be parsed, parseComplete panics. +func (cn *conn) parseComplete(commandTag string) (driver.Result, string) { + commandsWithAffectedRows := []string{ + "SELECT ", + // INSERT is handled below + "UPDATE ", + "DELETE ", + "FETCH ", + "MOVE ", + "COPY ", + } + + var affectedRows *string + for _, tag := range commandsWithAffectedRows { + if strings.HasPrefix(commandTag, tag) { + t := commandTag[len(tag):] + affectedRows = &t + commandTag = tag[:len(tag)-1] + break + } + } + // INSERT also includes the oid of the inserted row in its command tag. + // Oids in user tables are deprecated, and the oid is only returned when + // exactly one row is inserted, so it's unlikely to be of value to any + // real-world application and we can ignore it. + if affectedRows == nil && strings.HasPrefix(commandTag, "INSERT ") { + parts := strings.Split(commandTag, " ") + if len(parts) != 3 { + cn.err.set(driver.ErrBadConn) + errorf("unexpected INSERT command tag %s", commandTag) + } + affectedRows = &parts[len(parts)-1] + commandTag = "INSERT" + } + // There should be no affected rows attached to the tag, just return it + if affectedRows == nil { + return driver.RowsAffected(0), commandTag + } + n, err := strconv.ParseInt(*affectedRows, 10, 64) + if err != nil { + cn.err.set(driver.ErrBadConn) + errorf("could not parse commandTag: %s", err) + } + return driver.RowsAffected(n), commandTag +} + +type rowsHeader struct { + colNames []string + colTyps []fieldDesc + colFmts []format +} + +type rows struct { + cn *conn + finish func() + rowsHeader + done bool + rb readBuf + result driver.Result + tag string + + next *rowsHeader +} + +func (rs *rows) Close() error { + if finish := rs.finish; finish != nil { + defer finish() + } + // no need to look at cn.bad as Next() will + for { + err := rs.Next(nil) + switch err { + case nil: + case io.EOF: + // rs.Next can return io.EOF on both 'Z' (ready for query) and 'T' (row + // description, used with HasNextResultSet). We need to fetch messages until + // we hit a 'Z', which is done by waiting for done to be set. + if rs.done { + return nil + } + default: + return err + } + } +} + +func (rs *rows) Columns() []string { + return rs.colNames +} + +func (rs *rows) Result() driver.Result { + if rs.result == nil { + return emptyRows + } + return rs.result +} + +func (rs *rows) Tag() string { + return rs.tag +} + +func (rs *rows) Next(dest []driver.Value) (err error) { + if rs.done { + return io.EOF + } + + conn := rs.cn + if err := conn.err.getForNext(); err != nil { + return err + } + defer conn.errRecover(&err) + + for { + t := conn.recv1Buf(&rs.rb) + switch t { + case 'E': + err = parseError(&rs.rb) + case 'C', 'I': + if t == 'C' { + rs.result, rs.tag = conn.parseComplete(rs.rb.string()) + } + continue + case 'Z': + conn.processReadyForQuery(&rs.rb) + rs.done = true + if err != nil { + return err + } + return io.EOF + case 'D': + n := rs.rb.int16() + if err != nil { + conn.err.set(driver.ErrBadConn) + errorf("unexpected DataRow after error %s", err) + } + if n < len(dest) { + dest = dest[:n] + } + for i := range dest { + l := rs.rb.int32() + if l == -1 { + dest[i] = nil + continue + } + dest[i] = decode(&conn.parameterStatus, rs.rb.next(l), rs.colTyps[i].OID, rs.colFmts[i]) + } + return + case 'T': + next := parsePortalRowDescribe(&rs.rb) + rs.next = &next + return io.EOF + default: + errorf("unexpected message after execute: %q", t) + } + } +} + +func (rs *rows) HasNextResultSet() bool { + hasNext := rs.next != nil && !rs.done + return hasNext +} + +func (rs *rows) NextResultSet() error { + if rs.next == nil { + return io.EOF + } + rs.rowsHeader = *rs.next + rs.next = nil + return nil +} + +// QuoteIdentifier quotes an "identifier" (e.g. a table or a column name) to be +// used as part of an SQL statement. For example: +// +// tblname := "my_table" +// data := "my_data" +// quoted := pq.QuoteIdentifier(tblname) +// err := db.Exec(fmt.Sprintf("INSERT INTO %s VALUES ($1)", quoted), data) +// +// Any double quotes in name will be escaped. The quoted identifier will be +// case sensitive when used in a query. If the input string contains a zero +// byte, the result will be truncated immediately before it. +func QuoteIdentifier(name string) string { + end := strings.IndexRune(name, 0) + if end > -1 { + name = name[:end] + } + return `"` + strings.Replace(name, `"`, `""`, -1) + `"` +} + +// BufferQuoteIdentifier satisfies the same purpose as QuoteIdentifier, but backed by a +// byte buffer. +func BufferQuoteIdentifier(name string, buffer *bytes.Buffer) { + end := strings.IndexRune(name, 0) + if end > -1 { + name = name[:end] + } + buffer.WriteRune('"') + buffer.WriteString(strings.Replace(name, `"`, `""`, -1)) + buffer.WriteRune('"') +} + +// QuoteLiteral quotes a 'literal' (e.g. a parameter, often used to pass literal +// to DDL and other statements that do not accept parameters) to be used as part +// of an SQL statement. For example: +// +// exp_date := pq.QuoteLiteral("2023-01-05 15:00:00Z") +// err := db.Exec(fmt.Sprintf("CREATE ROLE my_user VALID UNTIL %s", exp_date)) +// +// Any single quotes in name will be escaped. Any backslashes (i.e. "\") will be +// replaced by two backslashes (i.e. "\\") and the C-style escape identifier +// that PostgreSQL provides ('E') will be prepended to the string. +func QuoteLiteral(literal string) string { + // This follows the PostgreSQL internal algorithm for handling quoted literals + // from libpq, which can be found in the "PQEscapeStringInternal" function, + // which is found in the libpq/fe-exec.c source file: + // https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/interfaces/libpq/fe-exec.c + // + // substitute any single-quotes (') with two single-quotes ('') + literal = strings.Replace(literal, `'`, `''`, -1) + // determine if the string has any backslashes (\) in it. + // if it does, replace any backslashes (\) with two backslashes (\\) + // then, we need to wrap the entire string with a PostgreSQL + // C-style escape. Per how "PQEscapeStringInternal" handles this case, we + // also add a space before the "E" + if strings.Contains(literal, `\`) { + literal = strings.Replace(literal, `\`, `\\`, -1) + literal = ` E'` + literal + `'` + } else { + // otherwise, we can just wrap the literal with a pair of single quotes + literal = `'` + literal + `'` + } + return literal +} + +func md5s(s string) string { + h := md5.New() + h.Write([]byte(s)) + return fmt.Sprintf("%x", h.Sum(nil)) +} + +func (cn *conn) sendBinaryParameters(b *writeBuf, args []driver.Value) { + // Do one pass over the parameters to see if we're going to send any of + // them over in binary. If we are, create a paramFormats array at the + // same time. + var paramFormats []int + for i, x := range args { + _, ok := x.([]byte) + if ok { + if paramFormats == nil { + paramFormats = make([]int, len(args)) + } + paramFormats[i] = 1 + } + } + if paramFormats == nil { + b.int16(0) + } else { + b.int16(len(paramFormats)) + for _, x := range paramFormats { + b.int16(x) + } + } + + b.int16(len(args)) + for _, x := range args { + if x == nil { + b.int32(-1) + } else { + datum := binaryEncode(&cn.parameterStatus, x) + b.int32(len(datum)) + b.bytes(datum) + } + } +} + +func (cn *conn) sendBinaryModeQuery(query string, args []driver.Value) { + if len(args) >= 65536 { + errorf("got %d parameters but PostgreSQL only supports 65535 parameters", len(args)) + } + + b := cn.writeBuf('P') + b.byte(0) // unnamed statement + b.string(query) + b.int16(0) + + b.next('B') + b.int16(0) // unnamed portal and statement + cn.sendBinaryParameters(b, args) + b.bytes(colFmtDataAllText) + + b.next('D') + b.byte('P') + b.byte(0) // unnamed portal + + b.next('E') + b.byte(0) + b.int32(0) + + b.next('S') + cn.send(b) +} + +func (cn *conn) processParameterStatus(r *readBuf) { + var err error + + param := r.string() + switch param { + case "server_version": + var major1 int + var major2 int + _, err = fmt.Sscanf(r.string(), "%d.%d", &major1, &major2) + if err == nil { + cn.parameterStatus.serverVersion = major1*10000 + major2*100 + } + + case "TimeZone": + cn.parameterStatus.currentLocation, err = time.LoadLocation(r.string()) + if err != nil { + cn.parameterStatus.currentLocation = nil + } + + default: + // ignore + } +} + +func (cn *conn) processReadyForQuery(r *readBuf) { + cn.txnStatus = transactionStatus(r.byte()) +} + +func (cn *conn) readReadyForQuery() { + t, r := cn.recv1() + switch t { + case 'Z': + cn.processReadyForQuery(r) + return + default: + cn.err.set(driver.ErrBadConn) + errorf("unexpected message %q; expected ReadyForQuery", t) + } +} + +func (cn *conn) processBackendKeyData(r *readBuf) { + cn.processID = r.int32() + cn.secretKey = r.int32() +} + +func (cn *conn) readParseResponse() { + t, r := cn.recv1() + switch t { + case '1': + return + case 'E': + err := parseError(r) + cn.readReadyForQuery() + panic(err) + default: + cn.err.set(driver.ErrBadConn) + errorf("unexpected Parse response %q", t) + } +} + +func (cn *conn) readStatementDescribeResponse() ( + paramTyps []oid.Oid, + colNames []string, + colTyps []fieldDesc, +) { + for { + t, r := cn.recv1() + switch t { + case 't': + nparams := r.int16() + paramTyps = make([]oid.Oid, nparams) + for i := range paramTyps { + paramTyps[i] = r.oid() + } + case 'n': + return paramTyps, nil, nil + case 'T': + colNames, colTyps = parseStatementRowDescribe(r) + return paramTyps, colNames, colTyps + case 'E': + err := parseError(r) + cn.readReadyForQuery() + panic(err) + default: + cn.err.set(driver.ErrBadConn) + errorf("unexpected Describe statement response %q", t) + } + } +} + +func (cn *conn) readPortalDescribeResponse() rowsHeader { + t, r := cn.recv1() + switch t { + case 'T': + return parsePortalRowDescribe(r) + case 'n': + return rowsHeader{} + case 'E': + err := parseError(r) + cn.readReadyForQuery() + panic(err) + default: + cn.err.set(driver.ErrBadConn) + errorf("unexpected Describe response %q", t) + } + panic("not reached") +} + +func (cn *conn) readBindResponse() { + t, r := cn.recv1() + switch t { + case '2': + return + case 'E': + err := parseError(r) + cn.readReadyForQuery() + panic(err) + default: + cn.err.set(driver.ErrBadConn) + errorf("unexpected Bind response %q", t) + } +} + +func (cn *conn) postExecuteWorkaround() { + // Work around a bug in sql.DB.QueryRow: in Go 1.2 and earlier it ignores + // any errors from rows.Next, which masks errors that happened during the + // execution of the query. To avoid the problem in common cases, we wait + // here for one more message from the database. If it's not an error the + // query will likely succeed (or perhaps has already, if it's a + // CommandComplete), so we push the message into the conn struct; recv1 + // will return it as the next message for rows.Next or rows.Close. + // However, if it's an error, we wait until ReadyForQuery and then return + // the error to our caller. + for { + t, r := cn.recv1() + switch t { + case 'E': + err := parseError(r) + cn.readReadyForQuery() + panic(err) + case 'C', 'D', 'I': + // the query didn't fail, but we can't process this message + cn.saveMessage(t, r) + return + default: + cn.err.set(driver.ErrBadConn) + errorf("unexpected message during extended query execution: %q", t) + } + } +} + +// Only for Exec(), since we ignore the returned data +func (cn *conn) readExecuteResponse( + protocolState string, +) (res driver.Result, commandTag string, err error) { + for { + t, r := cn.recv1() + switch t { + case 'C': + if err != nil { + cn.err.set(driver.ErrBadConn) + errorf("unexpected CommandComplete after error %s", err) + } + res, commandTag = cn.parseComplete(r.string()) + case 'Z': + cn.processReadyForQuery(r) + if res == nil && err == nil { + err = errUnexpectedReady + } + return res, commandTag, err + case 'E': + err = parseError(r) + case 'T', 'D', 'I': + if err != nil { + cn.err.set(driver.ErrBadConn) + errorf("unexpected %q after error %s", t, err) + } + if t == 'I' { + res = emptyRows + } + // ignore any results + default: + cn.err.set(driver.ErrBadConn) + errorf("unknown %s response: %q", protocolState, t) + } + } +} + +func parseStatementRowDescribe(r *readBuf) (colNames []string, colTyps []fieldDesc) { + n := r.int16() + colNames = make([]string, n) + colTyps = make([]fieldDesc, n) + for i := range colNames { + colNames[i] = r.string() + r.next(6) + colTyps[i].OID = r.oid() + colTyps[i].Len = r.int16() + colTyps[i].Mod = r.int32() + // format code not known when describing a statement; always 0 + r.next(2) + } + return +} + +func parsePortalRowDescribe(r *readBuf) rowsHeader { + n := r.int16() + colNames := make([]string, n) + colFmts := make([]format, n) + colTyps := make([]fieldDesc, n) + for i := range colNames { + colNames[i] = r.string() + r.next(6) + colTyps[i].OID = r.oid() + colTyps[i].Len = r.int16() + colTyps[i].Mod = r.int32() + colFmts[i] = format(r.int16()) + } + return rowsHeader{ + colNames: colNames, + colFmts: colFmts, + colTyps: colTyps, + } +} + +// parseEnviron tries to mimic some of libpq's environment handling +// +// To ease testing, it does not directly reference os.Environ, but is +// designed to accept its output. +// +// Environment-set connection information is intended to have a higher +// precedence than a library default but lower than any explicitly +// passed information (such as in the URL or connection string). +func parseEnviron(env []string) (out map[string]string) { + out = make(map[string]string) + + for _, v := range env { + parts := strings.SplitN(v, "=", 2) + + accrue := func(keyname string) { + out[keyname] = parts[1] + } + unsupported := func() { + panic(fmt.Sprintf("setting %v not supported", parts[0])) + } + + // The order of these is the same as is seen in the + // PostgreSQL 9.1 manual. Unsupported but well-defined + // keys cause a panic; these should be unset prior to + // execution. Options which pq expects to be set to a + // certain value are allowed, but must be set to that + // value if present (they can, of course, be absent). + switch parts[0] { + case "PGHOST": + accrue("host") + case "PGHOSTADDR": + unsupported() + case "PGPORT": + accrue("port") + case "PGDATABASE": + accrue("dbname") + case "PGUSER": + accrue("user") + case "PGPASSWORD": + accrue("password") + case "PGSERVICE", "PGSERVICEFILE", "PGREALM": + unsupported() + case "PGOPTIONS": + accrue("options") + case "PGAPPNAME": + accrue("application_name") + case "PGSSLMODE": + accrue("sslmode") + case "PGSSLCERT": + accrue("sslcert") + case "PGSSLKEY": + accrue("sslkey") + case "PGSSLROOTCERT": + accrue("sslrootcert") + case "PGSSLSNI": + accrue("sslsni") + case "PGREQUIRESSL", "PGSSLCRL": + unsupported() + case "PGREQUIREPEER": + unsupported() + case "PGKRBSRVNAME", "PGGSSLIB": + unsupported() + case "PGCONNECT_TIMEOUT": + accrue("connect_timeout") + case "PGCLIENTENCODING": + accrue("client_encoding") + case "PGDATESTYLE": + accrue("datestyle") + case "PGTZ": + accrue("timezone") + case "PGGEQO": + accrue("geqo") + case "PGSYSCONFDIR", "PGLOCALEDIR": + unsupported() + } + } + + return out +} + +// isUTF8 returns whether name is a fuzzy variation of the string "UTF-8". +func isUTF8(name string) bool { + // Recognize all sorts of silly things as "UTF-8", like Postgres does + s := strings.Map(alnumLowerASCII, name) + return s == "utf8" || s == "unicode" +} + +func alnumLowerASCII(ch rune) rune { + if 'A' <= ch && ch <= 'Z' { + return ch + ('a' - 'A') + } + if 'a' <= ch && ch <= 'z' || '0' <= ch && ch <= '9' { + return ch + } + return -1 // discard +} + +// The database/sql/driver package says: +// All Conn implementations should implement the following interfaces: Pinger, SessionResetter, and Validator. +var _ driver.Pinger = &conn{} +var _ driver.SessionResetter = &conn{} + +func (cn *conn) ResetSession(ctx context.Context) error { + // Ensure bad connections are reported: From database/sql/driver: + // If a connection is never returned to the connection pool but immediately reused, then + // ResetSession is called prior to reuse but IsValid is not called. + return cn.err.get() +} + +func (cn *conn) IsValid() bool { + return cn.err.get() == nil +} diff --git a/vendor/github.com/lib/pq/conn_go115.go b/vendor/github.com/lib/pq/conn_go115.go new file mode 100644 index 00000000000..f4ef030f990 --- /dev/null +++ b/vendor/github.com/lib/pq/conn_go115.go @@ -0,0 +1,8 @@ +//go:build go1.15 +// +build go1.15 + +package pq + +import "database/sql/driver" + +var _ driver.Validator = &conn{} diff --git a/vendor/github.com/lib/pq/conn_go18.go b/vendor/github.com/lib/pq/conn_go18.go new file mode 100644 index 00000000000..63d4ca6aaa0 --- /dev/null +++ b/vendor/github.com/lib/pq/conn_go18.go @@ -0,0 +1,247 @@ +package pq + +import ( + "context" + "database/sql" + "database/sql/driver" + "fmt" + "io" + "io/ioutil" + "time" +) + +const ( + watchCancelDialContextTimeout = time.Second * 10 +) + +// Implement the "QueryerContext" interface +func (cn *conn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) { + list := make([]driver.Value, len(args)) + for i, nv := range args { + list[i] = nv.Value + } + finish := cn.watchCancel(ctx) + r, err := cn.query(query, list) + if err != nil { + if finish != nil { + finish() + } + return nil, err + } + r.finish = finish + return r, nil +} + +// Implement the "ExecerContext" interface +func (cn *conn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) { + list := make([]driver.Value, len(args)) + for i, nv := range args { + list[i] = nv.Value + } + + if finish := cn.watchCancel(ctx); finish != nil { + defer finish() + } + + return cn.Exec(query, list) +} + +// Implement the "ConnPrepareContext" interface +func (cn *conn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) { + if finish := cn.watchCancel(ctx); finish != nil { + defer finish() + } + return cn.Prepare(query) +} + +// Implement the "ConnBeginTx" interface +func (cn *conn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) { + var mode string + + switch sql.IsolationLevel(opts.Isolation) { + case sql.LevelDefault: + // Don't touch mode: use the server's default + case sql.LevelReadUncommitted: + mode = " ISOLATION LEVEL READ UNCOMMITTED" + case sql.LevelReadCommitted: + mode = " ISOLATION LEVEL READ COMMITTED" + case sql.LevelRepeatableRead: + mode = " ISOLATION LEVEL REPEATABLE READ" + case sql.LevelSerializable: + mode = " ISOLATION LEVEL SERIALIZABLE" + default: + return nil, fmt.Errorf("pq: isolation level not supported: %d", opts.Isolation) + } + + if opts.ReadOnly { + mode += " READ ONLY" + } else { + mode += " READ WRITE" + } + + tx, err := cn.begin(mode) + if err != nil { + return nil, err + } + cn.txnFinish = cn.watchCancel(ctx) + return tx, nil +} + +func (cn *conn) Ping(ctx context.Context) error { + if finish := cn.watchCancel(ctx); finish != nil { + defer finish() + } + rows, err := cn.simpleQuery(";") + if err != nil { + return driver.ErrBadConn // https://golang.org/pkg/database/sql/driver/#Pinger + } + rows.Close() + return nil +} + +func (cn *conn) watchCancel(ctx context.Context) func() { + if done := ctx.Done(); done != nil { + finished := make(chan struct{}, 1) + go func() { + select { + case <-done: + select { + case finished <- struct{}{}: + default: + // We raced with the finish func, let the next query handle this with the + // context. + return + } + + // Set the connection state to bad so it does not get reused. + cn.err.set(ctx.Err()) + + // At this point the function level context is canceled, + // so it must not be used for the additional network + // request to cancel the query. + // Create a new context to pass into the dial. + ctxCancel, cancel := context.WithTimeout(context.Background(), watchCancelDialContextTimeout) + defer cancel() + + _ = cn.cancel(ctxCancel) + case <-finished: + } + }() + return func() { + select { + case <-finished: + cn.err.set(ctx.Err()) + cn.Close() + case finished <- struct{}{}: + } + } + } + return nil +} + +func (cn *conn) cancel(ctx context.Context) error { + // Create a new values map (copy). This makes sure the connection created + // in this method cannot write to the same underlying data, which could + // cause a concurrent map write panic. This is necessary because cancel + // is called from a goroutine in watchCancel. + o := make(values) + for k, v := range cn.opts { + o[k] = v + } + + c, err := dial(ctx, cn.dialer, o) + if err != nil { + return err + } + defer c.Close() + + { + can := conn{ + c: c, + } + err = can.ssl(o) + if err != nil { + return err + } + + w := can.writeBuf(0) + w.int32(80877102) // cancel request code + w.int32(cn.processID) + w.int32(cn.secretKey) + + if err := can.sendStartupPacket(w); err != nil { + return err + } + } + + // Read until EOF to ensure that the server received the cancel. + { + _, err := io.Copy(ioutil.Discard, c) + return err + } +} + +// Implement the "StmtQueryContext" interface +func (st *stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) { + list := make([]driver.Value, len(args)) + for i, nv := range args { + list[i] = nv.Value + } + finish := st.watchCancel(ctx) + r, err := st.query(list) + if err != nil { + if finish != nil { + finish() + } + return nil, err + } + r.finish = finish + return r, nil +} + +// Implement the "StmtExecContext" interface +func (st *stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) { + list := make([]driver.Value, len(args)) + for i, nv := range args { + list[i] = nv.Value + } + + if finish := st.watchCancel(ctx); finish != nil { + defer finish() + } + + return st.Exec(list) +} + +// watchCancel is implemented on stmt in order to not mark the parent conn as bad +func (st *stmt) watchCancel(ctx context.Context) func() { + if done := ctx.Done(); done != nil { + finished := make(chan struct{}) + go func() { + select { + case <-done: + // At this point the function level context is canceled, + // so it must not be used for the additional network + // request to cancel the query. + // Create a new context to pass into the dial. + ctxCancel, cancel := context.WithTimeout(context.Background(), watchCancelDialContextTimeout) + defer cancel() + + _ = st.cancel(ctxCancel) + finished <- struct{}{} + case <-finished: + } + }() + return func() { + select { + case <-finished: + case finished <- struct{}{}: + } + } + } + return nil +} + +func (st *stmt) cancel(ctx context.Context) error { + return st.cn.cancel(ctx) +} diff --git a/vendor/github.com/lib/pq/connector.go b/vendor/github.com/lib/pq/connector.go new file mode 100644 index 00000000000..1145e12257a --- /dev/null +++ b/vendor/github.com/lib/pq/connector.go @@ -0,0 +1,120 @@ +package pq + +import ( + "context" + "database/sql/driver" + "errors" + "fmt" + "os" + "strings" +) + +// Connector represents a fixed configuration for the pq driver with a given +// name. Connector satisfies the database/sql/driver Connector interface and +// can be used to create any number of DB Conn's via the database/sql OpenDB +// function. +// +// See https://golang.org/pkg/database/sql/driver/#Connector. +// See https://golang.org/pkg/database/sql/#OpenDB. +type Connector struct { + opts values + dialer Dialer +} + +// Connect returns a connection to the database using the fixed configuration +// of this Connector. Context is not used. +func (c *Connector) Connect(ctx context.Context) (driver.Conn, error) { + return c.open(ctx) +} + +// Dialer allows change the dialer used to open connections. +func (c *Connector) Dialer(dialer Dialer) { + c.dialer = dialer +} + +// Driver returns the underlying driver of this Connector. +func (c *Connector) Driver() driver.Driver { + return &Driver{} +} + +// NewConnector returns a connector for the pq driver in a fixed configuration +// with the given dsn. The returned connector can be used to create any number +// of equivalent Conn's. The returned connector is intended to be used with +// database/sql.OpenDB. +// +// See https://golang.org/pkg/database/sql/driver/#Connector. +// See https://golang.org/pkg/database/sql/#OpenDB. +func NewConnector(dsn string) (*Connector, error) { + var err error + o := make(values) + + // A number of defaults are applied here, in this order: + // + // * Very low precedence defaults applied in every situation + // * Environment variables + // * Explicitly passed connection information + o["host"] = "localhost" + o["port"] = "5432" + // N.B.: Extra float digits should be set to 3, but that breaks + // Postgres 8.4 and older, where the max is 2. + o["extra_float_digits"] = "2" + for k, v := range parseEnviron(os.Environ()) { + o[k] = v + } + + if strings.HasPrefix(dsn, "postgres://") || strings.HasPrefix(dsn, "postgresql://") { + dsn, err = ParseURL(dsn) + if err != nil { + return nil, err + } + } + + if err := parseOpts(dsn, o); err != nil { + return nil, err + } + + // Use the "fallback" application name if necessary + if fallback, ok := o["fallback_application_name"]; ok { + if _, ok := o["application_name"]; !ok { + o["application_name"] = fallback + } + } + + // We can't work with any client_encoding other than UTF-8 currently. + // However, we have historically allowed the user to set it to UTF-8 + // explicitly, and there's no reason to break such programs, so allow that. + // Note that the "options" setting could also set client_encoding, but + // parsing its value is not worth it. Instead, we always explicitly send + // client_encoding as a separate run-time parameter, which should override + // anything set in options. + if enc, ok := o["client_encoding"]; ok && !isUTF8(enc) { + return nil, errors.New("client_encoding must be absent or 'UTF8'") + } + o["client_encoding"] = "UTF8" + // DateStyle needs a similar treatment. + if datestyle, ok := o["datestyle"]; ok { + if datestyle != "ISO, MDY" { + return nil, fmt.Errorf("setting datestyle must be absent or %v; got %v", "ISO, MDY", datestyle) + } + } else { + o["datestyle"] = "ISO, MDY" + } + + // If a user is not provided by any other means, the last + // resort is to use the current operating system provided user + // name. + if _, ok := o["user"]; !ok { + u, err := userCurrent() + if err != nil { + return nil, err + } + o["user"] = u + } + + // SSL is not necessary or supported over UNIX domain sockets + if network, _ := network(o); network == "unix" { + o["sslmode"] = "disable" + } + + return &Connector{opts: o, dialer: defaultDialer{}}, nil +} diff --git a/vendor/github.com/lib/pq/copy.go b/vendor/github.com/lib/pq/copy.go new file mode 100644 index 00000000000..a8f16b2b26b --- /dev/null +++ b/vendor/github.com/lib/pq/copy.go @@ -0,0 +1,348 @@ +package pq + +import ( + "bytes" + "context" + "database/sql/driver" + "encoding/binary" + "errors" + "fmt" + "sync" +) + +var ( + errCopyInClosed = errors.New("pq: copyin statement has already been closed") + errBinaryCopyNotSupported = errors.New("pq: only text format supported for COPY") + errCopyToNotSupported = errors.New("pq: COPY TO is not supported") + errCopyNotSupportedOutsideTxn = errors.New("pq: COPY is only allowed inside a transaction") + errCopyInProgress = errors.New("pq: COPY in progress") +) + +// CopyIn creates a COPY FROM statement which can be prepared with +// Tx.Prepare(). The target table should be visible in search_path. +func CopyIn(table string, columns ...string) string { + buffer := bytes.NewBufferString("COPY ") + BufferQuoteIdentifier(table, buffer) + buffer.WriteString(" (") + makeStmt(buffer, columns...) + return buffer.String() +} + +// MakeStmt makes the stmt string for CopyIn and CopyInSchema. +func makeStmt(buffer *bytes.Buffer, columns ...string) { + //s := bytes.NewBufferString() + for i, col := range columns { + if i != 0 { + buffer.WriteString(", ") + } + BufferQuoteIdentifier(col, buffer) + } + buffer.WriteString(") FROM STDIN") +} + +// CopyInSchema creates a COPY FROM statement which can be prepared with +// Tx.Prepare(). +func CopyInSchema(schema, table string, columns ...string) string { + buffer := bytes.NewBufferString("COPY ") + BufferQuoteIdentifier(schema, buffer) + buffer.WriteRune('.') + BufferQuoteIdentifier(table, buffer) + buffer.WriteString(" (") + makeStmt(buffer, columns...) + return buffer.String() +} + +type copyin struct { + cn *conn + buffer []byte + rowData chan []byte + done chan bool + + closed bool + + mu struct { + sync.Mutex + err error + driver.Result + } +} + +const ciBufferSize = 64 * 1024 + +// flush buffer before the buffer is filled up and needs reallocation +const ciBufferFlushSize = 63 * 1024 + +func (cn *conn) prepareCopyIn(q string) (_ driver.Stmt, err error) { + if !cn.isInTransaction() { + return nil, errCopyNotSupportedOutsideTxn + } + + ci := ©in{ + cn: cn, + buffer: make([]byte, 0, ciBufferSize), + rowData: make(chan []byte), + done: make(chan bool, 1), + } + // add CopyData identifier + 4 bytes for message length + ci.buffer = append(ci.buffer, 'd', 0, 0, 0, 0) + + b := cn.writeBuf('Q') + b.string(q) + cn.send(b) + +awaitCopyInResponse: + for { + t, r := cn.recv1() + switch t { + case 'G': + if r.byte() != 0 { + err = errBinaryCopyNotSupported + break awaitCopyInResponse + } + go ci.resploop() + return ci, nil + case 'H': + err = errCopyToNotSupported + break awaitCopyInResponse + case 'E': + err = parseError(r) + case 'Z': + if err == nil { + ci.setBad(driver.ErrBadConn) + errorf("unexpected ReadyForQuery in response to COPY") + } + cn.processReadyForQuery(r) + return nil, err + default: + ci.setBad(driver.ErrBadConn) + errorf("unknown response for copy query: %q", t) + } + } + + // something went wrong, abort COPY before we return + b = cn.writeBuf('f') + b.string(err.Error()) + cn.send(b) + + for { + t, r := cn.recv1() + switch t { + case 'c', 'C', 'E': + case 'Z': + // correctly aborted, we're done + cn.processReadyForQuery(r) + return nil, err + default: + ci.setBad(driver.ErrBadConn) + errorf("unknown response for CopyFail: %q", t) + } + } +} + +func (ci *copyin) flush(buf []byte) { + // set message length (without message identifier) + binary.BigEndian.PutUint32(buf[1:], uint32(len(buf)-1)) + + _, err := ci.cn.c.Write(buf) + if err != nil { + panic(err) + } +} + +func (ci *copyin) resploop() { + for { + var r readBuf + t, err := ci.cn.recvMessage(&r) + if err != nil { + ci.setBad(driver.ErrBadConn) + ci.setError(err) + ci.done <- true + return + } + switch t { + case 'C': + // complete + res, _ := ci.cn.parseComplete(r.string()) + ci.setResult(res) + case 'N': + if n := ci.cn.noticeHandler; n != nil { + n(parseError(&r)) + } + case 'Z': + ci.cn.processReadyForQuery(&r) + ci.done <- true + return + case 'E': + err := parseError(&r) + ci.setError(err) + default: + ci.setBad(driver.ErrBadConn) + ci.setError(fmt.Errorf("unknown response during CopyIn: %q", t)) + ci.done <- true + return + } + } +} + +func (ci *copyin) setBad(err error) { + ci.cn.err.set(err) +} + +func (ci *copyin) getBad() error { + return ci.cn.err.get() +} + +func (ci *copyin) err() error { + ci.mu.Lock() + err := ci.mu.err + ci.mu.Unlock() + return err +} + +// setError() sets ci.err if one has not been set already. Caller must not be +// holding ci.Mutex. +func (ci *copyin) setError(err error) { + ci.mu.Lock() + if ci.mu.err == nil { + ci.mu.err = err + } + ci.mu.Unlock() +} + +func (ci *copyin) setResult(result driver.Result) { + ci.mu.Lock() + ci.mu.Result = result + ci.mu.Unlock() +} + +func (ci *copyin) getResult() driver.Result { + ci.mu.Lock() + result := ci.mu.Result + ci.mu.Unlock() + if result == nil { + return driver.RowsAffected(0) + } + return result +} + +func (ci *copyin) NumInput() int { + return -1 +} + +func (ci *copyin) Query(v []driver.Value) (r driver.Rows, err error) { + return nil, ErrNotSupported +} + +// Exec inserts values into the COPY stream. The insert is asynchronous +// and Exec can return errors from previous Exec calls to the same +// COPY stmt. +// +// You need to call Exec(nil) to sync the COPY stream and to get any +// errors from pending data, since Stmt.Close() doesn't return errors +// to the user. +func (ci *copyin) Exec(v []driver.Value) (r driver.Result, err error) { + if ci.closed { + return nil, errCopyInClosed + } + + if err := ci.getBad(); err != nil { + return nil, err + } + defer ci.cn.errRecover(&err) + + if err := ci.err(); err != nil { + return nil, err + } + + if len(v) == 0 { + if err := ci.Close(); err != nil { + return driver.RowsAffected(0), err + } + + return ci.getResult(), nil + } + + numValues := len(v) + for i, value := range v { + ci.buffer = appendEncodedText(&ci.cn.parameterStatus, ci.buffer, value) + if i < numValues-1 { + ci.buffer = append(ci.buffer, '\t') + } + } + + ci.buffer = append(ci.buffer, '\n') + + if len(ci.buffer) > ciBufferFlushSize { + ci.flush(ci.buffer) + // reset buffer, keep bytes for message identifier and length + ci.buffer = ci.buffer[:5] + } + + return driver.RowsAffected(0), nil +} + +// CopyData inserts a raw string into the COPY stream. The insert is +// asynchronous and CopyData can return errors from previous CopyData calls to +// the same COPY stmt. +// +// You need to call Exec(nil) to sync the COPY stream and to get any +// errors from pending data, since Stmt.Close() doesn't return errors +// to the user. +func (ci *copyin) CopyData(ctx context.Context, line string) (r driver.Result, err error) { + if ci.closed { + return nil, errCopyInClosed + } + + if finish := ci.cn.watchCancel(ctx); finish != nil { + defer finish() + } + + if err := ci.getBad(); err != nil { + return nil, err + } + defer ci.cn.errRecover(&err) + + if err := ci.err(); err != nil { + return nil, err + } + + ci.buffer = append(ci.buffer, []byte(line)...) + ci.buffer = append(ci.buffer, '\n') + + if len(ci.buffer) > ciBufferFlushSize { + ci.flush(ci.buffer) + // reset buffer, keep bytes for message identifier and length + ci.buffer = ci.buffer[:5] + } + + return driver.RowsAffected(0), nil +} + +func (ci *copyin) Close() (err error) { + if ci.closed { // Don't do anything, we're already closed + return nil + } + ci.closed = true + + if err := ci.getBad(); err != nil { + return err + } + defer ci.cn.errRecover(&err) + + if len(ci.buffer) > 0 { + ci.flush(ci.buffer) + } + // Avoid touching the scratch buffer as resploop could be using it. + err = ci.cn.sendSimpleMessage('c') + if err != nil { + return err + } + + <-ci.done + ci.cn.inCopy = false + + if err := ci.err(); err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/lib/pq/doc.go b/vendor/github.com/lib/pq/doc.go new file mode 100644 index 00000000000..b57184801ba --- /dev/null +++ b/vendor/github.com/lib/pq/doc.go @@ -0,0 +1,268 @@ +/* +Package pq is a pure Go Postgres driver for the database/sql package. + +In most cases clients will use the database/sql package instead of +using this package directly. For example: + + import ( + "database/sql" + + _ "github.com/lib/pq" + ) + + func main() { + connStr := "user=pqgotest dbname=pqgotest sslmode=verify-full" + db, err := sql.Open("postgres", connStr) + if err != nil { + log.Fatal(err) + } + + age := 21 + rows, err := db.Query("SELECT name FROM users WHERE age = $1", age) + … + } + +You can also connect to a database using a URL. For example: + + connStr := "postgres://pqgotest:password@localhost/pqgotest?sslmode=verify-full" + db, err := sql.Open("postgres", connStr) + + +Connection String Parameters + + +Similarly to libpq, when establishing a connection using pq you are expected to +supply a connection string containing zero or more parameters. +A subset of the connection parameters supported by libpq are also supported by pq. +Additionally, pq also lets you specify run-time parameters (such as search_path or work_mem) +directly in the connection string. This is different from libpq, which does not allow +run-time parameters in the connection string, instead requiring you to supply +them in the options parameter. + +For compatibility with libpq, the following special connection parameters are +supported: + + * dbname - The name of the database to connect to + * user - The user to sign in as + * password - The user's password + * host - The host to connect to. Values that start with / are for unix + domain sockets. (default is localhost) + * port - The port to bind to. (default is 5432) + * sslmode - Whether or not to use SSL (default is require, this is not + the default for libpq) + * fallback_application_name - An application_name to fall back to if one isn't provided. + * connect_timeout - Maximum wait for connection, in seconds. Zero or + not specified means wait indefinitely. + * sslcert - Cert file location. The file must contain PEM encoded data. + * sslkey - Key file location. The file must contain PEM encoded data. + * sslrootcert - The location of the root certificate file. The file + must contain PEM encoded data. + +Valid values for sslmode are: + + * disable - No SSL + * require - Always SSL (skip verification) + * verify-ca - Always SSL (verify that the certificate presented by the + server was signed by a trusted CA) + * verify-full - Always SSL (verify that the certification presented by + the server was signed by a trusted CA and the server host name + matches the one in the certificate) + +See http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING +for more information about connection string parameters. + +Use single quotes for values that contain whitespace: + + "user=pqgotest password='with spaces'" + +A backslash will escape the next character in values: + + "user=space\ man password='it\'s valid'" + +Note that the connection parameter client_encoding (which sets the +text encoding for the connection) may be set but must be "UTF8", +matching with the same rules as Postgres. It is an error to provide +any other value. + +In addition to the parameters listed above, any run-time parameter that can be +set at backend start time can be set in the connection string. For more +information, see +http://www.postgresql.org/docs/current/static/runtime-config.html. + +Most environment variables as specified at http://www.postgresql.org/docs/current/static/libpq-envars.html +supported by libpq are also supported by pq. If any of the environment +variables not supported by pq are set, pq will panic during connection +establishment. Environment variables have a lower precedence than explicitly +provided connection parameters. + +The pgpass mechanism as described in http://www.postgresql.org/docs/current/static/libpq-pgpass.html +is supported, but on Windows PGPASSFILE must be specified explicitly. + + +Queries + + +database/sql does not dictate any specific format for parameter +markers in query strings, and pq uses the Postgres-native ordinal markers, +as shown above. The same marker can be reused for the same parameter: + + rows, err := db.Query(`SELECT name FROM users WHERE favorite_fruit = $1 + OR age BETWEEN $2 AND $2 + 3`, "orange", 64) + +pq does not support the LastInsertId() method of the Result type in database/sql. +To return the identifier of an INSERT (or UPDATE or DELETE), use the Postgres +RETURNING clause with a standard Query or QueryRow call: + + var userid int + err := db.QueryRow(`INSERT INTO users(name, favorite_fruit, age) + VALUES('beatrice', 'starfruit', 93) RETURNING id`).Scan(&userid) + +For more details on RETURNING, see the Postgres documentation: + + http://www.postgresql.org/docs/current/static/sql-insert.html + http://www.postgresql.org/docs/current/static/sql-update.html + http://www.postgresql.org/docs/current/static/sql-delete.html + +For additional instructions on querying see the documentation for the database/sql package. + + +Data Types + + +Parameters pass through driver.DefaultParameterConverter before they are handled +by this package. When the binary_parameters connection option is enabled, +[]byte values are sent directly to the backend as data in binary format. + +This package returns the following types for values from the PostgreSQL backend: + + - integer types smallint, integer, and bigint are returned as int64 + - floating-point types real and double precision are returned as float64 + - character types char, varchar, and text are returned as string + - temporal types date, time, timetz, timestamp, and timestamptz are + returned as time.Time + - the boolean type is returned as bool + - the bytea type is returned as []byte + +All other types are returned directly from the backend as []byte values in text format. + + +Errors + + +pq may return errors of type *pq.Error which can be interrogated for error details: + + if err, ok := err.(*pq.Error); ok { + fmt.Println("pq error:", err.Code.Name()) + } + +See the pq.Error type for details. + + +Bulk imports + +You can perform bulk imports by preparing a statement returned by pq.CopyIn (or +pq.CopyInSchema) in an explicit transaction (sql.Tx). The returned statement +handle can then be repeatedly "executed" to copy data into the target table. +After all data has been processed you should call Exec() once with no arguments +to flush all buffered data. Any call to Exec() might return an error which +should be handled appropriately, but because of the internal buffering an error +returned by Exec() might not be related to the data passed in the call that +failed. + +CopyIn uses COPY FROM internally. It is not possible to COPY outside of an +explicit transaction in pq. + +Usage example: + + txn, err := db.Begin() + if err != nil { + log.Fatal(err) + } + + stmt, err := txn.Prepare(pq.CopyIn("users", "name", "age")) + if err != nil { + log.Fatal(err) + } + + for _, user := range users { + _, err = stmt.Exec(user.Name, int64(user.Age)) + if err != nil { + log.Fatal(err) + } + } + + _, err = stmt.Exec() + if err != nil { + log.Fatal(err) + } + + err = stmt.Close() + if err != nil { + log.Fatal(err) + } + + err = txn.Commit() + if err != nil { + log.Fatal(err) + } + + +Notifications + + +PostgreSQL supports a simple publish/subscribe model over database +connections. See http://www.postgresql.org/docs/current/static/sql-notify.html +for more information about the general mechanism. + +To start listening for notifications, you first have to open a new connection +to the database by calling NewListener. This connection can not be used for +anything other than LISTEN / NOTIFY. Calling Listen will open a "notification +channel"; once a notification channel is open, a notification generated on that +channel will effect a send on the Listener.Notify channel. A notification +channel will remain open until Unlisten is called, though connection loss might +result in some notifications being lost. To solve this problem, Listener sends +a nil pointer over the Notify channel any time the connection is re-established +following a connection loss. The application can get information about the +state of the underlying connection by setting an event callback in the call to +NewListener. + +A single Listener can safely be used from concurrent goroutines, which means +that there is often no need to create more than one Listener in your +application. However, a Listener is always connected to a single database, so +you will need to create a new Listener instance for every database you want to +receive notifications in. + +The channel name in both Listen and Unlisten is case sensitive, and can contain +any characters legal in an identifier (see +http://www.postgresql.org/docs/current/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS +for more information). Note that the channel name will be truncated to 63 +bytes by the PostgreSQL server. + +You can find a complete, working example of Listener usage at +https://godoc.org/github.com/lib/pq/example/listen. + + +Kerberos Support + + +If you need support for Kerberos authentication, add the following to your main +package: + + import "github.com/lib/pq/auth/kerberos" + + func init() { + pq.RegisterGSSProvider(func() (pq.Gss, error) { return kerberos.NewGSS() }) + } + +This package is in a separate module so that users who don't need Kerberos +don't have to download unnecessary dependencies. + +When imported, additional connection string parameters are supported: + + * krbsrvname - GSS (Kerberos) service name when constructing the + SPN (default is `postgres`). This will be combined with the host + to form the full SPN: `krbsrvname/host`. + * krbspn - GSS (Kerberos) SPN. This takes priority over + `krbsrvname` if present. +*/ +package pq diff --git a/vendor/github.com/lib/pq/encode.go b/vendor/github.com/lib/pq/encode.go new file mode 100644 index 00000000000..bffe6096aff --- /dev/null +++ b/vendor/github.com/lib/pq/encode.go @@ -0,0 +1,632 @@ +package pq + +import ( + "bytes" + "database/sql/driver" + "encoding/binary" + "encoding/hex" + "errors" + "fmt" + "math" + "regexp" + "strconv" + "strings" + "sync" + "time" + + "github.com/lib/pq/oid" +) + +var time2400Regex = regexp.MustCompile(`^(24:00(?::00(?:\.0+)?)?)(?:[Z+-].*)?$`) + +func binaryEncode(parameterStatus *parameterStatus, x interface{}) []byte { + switch v := x.(type) { + case []byte: + return v + default: + return encode(parameterStatus, x, oid.T_unknown) + } +} + +func encode(parameterStatus *parameterStatus, x interface{}, pgtypOid oid.Oid) []byte { + switch v := x.(type) { + case int64: + return strconv.AppendInt(nil, v, 10) + case float64: + return strconv.AppendFloat(nil, v, 'f', -1, 64) + case []byte: + if pgtypOid == oid.T_bytea { + return encodeBytea(parameterStatus.serverVersion, v) + } + + return v + case string: + if pgtypOid == oid.T_bytea { + return encodeBytea(parameterStatus.serverVersion, []byte(v)) + } + + return []byte(v) + case bool: + return strconv.AppendBool(nil, v) + case time.Time: + return formatTs(v) + + default: + errorf("encode: unknown type for %T", v) + } + + panic("not reached") +} + +func decode(parameterStatus *parameterStatus, s []byte, typ oid.Oid, f format) interface{} { + switch f { + case formatBinary: + return binaryDecode(parameterStatus, s, typ) + case formatText: + return textDecode(parameterStatus, s, typ) + default: + panic("not reached") + } +} + +func binaryDecode(parameterStatus *parameterStatus, s []byte, typ oid.Oid) interface{} { + switch typ { + case oid.T_bytea: + return s + case oid.T_int8: + return int64(binary.BigEndian.Uint64(s)) + case oid.T_int4: + return int64(int32(binary.BigEndian.Uint32(s))) + case oid.T_int2: + return int64(int16(binary.BigEndian.Uint16(s))) + case oid.T_uuid: + b, err := decodeUUIDBinary(s) + if err != nil { + panic(err) + } + return b + + default: + errorf("don't know how to decode binary parameter of type %d", uint32(typ)) + } + + panic("not reached") +} + +func textDecode(parameterStatus *parameterStatus, s []byte, typ oid.Oid) interface{} { + switch typ { + case oid.T_char, oid.T_varchar, oid.T_text: + return string(s) + case oid.T_bytea: + b, err := parseBytea(s) + if err != nil { + errorf("%s", err) + } + return b + case oid.T_timestamptz: + return parseTs(parameterStatus.currentLocation, string(s)) + case oid.T_timestamp, oid.T_date: + return parseTs(nil, string(s)) + case oid.T_time: + return mustParse("15:04:05", typ, s) + case oid.T_timetz: + return mustParse("15:04:05-07", typ, s) + case oid.T_bool: + return s[0] == 't' + case oid.T_int8, oid.T_int4, oid.T_int2: + i, err := strconv.ParseInt(string(s), 10, 64) + if err != nil { + errorf("%s", err) + } + return i + case oid.T_float4, oid.T_float8: + // We always use 64 bit parsing, regardless of whether the input text is for + // a float4 or float8, because clients expect float64s for all float datatypes + // and returning a 32-bit parsed float64 produces lossy results. + f, err := strconv.ParseFloat(string(s), 64) + if err != nil { + errorf("%s", err) + } + return f + } + + return s +} + +// appendEncodedText encodes item in text format as required by COPY +// and appends to buf +func appendEncodedText(parameterStatus *parameterStatus, buf []byte, x interface{}) []byte { + switch v := x.(type) { + case int64: + return strconv.AppendInt(buf, v, 10) + case float64: + return strconv.AppendFloat(buf, v, 'f', -1, 64) + case []byte: + encodedBytea := encodeBytea(parameterStatus.serverVersion, v) + return appendEscapedText(buf, string(encodedBytea)) + case string: + return appendEscapedText(buf, v) + case bool: + return strconv.AppendBool(buf, v) + case time.Time: + return append(buf, formatTs(v)...) + case nil: + return append(buf, "\\N"...) + default: + errorf("encode: unknown type for %T", v) + } + + panic("not reached") +} + +func appendEscapedText(buf []byte, text string) []byte { + escapeNeeded := false + startPos := 0 + var c byte + + // check if we need to escape + for i := 0; i < len(text); i++ { + c = text[i] + if c == '\\' || c == '\n' || c == '\r' || c == '\t' { + escapeNeeded = true + startPos = i + break + } + } + if !escapeNeeded { + return append(buf, text...) + } + + // copy till first char to escape, iterate the rest + result := append(buf, text[:startPos]...) + for i := startPos; i < len(text); i++ { + c = text[i] + switch c { + case '\\': + result = append(result, '\\', '\\') + case '\n': + result = append(result, '\\', 'n') + case '\r': + result = append(result, '\\', 'r') + case '\t': + result = append(result, '\\', 't') + default: + result = append(result, c) + } + } + return result +} + +func mustParse(f string, typ oid.Oid, s []byte) time.Time { + str := string(s) + + // Check for a minute and second offset in the timezone. + if typ == oid.T_timestamptz || typ == oid.T_timetz { + for i := 3; i <= 6; i += 3 { + if str[len(str)-i] == ':' { + f += ":00" + continue + } + break + } + } + + // Special case for 24:00 time. + // Unfortunately, golang does not parse 24:00 as a proper time. + // In this case, we want to try "round to the next day", to differentiate. + // As such, we find if the 24:00 time matches at the beginning; if so, + // we default it back to 00:00 but add a day later. + var is2400Time bool + switch typ { + case oid.T_timetz, oid.T_time: + if matches := time2400Regex.FindStringSubmatch(str); matches != nil { + // Concatenate timezone information at the back. + str = "00:00:00" + str[len(matches[1]):] + is2400Time = true + } + } + t, err := time.Parse(f, str) + if err != nil { + errorf("decode: %s", err) + } + if is2400Time { + t = t.Add(24 * time.Hour) + } + return t +} + +var errInvalidTimestamp = errors.New("invalid timestamp") + +type timestampParser struct { + err error +} + +func (p *timestampParser) expect(str string, char byte, pos int) { + if p.err != nil { + return + } + if pos+1 > len(str) { + p.err = errInvalidTimestamp + return + } + if c := str[pos]; c != char && p.err == nil { + p.err = fmt.Errorf("expected '%v' at position %v; got '%v'", char, pos, c) + } +} + +func (p *timestampParser) mustAtoi(str string, begin int, end int) int { + if p.err != nil { + return 0 + } + if begin < 0 || end < 0 || begin > end || end > len(str) { + p.err = errInvalidTimestamp + return 0 + } + result, err := strconv.Atoi(str[begin:end]) + if err != nil { + if p.err == nil { + p.err = fmt.Errorf("expected number; got '%v'", str) + } + return 0 + } + return result +} + +// The location cache caches the time zones typically used by the client. +type locationCache struct { + cache map[int]*time.Location + lock sync.Mutex +} + +// All connections share the same list of timezones. Benchmarking shows that +// about 5% speed could be gained by putting the cache in the connection and +// losing the mutex, at the cost of a small amount of memory and a somewhat +// significant increase in code complexity. +var globalLocationCache = newLocationCache() + +func newLocationCache() *locationCache { + return &locationCache{cache: make(map[int]*time.Location)} +} + +// Returns the cached timezone for the specified offset, creating and caching +// it if necessary. +func (c *locationCache) getLocation(offset int) *time.Location { + c.lock.Lock() + defer c.lock.Unlock() + + location, ok := c.cache[offset] + if !ok { + location = time.FixedZone("", offset) + c.cache[offset] = location + } + + return location +} + +var infinityTsEnabled = false +var infinityTsNegative time.Time +var infinityTsPositive time.Time + +const ( + infinityTsEnabledAlready = "pq: infinity timestamp enabled already" + infinityTsNegativeMustBeSmaller = "pq: infinity timestamp: negative value must be smaller (before) than positive" +) + +// EnableInfinityTs controls the handling of Postgres' "-infinity" and +// "infinity" "timestamp"s. +// +// If EnableInfinityTs is not called, "-infinity" and "infinity" will return +// []byte("-infinity") and []byte("infinity") respectively, and potentially +// cause error "sql: Scan error on column index 0: unsupported driver -> Scan +// pair: []uint8 -> *time.Time", when scanning into a time.Time value. +// +// Once EnableInfinityTs has been called, all connections created using this +// driver will decode Postgres' "-infinity" and "infinity" for "timestamp", +// "timestamp with time zone" and "date" types to the predefined minimum and +// maximum times, respectively. When encoding time.Time values, any time which +// equals or precedes the predefined minimum time will be encoded to +// "-infinity". Any values at or past the maximum time will similarly be +// encoded to "infinity". +// +// If EnableInfinityTs is called with negative >= positive, it will panic. +// Calling EnableInfinityTs after a connection has been established results in +// undefined behavior. If EnableInfinityTs is called more than once, it will +// panic. +func EnableInfinityTs(negative time.Time, positive time.Time) { + if infinityTsEnabled { + panic(infinityTsEnabledAlready) + } + if !negative.Before(positive) { + panic(infinityTsNegativeMustBeSmaller) + } + infinityTsEnabled = true + infinityTsNegative = negative + infinityTsPositive = positive +} + +/* + * Testing might want to toggle infinityTsEnabled + */ +func disableInfinityTs() { + infinityTsEnabled = false +} + +// This is a time function specific to the Postgres default DateStyle +// setting ("ISO, MDY"), the only one we currently support. This +// accounts for the discrepancies between the parsing available with +// time.Parse and the Postgres date formatting quirks. +func parseTs(currentLocation *time.Location, str string) interface{} { + switch str { + case "-infinity": + if infinityTsEnabled { + return infinityTsNegative + } + return []byte(str) + case "infinity": + if infinityTsEnabled { + return infinityTsPositive + } + return []byte(str) + } + t, err := ParseTimestamp(currentLocation, str) + if err != nil { + panic(err) + } + return t +} + +// ParseTimestamp parses Postgres' text format. It returns a time.Time in +// currentLocation iff that time's offset agrees with the offset sent from the +// Postgres server. Otherwise, ParseTimestamp returns a time.Time with the +// fixed offset offset provided by the Postgres server. +func ParseTimestamp(currentLocation *time.Location, str string) (time.Time, error) { + p := timestampParser{} + + monSep := strings.IndexRune(str, '-') + // this is Gregorian year, not ISO Year + // In Gregorian system, the year 1 BC is followed by AD 1 + year := p.mustAtoi(str, 0, monSep) + daySep := monSep + 3 + month := p.mustAtoi(str, monSep+1, daySep) + p.expect(str, '-', daySep) + timeSep := daySep + 3 + day := p.mustAtoi(str, daySep+1, timeSep) + + minLen := monSep + len("01-01") + 1 + + isBC := strings.HasSuffix(str, " BC") + if isBC { + minLen += 3 + } + + var hour, minute, second int + if len(str) > minLen { + p.expect(str, ' ', timeSep) + minSep := timeSep + 3 + p.expect(str, ':', minSep) + hour = p.mustAtoi(str, timeSep+1, minSep) + secSep := minSep + 3 + p.expect(str, ':', secSep) + minute = p.mustAtoi(str, minSep+1, secSep) + secEnd := secSep + 3 + second = p.mustAtoi(str, secSep+1, secEnd) + } + remainderIdx := monSep + len("01-01 00:00:00") + 1 + // Three optional (but ordered) sections follow: the + // fractional seconds, the time zone offset, and the BC + // designation. We set them up here and adjust the other + // offsets if the preceding sections exist. + + nanoSec := 0 + tzOff := 0 + + if remainderIdx < len(str) && str[remainderIdx] == '.' { + fracStart := remainderIdx + 1 + fracOff := strings.IndexAny(str[fracStart:], "-+Z ") + if fracOff < 0 { + fracOff = len(str) - fracStart + } + fracSec := p.mustAtoi(str, fracStart, fracStart+fracOff) + nanoSec = fracSec * (1000000000 / int(math.Pow(10, float64(fracOff)))) + + remainderIdx += fracOff + 1 + } + if tzStart := remainderIdx; tzStart < len(str) && (str[tzStart] == '-' || str[tzStart] == '+') { + // time zone separator is always '-' or '+' or 'Z' (UTC is +00) + var tzSign int + switch c := str[tzStart]; c { + case '-': + tzSign = -1 + case '+': + tzSign = +1 + default: + return time.Time{}, fmt.Errorf("expected '-' or '+' at position %v; got %v", tzStart, c) + } + tzHours := p.mustAtoi(str, tzStart+1, tzStart+3) + remainderIdx += 3 + var tzMin, tzSec int + if remainderIdx < len(str) && str[remainderIdx] == ':' { + tzMin = p.mustAtoi(str, remainderIdx+1, remainderIdx+3) + remainderIdx += 3 + } + if remainderIdx < len(str) && str[remainderIdx] == ':' { + tzSec = p.mustAtoi(str, remainderIdx+1, remainderIdx+3) + remainderIdx += 3 + } + tzOff = tzSign * ((tzHours * 60 * 60) + (tzMin * 60) + tzSec) + } else if tzStart < len(str) && str[tzStart] == 'Z' { + // time zone Z separator indicates UTC is +00 + remainderIdx += 1 + } + + var isoYear int + + if isBC { + isoYear = 1 - year + remainderIdx += 3 + } else { + isoYear = year + } + if remainderIdx < len(str) { + return time.Time{}, fmt.Errorf("expected end of input, got %v", str[remainderIdx:]) + } + t := time.Date(isoYear, time.Month(month), day, + hour, minute, second, nanoSec, + globalLocationCache.getLocation(tzOff)) + + if currentLocation != nil { + // Set the location of the returned Time based on the session's + // TimeZone value, but only if the local time zone database agrees with + // the remote database on the offset. + lt := t.In(currentLocation) + _, newOff := lt.Zone() + if newOff == tzOff { + t = lt + } + } + + return t, p.err +} + +// formatTs formats t into a format postgres understands. +func formatTs(t time.Time) []byte { + if infinityTsEnabled { + // t <= -infinity : ! (t > -infinity) + if !t.After(infinityTsNegative) { + return []byte("-infinity") + } + // t >= infinity : ! (!t < infinity) + if !t.Before(infinityTsPositive) { + return []byte("infinity") + } + } + return FormatTimestamp(t) +} + +// FormatTimestamp formats t into Postgres' text format for timestamps. +func FormatTimestamp(t time.Time) []byte { + // Need to send dates before 0001 A.D. with " BC" suffix, instead of the + // minus sign preferred by Go. + // Beware, "0000" in ISO is "1 BC", "-0001" is "2 BC" and so on + bc := false + if t.Year() <= 0 { + // flip year sign, and add 1, e.g: "0" will be "1", and "-10" will be "11" + t = t.AddDate((-t.Year())*2+1, 0, 0) + bc = true + } + b := []byte(t.Format("2006-01-02 15:04:05.999999999Z07:00")) + + _, offset := t.Zone() + offset %= 60 + if offset != 0 { + // RFC3339Nano already printed the minus sign + if offset < 0 { + offset = -offset + } + + b = append(b, ':') + if offset < 10 { + b = append(b, '0') + } + b = strconv.AppendInt(b, int64(offset), 10) + } + + if bc { + b = append(b, " BC"...) + } + return b +} + +// Parse a bytea value received from the server. Both "hex" and the legacy +// "escape" format are supported. +func parseBytea(s []byte) (result []byte, err error) { + if len(s) >= 2 && bytes.Equal(s[:2], []byte("\\x")) { + // bytea_output = hex + s = s[2:] // trim off leading "\\x" + result = make([]byte, hex.DecodedLen(len(s))) + _, err := hex.Decode(result, s) + if err != nil { + return nil, err + } + } else { + // bytea_output = escape + for len(s) > 0 { + if s[0] == '\\' { + // escaped '\\' + if len(s) >= 2 && s[1] == '\\' { + result = append(result, '\\') + s = s[2:] + continue + } + + // '\\' followed by an octal number + if len(s) < 4 { + return nil, fmt.Errorf("invalid bytea sequence %v", s) + } + r, err := strconv.ParseUint(string(s[1:4]), 8, 8) + if err != nil { + return nil, fmt.Errorf("could not parse bytea value: %s", err.Error()) + } + result = append(result, byte(r)) + s = s[4:] + } else { + // We hit an unescaped, raw byte. Try to read in as many as + // possible in one go. + i := bytes.IndexByte(s, '\\') + if i == -1 { + result = append(result, s...) + break + } + result = append(result, s[:i]...) + s = s[i:] + } + } + } + + return result, nil +} + +func encodeBytea(serverVersion int, v []byte) (result []byte) { + if serverVersion >= 90000 { + // Use the hex format if we know that the server supports it + result = make([]byte, 2+hex.EncodedLen(len(v))) + result[0] = '\\' + result[1] = 'x' + hex.Encode(result[2:], v) + } else { + // .. or resort to "escape" + for _, b := range v { + if b == '\\' { + result = append(result, '\\', '\\') + } else if b < 0x20 || b > 0x7e { + result = append(result, []byte(fmt.Sprintf("\\%03o", b))...) + } else { + result = append(result, b) + } + } + } + + return result +} + +// NullTime represents a time.Time that may be null. NullTime implements the +// sql.Scanner interface so it can be used as a scan destination, similar to +// sql.NullString. +type NullTime struct { + Time time.Time + Valid bool // Valid is true if Time is not NULL +} + +// Scan implements the Scanner interface. +func (nt *NullTime) Scan(value interface{}) error { + nt.Time, nt.Valid = value.(time.Time) + return nil +} + +// Value implements the driver Valuer interface. +func (nt NullTime) Value() (driver.Value, error) { + if !nt.Valid { + return nil, nil + } + return nt.Time, nil +} diff --git a/vendor/github.com/lib/pq/error.go b/vendor/github.com/lib/pq/error.go new file mode 100644 index 00000000000..f67c5a5fa64 --- /dev/null +++ b/vendor/github.com/lib/pq/error.go @@ -0,0 +1,523 @@ +package pq + +import ( + "database/sql/driver" + "fmt" + "io" + "net" + "runtime" +) + +// Error severities +const ( + Efatal = "FATAL" + Epanic = "PANIC" + Ewarning = "WARNING" + Enotice = "NOTICE" + Edebug = "DEBUG" + Einfo = "INFO" + Elog = "LOG" +) + +// Error represents an error communicating with the server. +// +// See http://www.postgresql.org/docs/current/static/protocol-error-fields.html for details of the fields +type Error struct { + Severity string + Code ErrorCode + Message string + Detail string + Hint string + Position string + InternalPosition string + InternalQuery string + Where string + Schema string + Table string + Column string + DataTypeName string + Constraint string + File string + Line string + Routine string +} + +// ErrorCode is a five-character error code. +type ErrorCode string + +// Name returns a more human friendly rendering of the error code, namely the +// "condition name". +// +// See http://www.postgresql.org/docs/9.3/static/errcodes-appendix.html for +// details. +func (ec ErrorCode) Name() string { + return errorCodeNames[ec] +} + +// ErrorClass is only the class part of an error code. +type ErrorClass string + +// Name returns the condition name of an error class. It is equivalent to the +// condition name of the "standard" error code (i.e. the one having the last +// three characters "000"). +func (ec ErrorClass) Name() string { + return errorCodeNames[ErrorCode(ec+"000")] +} + +// Class returns the error class, e.g. "28". +// +// See http://www.postgresql.org/docs/9.3/static/errcodes-appendix.html for +// details. +func (ec ErrorCode) Class() ErrorClass { + return ErrorClass(ec[0:2]) +} + +// errorCodeNames is a mapping between the five-character error codes and the +// human readable "condition names". It is derived from the list at +// http://www.postgresql.org/docs/9.3/static/errcodes-appendix.html +var errorCodeNames = map[ErrorCode]string{ + // Class 00 - Successful Completion + "00000": "successful_completion", + // Class 01 - Warning + "01000": "warning", + "0100C": "dynamic_result_sets_returned", + "01008": "implicit_zero_bit_padding", + "01003": "null_value_eliminated_in_set_function", + "01007": "privilege_not_granted", + "01006": "privilege_not_revoked", + "01004": "string_data_right_truncation", + "01P01": "deprecated_feature", + // Class 02 - No Data (this is also a warning class per the SQL standard) + "02000": "no_data", + "02001": "no_additional_dynamic_result_sets_returned", + // Class 03 - SQL Statement Not Yet Complete + "03000": "sql_statement_not_yet_complete", + // Class 08 - Connection Exception + "08000": "connection_exception", + "08003": "connection_does_not_exist", + "08006": "connection_failure", + "08001": "sqlclient_unable_to_establish_sqlconnection", + "08004": "sqlserver_rejected_establishment_of_sqlconnection", + "08007": "transaction_resolution_unknown", + "08P01": "protocol_violation", + // Class 09 - Triggered Action Exception + "09000": "triggered_action_exception", + // Class 0A - Feature Not Supported + "0A000": "feature_not_supported", + // Class 0B - Invalid Transaction Initiation + "0B000": "invalid_transaction_initiation", + // Class 0F - Locator Exception + "0F000": "locator_exception", + "0F001": "invalid_locator_specification", + // Class 0L - Invalid Grantor + "0L000": "invalid_grantor", + "0LP01": "invalid_grant_operation", + // Class 0P - Invalid Role Specification + "0P000": "invalid_role_specification", + // Class 0Z - Diagnostics Exception + "0Z000": "diagnostics_exception", + "0Z002": "stacked_diagnostics_accessed_without_active_handler", + // Class 20 - Case Not Found + "20000": "case_not_found", + // Class 21 - Cardinality Violation + "21000": "cardinality_violation", + // Class 22 - Data Exception + "22000": "data_exception", + "2202E": "array_subscript_error", + "22021": "character_not_in_repertoire", + "22008": "datetime_field_overflow", + "22012": "division_by_zero", + "22005": "error_in_assignment", + "2200B": "escape_character_conflict", + "22022": "indicator_overflow", + "22015": "interval_field_overflow", + "2201E": "invalid_argument_for_logarithm", + "22014": "invalid_argument_for_ntile_function", + "22016": "invalid_argument_for_nth_value_function", + "2201F": "invalid_argument_for_power_function", + "2201G": "invalid_argument_for_width_bucket_function", + "22018": "invalid_character_value_for_cast", + "22007": "invalid_datetime_format", + "22019": "invalid_escape_character", + "2200D": "invalid_escape_octet", + "22025": "invalid_escape_sequence", + "22P06": "nonstandard_use_of_escape_character", + "22010": "invalid_indicator_parameter_value", + "22023": "invalid_parameter_value", + "2201B": "invalid_regular_expression", + "2201W": "invalid_row_count_in_limit_clause", + "2201X": "invalid_row_count_in_result_offset_clause", + "22009": "invalid_time_zone_displacement_value", + "2200C": "invalid_use_of_escape_character", + "2200G": "most_specific_type_mismatch", + "22004": "null_value_not_allowed", + "22002": "null_value_no_indicator_parameter", + "22003": "numeric_value_out_of_range", + "2200H": "sequence_generator_limit_exceeded", + "22026": "string_data_length_mismatch", + "22001": "string_data_right_truncation", + "22011": "substring_error", + "22027": "trim_error", + "22024": "unterminated_c_string", + "2200F": "zero_length_character_string", + "22P01": "floating_point_exception", + "22P02": "invalid_text_representation", + "22P03": "invalid_binary_representation", + "22P04": "bad_copy_file_format", + "22P05": "untranslatable_character", + "2200L": "not_an_xml_document", + "2200M": "invalid_xml_document", + "2200N": "invalid_xml_content", + "2200S": "invalid_xml_comment", + "2200T": "invalid_xml_processing_instruction", + // Class 23 - Integrity Constraint Violation + "23000": "integrity_constraint_violation", + "23001": "restrict_violation", + "23502": "not_null_violation", + "23503": "foreign_key_violation", + "23505": "unique_violation", + "23514": "check_violation", + "23P01": "exclusion_violation", + // Class 24 - Invalid Cursor State + "24000": "invalid_cursor_state", + // Class 25 - Invalid Transaction State + "25000": "invalid_transaction_state", + "25001": "active_sql_transaction", + "25002": "branch_transaction_already_active", + "25008": "held_cursor_requires_same_isolation_level", + "25003": "inappropriate_access_mode_for_branch_transaction", + "25004": "inappropriate_isolation_level_for_branch_transaction", + "25005": "no_active_sql_transaction_for_branch_transaction", + "25006": "read_only_sql_transaction", + "25007": "schema_and_data_statement_mixing_not_supported", + "25P01": "no_active_sql_transaction", + "25P02": "in_failed_sql_transaction", + // Class 26 - Invalid SQL Statement Name + "26000": "invalid_sql_statement_name", + // Class 27 - Triggered Data Change Violation + "27000": "triggered_data_change_violation", + // Class 28 - Invalid Authorization Specification + "28000": "invalid_authorization_specification", + "28P01": "invalid_password", + // Class 2B - Dependent Privilege Descriptors Still Exist + "2B000": "dependent_privilege_descriptors_still_exist", + "2BP01": "dependent_objects_still_exist", + // Class 2D - Invalid Transaction Termination + "2D000": "invalid_transaction_termination", + // Class 2F - SQL Routine Exception + "2F000": "sql_routine_exception", + "2F005": "function_executed_no_return_statement", + "2F002": "modifying_sql_data_not_permitted", + "2F003": "prohibited_sql_statement_attempted", + "2F004": "reading_sql_data_not_permitted", + // Class 34 - Invalid Cursor Name + "34000": "invalid_cursor_name", + // Class 38 - External Routine Exception + "38000": "external_routine_exception", + "38001": "containing_sql_not_permitted", + "38002": "modifying_sql_data_not_permitted", + "38003": "prohibited_sql_statement_attempted", + "38004": "reading_sql_data_not_permitted", + // Class 39 - External Routine Invocation Exception + "39000": "external_routine_invocation_exception", + "39001": "invalid_sqlstate_returned", + "39004": "null_value_not_allowed", + "39P01": "trigger_protocol_violated", + "39P02": "srf_protocol_violated", + // Class 3B - Savepoint Exception + "3B000": "savepoint_exception", + "3B001": "invalid_savepoint_specification", + // Class 3D - Invalid Catalog Name + "3D000": "invalid_catalog_name", + // Class 3F - Invalid Schema Name + "3F000": "invalid_schema_name", + // Class 40 - Transaction Rollback + "40000": "transaction_rollback", + "40002": "transaction_integrity_constraint_violation", + "40001": "serialization_failure", + "40003": "statement_completion_unknown", + "40P01": "deadlock_detected", + // Class 42 - Syntax Error or Access Rule Violation + "42000": "syntax_error_or_access_rule_violation", + "42601": "syntax_error", + "42501": "insufficient_privilege", + "42846": "cannot_coerce", + "42803": "grouping_error", + "42P20": "windowing_error", + "42P19": "invalid_recursion", + "42830": "invalid_foreign_key", + "42602": "invalid_name", + "42622": "name_too_long", + "42939": "reserved_name", + "42804": "datatype_mismatch", + "42P18": "indeterminate_datatype", + "42P21": "collation_mismatch", + "42P22": "indeterminate_collation", + "42809": "wrong_object_type", + "42703": "undefined_column", + "42883": "undefined_function", + "42P01": "undefined_table", + "42P02": "undefined_parameter", + "42704": "undefined_object", + "42701": "duplicate_column", + "42P03": "duplicate_cursor", + "42P04": "duplicate_database", + "42723": "duplicate_function", + "42P05": "duplicate_prepared_statement", + "42P06": "duplicate_schema", + "42P07": "duplicate_table", + "42712": "duplicate_alias", + "42710": "duplicate_object", + "42702": "ambiguous_column", + "42725": "ambiguous_function", + "42P08": "ambiguous_parameter", + "42P09": "ambiguous_alias", + "42P10": "invalid_column_reference", + "42611": "invalid_column_definition", + "42P11": "invalid_cursor_definition", + "42P12": "invalid_database_definition", + "42P13": "invalid_function_definition", + "42P14": "invalid_prepared_statement_definition", + "42P15": "invalid_schema_definition", + "42P16": "invalid_table_definition", + "42P17": "invalid_object_definition", + // Class 44 - WITH CHECK OPTION Violation + "44000": "with_check_option_violation", + // Class 53 - Insufficient Resources + "53000": "insufficient_resources", + "53100": "disk_full", + "53200": "out_of_memory", + "53300": "too_many_connections", + "53400": "configuration_limit_exceeded", + // Class 54 - Program Limit Exceeded + "54000": "program_limit_exceeded", + "54001": "statement_too_complex", + "54011": "too_many_columns", + "54023": "too_many_arguments", + // Class 55 - Object Not In Prerequisite State + "55000": "object_not_in_prerequisite_state", + "55006": "object_in_use", + "55P02": "cant_change_runtime_param", + "55P03": "lock_not_available", + // Class 57 - Operator Intervention + "57000": "operator_intervention", + "57014": "query_canceled", + "57P01": "admin_shutdown", + "57P02": "crash_shutdown", + "57P03": "cannot_connect_now", + "57P04": "database_dropped", + // Class 58 - System Error (errors external to PostgreSQL itself) + "58000": "system_error", + "58030": "io_error", + "58P01": "undefined_file", + "58P02": "duplicate_file", + // Class F0 - Configuration File Error + "F0000": "config_file_error", + "F0001": "lock_file_exists", + // Class HV - Foreign Data Wrapper Error (SQL/MED) + "HV000": "fdw_error", + "HV005": "fdw_column_name_not_found", + "HV002": "fdw_dynamic_parameter_value_needed", + "HV010": "fdw_function_sequence_error", + "HV021": "fdw_inconsistent_descriptor_information", + "HV024": "fdw_invalid_attribute_value", + "HV007": "fdw_invalid_column_name", + "HV008": "fdw_invalid_column_number", + "HV004": "fdw_invalid_data_type", + "HV006": "fdw_invalid_data_type_descriptors", + "HV091": "fdw_invalid_descriptor_field_identifier", + "HV00B": "fdw_invalid_handle", + "HV00C": "fdw_invalid_option_index", + "HV00D": "fdw_invalid_option_name", + "HV090": "fdw_invalid_string_length_or_buffer_length", + "HV00A": "fdw_invalid_string_format", + "HV009": "fdw_invalid_use_of_null_pointer", + "HV014": "fdw_too_many_handles", + "HV001": "fdw_out_of_memory", + "HV00P": "fdw_no_schemas", + "HV00J": "fdw_option_name_not_found", + "HV00K": "fdw_reply_handle", + "HV00Q": "fdw_schema_not_found", + "HV00R": "fdw_table_not_found", + "HV00L": "fdw_unable_to_create_execution", + "HV00M": "fdw_unable_to_create_reply", + "HV00N": "fdw_unable_to_establish_connection", + // Class P0 - PL/pgSQL Error + "P0000": "plpgsql_error", + "P0001": "raise_exception", + "P0002": "no_data_found", + "P0003": "too_many_rows", + // Class XX - Internal Error + "XX000": "internal_error", + "XX001": "data_corrupted", + "XX002": "index_corrupted", +} + +func parseError(r *readBuf) *Error { + err := new(Error) + for t := r.byte(); t != 0; t = r.byte() { + msg := r.string() + switch t { + case 'S': + err.Severity = msg + case 'C': + err.Code = ErrorCode(msg) + case 'M': + err.Message = msg + case 'D': + err.Detail = msg + case 'H': + err.Hint = msg + case 'P': + err.Position = msg + case 'p': + err.InternalPosition = msg + case 'q': + err.InternalQuery = msg + case 'W': + err.Where = msg + case 's': + err.Schema = msg + case 't': + err.Table = msg + case 'c': + err.Column = msg + case 'd': + err.DataTypeName = msg + case 'n': + err.Constraint = msg + case 'F': + err.File = msg + case 'L': + err.Line = msg + case 'R': + err.Routine = msg + } + } + return err +} + +// Fatal returns true if the Error Severity is fatal. +func (err *Error) Fatal() bool { + return err.Severity == Efatal +} + +// SQLState returns the SQLState of the error. +func (err *Error) SQLState() string { + return string(err.Code) +} + +// Get implements the legacy PGError interface. New code should use the fields +// of the Error struct directly. +func (err *Error) Get(k byte) (v string) { + switch k { + case 'S': + return err.Severity + case 'C': + return string(err.Code) + case 'M': + return err.Message + case 'D': + return err.Detail + case 'H': + return err.Hint + case 'P': + return err.Position + case 'p': + return err.InternalPosition + case 'q': + return err.InternalQuery + case 'W': + return err.Where + case 's': + return err.Schema + case 't': + return err.Table + case 'c': + return err.Column + case 'd': + return err.DataTypeName + case 'n': + return err.Constraint + case 'F': + return err.File + case 'L': + return err.Line + case 'R': + return err.Routine + } + return "" +} + +func (err *Error) Error() string { + return "pq: " + err.Message +} + +// PGError is an interface used by previous versions of pq. It is provided +// only to support legacy code. New code should use the Error type. +type PGError interface { + Error() string + Fatal() bool + Get(k byte) (v string) +} + +func errorf(s string, args ...interface{}) { + panic(fmt.Errorf("pq: %s", fmt.Sprintf(s, args...))) +} + +// TODO(ainar-g) Rename to errorf after removing panics. +func fmterrorf(s string, args ...interface{}) error { + return fmt.Errorf("pq: %s", fmt.Sprintf(s, args...)) +} + +func errRecoverNoErrBadConn(err *error) { + e := recover() + if e == nil { + // Do nothing + return + } + var ok bool + *err, ok = e.(error) + if !ok { + *err = fmt.Errorf("pq: unexpected error: %#v", e) + } +} + +func (cn *conn) errRecover(err *error) { + e := recover() + switch v := e.(type) { + case nil: + // Do nothing + case runtime.Error: + cn.err.set(driver.ErrBadConn) + panic(v) + case *Error: + if v.Fatal() { + *err = driver.ErrBadConn + } else { + *err = v + } + case *net.OpError: + cn.err.set(driver.ErrBadConn) + *err = v + case *safeRetryError: + cn.err.set(driver.ErrBadConn) + *err = driver.ErrBadConn + case error: + if v == io.EOF || v.Error() == "remote error: handshake failure" { + *err = driver.ErrBadConn + } else { + *err = v + } + + default: + cn.err.set(driver.ErrBadConn) + panic(fmt.Sprintf("unknown error: %#v", e)) + } + + // Any time we return ErrBadConn, we need to remember it since *Tx doesn't + // mark the connection bad in database/sql. + if *err == driver.ErrBadConn { + cn.err.set(driver.ErrBadConn) + } +} diff --git a/vendor/github.com/lib/pq/krb.go b/vendor/github.com/lib/pq/krb.go new file mode 100644 index 00000000000..408ec01f977 --- /dev/null +++ b/vendor/github.com/lib/pq/krb.go @@ -0,0 +1,27 @@ +package pq + +// NewGSSFunc creates a GSS authentication provider, for use with +// RegisterGSSProvider. +type NewGSSFunc func() (GSS, error) + +var newGss NewGSSFunc + +// RegisterGSSProvider registers a GSS authentication provider. For example, if +// you need to use Kerberos to authenticate with your server, add this to your +// main package: +// +// import "github.com/lib/pq/auth/kerberos" +// +// func init() { +// pq.RegisterGSSProvider(func() (pq.GSS, error) { return kerberos.NewGSS() }) +// } +func RegisterGSSProvider(newGssArg NewGSSFunc) { + newGss = newGssArg +} + +// GSS provides GSSAPI authentication (e.g., Kerberos). +type GSS interface { + GetInitToken(host string, service string) ([]byte, error) + GetInitTokenFromSpn(spn string) ([]byte, error) + Continue(inToken []byte) (done bool, outToken []byte, err error) +} diff --git a/vendor/github.com/lib/pq/notice.go b/vendor/github.com/lib/pq/notice.go new file mode 100644 index 00000000000..70ad122a7d6 --- /dev/null +++ b/vendor/github.com/lib/pq/notice.go @@ -0,0 +1,72 @@ +//go:build go1.10 +// +build go1.10 + +package pq + +import ( + "context" + "database/sql/driver" +) + +// NoticeHandler returns the notice handler on the given connection, if any. A +// runtime panic occurs if c is not a pq connection. This is rarely used +// directly, use ConnectorNoticeHandler and ConnectorWithNoticeHandler instead. +func NoticeHandler(c driver.Conn) func(*Error) { + return c.(*conn).noticeHandler +} + +// SetNoticeHandler sets the given notice handler on the given connection. A +// runtime panic occurs if c is not a pq connection. A nil handler may be used +// to unset it. This is rarely used directly, use ConnectorNoticeHandler and +// ConnectorWithNoticeHandler instead. +// +// Note: Notice handlers are executed synchronously by pq meaning commands +// won't continue to be processed until the handler returns. +func SetNoticeHandler(c driver.Conn, handler func(*Error)) { + c.(*conn).noticeHandler = handler +} + +// NoticeHandlerConnector wraps a regular connector and sets a notice handler +// on it. +type NoticeHandlerConnector struct { + driver.Connector + noticeHandler func(*Error) +} + +// Connect calls the underlying connector's connect method and then sets the +// notice handler. +func (n *NoticeHandlerConnector) Connect(ctx context.Context) (driver.Conn, error) { + c, err := n.Connector.Connect(ctx) + if err == nil { + SetNoticeHandler(c, n.noticeHandler) + } + return c, err +} + +// ConnectorNoticeHandler returns the currently set notice handler, if any. If +// the given connector is not a result of ConnectorWithNoticeHandler, nil is +// returned. +func ConnectorNoticeHandler(c driver.Connector) func(*Error) { + if c, ok := c.(*NoticeHandlerConnector); ok { + return c.noticeHandler + } + return nil +} + +// ConnectorWithNoticeHandler creates or sets the given handler for the given +// connector. If the given connector is a result of calling this function +// previously, it is simply set on the given connector and returned. Otherwise, +// this returns a new connector wrapping the given one and setting the notice +// handler. A nil notice handler may be used to unset it. +// +// The returned connector is intended to be used with database/sql.OpenDB. +// +// Note: Notice handlers are executed synchronously by pq meaning commands +// won't continue to be processed until the handler returns. +func ConnectorWithNoticeHandler(c driver.Connector, handler func(*Error)) *NoticeHandlerConnector { + if c, ok := c.(*NoticeHandlerConnector); ok { + c.noticeHandler = handler + return c + } + return &NoticeHandlerConnector{Connector: c, noticeHandler: handler} +} diff --git a/vendor/github.com/lib/pq/notify.go b/vendor/github.com/lib/pq/notify.go new file mode 100644 index 00000000000..5c421fdb8b5 --- /dev/null +++ b/vendor/github.com/lib/pq/notify.go @@ -0,0 +1,858 @@ +package pq + +// Package pq is a pure Go Postgres driver for the database/sql package. +// This module contains support for Postgres LISTEN/NOTIFY. + +import ( + "context" + "database/sql/driver" + "errors" + "fmt" + "sync" + "sync/atomic" + "time" +) + +// Notification represents a single notification from the database. +type Notification struct { + // Process ID (PID) of the notifying postgres backend. + BePid int + // Name of the channel the notification was sent on. + Channel string + // Payload, or the empty string if unspecified. + Extra string +} + +func recvNotification(r *readBuf) *Notification { + bePid := r.int32() + channel := r.string() + extra := r.string() + + return &Notification{bePid, channel, extra} +} + +// SetNotificationHandler sets the given notification handler on the given +// connection. A runtime panic occurs if c is not a pq connection. A nil handler +// may be used to unset it. +// +// Note: Notification handlers are executed synchronously by pq meaning commands +// won't continue to be processed until the handler returns. +func SetNotificationHandler(c driver.Conn, handler func(*Notification)) { + c.(*conn).notificationHandler = handler +} + +// NotificationHandlerConnector wraps a regular connector and sets a notification handler +// on it. +type NotificationHandlerConnector struct { + driver.Connector + notificationHandler func(*Notification) +} + +// Connect calls the underlying connector's connect method and then sets the +// notification handler. +func (n *NotificationHandlerConnector) Connect(ctx context.Context) (driver.Conn, error) { + c, err := n.Connector.Connect(ctx) + if err == nil { + SetNotificationHandler(c, n.notificationHandler) + } + return c, err +} + +// ConnectorNotificationHandler returns the currently set notification handler, if any. If +// the given connector is not a result of ConnectorWithNotificationHandler, nil is +// returned. +func ConnectorNotificationHandler(c driver.Connector) func(*Notification) { + if c, ok := c.(*NotificationHandlerConnector); ok { + return c.notificationHandler + } + return nil +} + +// ConnectorWithNotificationHandler creates or sets the given handler for the given +// connector. If the given connector is a result of calling this function +// previously, it is simply set on the given connector and returned. Otherwise, +// this returns a new connector wrapping the given one and setting the notification +// handler. A nil notification handler may be used to unset it. +// +// The returned connector is intended to be used with database/sql.OpenDB. +// +// Note: Notification handlers are executed synchronously by pq meaning commands +// won't continue to be processed until the handler returns. +func ConnectorWithNotificationHandler(c driver.Connector, handler func(*Notification)) *NotificationHandlerConnector { + if c, ok := c.(*NotificationHandlerConnector); ok { + c.notificationHandler = handler + return c + } + return &NotificationHandlerConnector{Connector: c, notificationHandler: handler} +} + +const ( + connStateIdle int32 = iota + connStateExpectResponse + connStateExpectReadyForQuery +) + +type message struct { + typ byte + err error +} + +var errListenerConnClosed = errors.New("pq: ListenerConn has been closed") + +// ListenerConn is a low-level interface for waiting for notifications. You +// should use Listener instead. +type ListenerConn struct { + // guards cn and err + connectionLock sync.Mutex + cn *conn + err error + + connState int32 + + // the sending goroutine will be holding this lock + senderLock sync.Mutex + + notificationChan chan<- *Notification + + replyChan chan message +} + +// NewListenerConn creates a new ListenerConn. Use NewListener instead. +func NewListenerConn(name string, notificationChan chan<- *Notification) (*ListenerConn, error) { + return newDialListenerConn(defaultDialer{}, name, notificationChan) +} + +func newDialListenerConn(d Dialer, name string, c chan<- *Notification) (*ListenerConn, error) { + cn, err := DialOpen(d, name) + if err != nil { + return nil, err + } + + l := &ListenerConn{ + cn: cn.(*conn), + notificationChan: c, + connState: connStateIdle, + replyChan: make(chan message, 2), + } + + go l.listenerConnMain() + + return l, nil +} + +// We can only allow one goroutine at a time to be running a query on the +// connection for various reasons, so the goroutine sending on the connection +// must be holding senderLock. +// +// Returns an error if an unrecoverable error has occurred and the ListenerConn +// should be abandoned. +func (l *ListenerConn) acquireSenderLock() error { + // we must acquire senderLock first to avoid deadlocks; see ExecSimpleQuery + l.senderLock.Lock() + + l.connectionLock.Lock() + err := l.err + l.connectionLock.Unlock() + if err != nil { + l.senderLock.Unlock() + return err + } + return nil +} + +func (l *ListenerConn) releaseSenderLock() { + l.senderLock.Unlock() +} + +// setState advances the protocol state to newState. Returns false if moving +// to that state from the current state is not allowed. +func (l *ListenerConn) setState(newState int32) bool { + var expectedState int32 + + switch newState { + case connStateIdle: + expectedState = connStateExpectReadyForQuery + case connStateExpectResponse: + expectedState = connStateIdle + case connStateExpectReadyForQuery: + expectedState = connStateExpectResponse + default: + panic(fmt.Sprintf("unexpected listenerConnState %d", newState)) + } + + return atomic.CompareAndSwapInt32(&l.connState, expectedState, newState) +} + +// Main logic is here: receive messages from the postgres backend, forward +// notifications and query replies and keep the internal state in sync with the +// protocol state. Returns when the connection has been lost, is about to go +// away or should be discarded because we couldn't agree on the state with the +// server backend. +func (l *ListenerConn) listenerConnLoop() (err error) { + defer errRecoverNoErrBadConn(&err) + + r := &readBuf{} + for { + t, err := l.cn.recvMessage(r) + if err != nil { + return err + } + + switch t { + case 'A': + // recvNotification copies all the data so we don't need to worry + // about the scratch buffer being overwritten. + l.notificationChan <- recvNotification(r) + + case 'T', 'D': + // only used by tests; ignore + + case 'E': + // We might receive an ErrorResponse even when not in a query; it + // is expected that the server will close the connection after + // that, but we should make sure that the error we display is the + // one from the stray ErrorResponse, not io.ErrUnexpectedEOF. + if !l.setState(connStateExpectReadyForQuery) { + return parseError(r) + } + l.replyChan <- message{t, parseError(r)} + + case 'C', 'I': + if !l.setState(connStateExpectReadyForQuery) { + // protocol out of sync + return fmt.Errorf("unexpected CommandComplete") + } + // ExecSimpleQuery doesn't need to know about this message + + case 'Z': + if !l.setState(connStateIdle) { + // protocol out of sync + return fmt.Errorf("unexpected ReadyForQuery") + } + l.replyChan <- message{t, nil} + + case 'S': + // ignore + case 'N': + if n := l.cn.noticeHandler; n != nil { + n(parseError(r)) + } + default: + return fmt.Errorf("unexpected message %q from server in listenerConnLoop", t) + } + } +} + +// This is the main routine for the goroutine receiving on the database +// connection. Most of the main logic is in listenerConnLoop. +func (l *ListenerConn) listenerConnMain() { + err := l.listenerConnLoop() + + // listenerConnLoop terminated; we're done, but we still have to clean up. + // Make sure nobody tries to start any new queries by making sure the err + // pointer is set. It is important that we do not overwrite its value; a + // connection could be closed by either this goroutine or one sending on + // the connection -- whoever closes the connection is assumed to have the + // more meaningful error message (as the other one will probably get + // net.errClosed), so that goroutine sets the error we expose while the + // other error is discarded. If the connection is lost while two + // goroutines are operating on the socket, it probably doesn't matter which + // error we expose so we don't try to do anything more complex. + l.connectionLock.Lock() + if l.err == nil { + l.err = err + } + l.cn.Close() + l.connectionLock.Unlock() + + // There might be a query in-flight; make sure nobody's waiting for a + // response to it, since there's not going to be one. + close(l.replyChan) + + // let the listener know we're done + close(l.notificationChan) + + // this ListenerConn is done +} + +// Listen sends a LISTEN query to the server. See ExecSimpleQuery. +func (l *ListenerConn) Listen(channel string) (bool, error) { + return l.ExecSimpleQuery("LISTEN " + QuoteIdentifier(channel)) +} + +// Unlisten sends an UNLISTEN query to the server. See ExecSimpleQuery. +func (l *ListenerConn) Unlisten(channel string) (bool, error) { + return l.ExecSimpleQuery("UNLISTEN " + QuoteIdentifier(channel)) +} + +// UnlistenAll sends an `UNLISTEN *` query to the server. See ExecSimpleQuery. +func (l *ListenerConn) UnlistenAll() (bool, error) { + return l.ExecSimpleQuery("UNLISTEN *") +} + +// Ping the remote server to make sure it's alive. Non-nil error means the +// connection has failed and should be abandoned. +func (l *ListenerConn) Ping() error { + sent, err := l.ExecSimpleQuery("") + if !sent { + return err + } + if err != nil { + // shouldn't happen + panic(err) + } + return nil +} + +// Attempt to send a query on the connection. Returns an error if sending the +// query failed, and the caller should initiate closure of this connection. +// The caller must be holding senderLock (see acquireSenderLock and +// releaseSenderLock). +func (l *ListenerConn) sendSimpleQuery(q string) (err error) { + defer errRecoverNoErrBadConn(&err) + + // must set connection state before sending the query + if !l.setState(connStateExpectResponse) { + panic("two queries running at the same time") + } + + // Can't use l.cn.writeBuf here because it uses the scratch buffer which + // might get overwritten by listenerConnLoop. + b := &writeBuf{ + buf: []byte("Q\x00\x00\x00\x00"), + pos: 1, + } + b.string(q) + l.cn.send(b) + + return nil +} + +// ExecSimpleQuery executes a "simple query" (i.e. one with no bindable +// parameters) on the connection. The possible return values are: +// 1) "executed" is true; the query was executed to completion on the +// database server. If the query failed, err will be set to the error +// returned by the database, otherwise err will be nil. +// 2) If "executed" is false, the query could not be executed on the remote +// server. err will be non-nil. +// +// After a call to ExecSimpleQuery has returned an executed=false value, the +// connection has either been closed or will be closed shortly thereafter, and +// all subsequently executed queries will return an error. +func (l *ListenerConn) ExecSimpleQuery(q string) (executed bool, err error) { + if err = l.acquireSenderLock(); err != nil { + return false, err + } + defer l.releaseSenderLock() + + err = l.sendSimpleQuery(q) + if err != nil { + // We can't know what state the protocol is in, so we need to abandon + // this connection. + l.connectionLock.Lock() + // Set the error pointer if it hasn't been set already; see + // listenerConnMain. + if l.err == nil { + l.err = err + } + l.connectionLock.Unlock() + l.cn.c.Close() + return false, err + } + + // now we just wait for a reply.. + for { + m, ok := <-l.replyChan + if !ok { + // We lost the connection to server, don't bother waiting for a + // a response. err should have been set already. + l.connectionLock.Lock() + err := l.err + l.connectionLock.Unlock() + return false, err + } + switch m.typ { + case 'Z': + // sanity check + if m.err != nil { + panic("m.err != nil") + } + // done; err might or might not be set + return true, err + + case 'E': + // sanity check + if m.err == nil { + panic("m.err == nil") + } + // server responded with an error; ReadyForQuery to follow + err = m.err + + default: + return false, fmt.Errorf("unknown response for simple query: %q", m.typ) + } + } +} + +// Close closes the connection. +func (l *ListenerConn) Close() error { + l.connectionLock.Lock() + if l.err != nil { + l.connectionLock.Unlock() + return errListenerConnClosed + } + l.err = errListenerConnClosed + l.connectionLock.Unlock() + // We can't send anything on the connection without holding senderLock. + // Simply close the net.Conn to wake up everyone operating on it. + return l.cn.c.Close() +} + +// Err returns the reason the connection was closed. It is not safe to call +// this function until l.Notify has been closed. +func (l *ListenerConn) Err() error { + return l.err +} + +var errListenerClosed = errors.New("pq: Listener has been closed") + +// ErrChannelAlreadyOpen is returned from Listen when a channel is already +// open. +var ErrChannelAlreadyOpen = errors.New("pq: channel is already open") + +// ErrChannelNotOpen is returned from Unlisten when a channel is not open. +var ErrChannelNotOpen = errors.New("pq: channel is not open") + +// ListenerEventType is an enumeration of listener event types. +type ListenerEventType int + +const ( + // ListenerEventConnected is emitted only when the database connection + // has been initially initialized. The err argument of the callback + // will always be nil. + ListenerEventConnected ListenerEventType = iota + + // ListenerEventDisconnected is emitted after a database connection has + // been lost, either because of an error or because Close has been + // called. The err argument will be set to the reason the database + // connection was lost. + ListenerEventDisconnected + + // ListenerEventReconnected is emitted after a database connection has + // been re-established after connection loss. The err argument of the + // callback will always be nil. After this event has been emitted, a + // nil pq.Notification is sent on the Listener.Notify channel. + ListenerEventReconnected + + // ListenerEventConnectionAttemptFailed is emitted after a connection + // to the database was attempted, but failed. The err argument will be + // set to an error describing why the connection attempt did not + // succeed. + ListenerEventConnectionAttemptFailed +) + +// EventCallbackType is the event callback type. See also ListenerEventType +// constants' documentation. +type EventCallbackType func(event ListenerEventType, err error) + +// Listener provides an interface for listening to notifications from a +// PostgreSQL database. For general usage information, see section +// "Notifications". +// +// Listener can safely be used from concurrently running goroutines. +type Listener struct { + // Channel for receiving notifications from the database. In some cases a + // nil value will be sent. See section "Notifications" above. + Notify chan *Notification + + name string + minReconnectInterval time.Duration + maxReconnectInterval time.Duration + dialer Dialer + eventCallback EventCallbackType + + lock sync.Mutex + isClosed bool + reconnectCond *sync.Cond + cn *ListenerConn + connNotificationChan <-chan *Notification + channels map[string]struct{} +} + +// NewListener creates a new database connection dedicated to LISTEN / NOTIFY. +// +// name should be set to a connection string to be used to establish the +// database connection (see section "Connection String Parameters" above). +// +// minReconnectInterval controls the duration to wait before trying to +// re-establish the database connection after connection loss. After each +// consecutive failure this interval is doubled, until maxReconnectInterval is +// reached. Successfully completing the connection establishment procedure +// resets the interval back to minReconnectInterval. +// +// The last parameter eventCallback can be set to a function which will be +// called by the Listener when the state of the underlying database connection +// changes. This callback will be called by the goroutine which dispatches the +// notifications over the Notify channel, so you should try to avoid doing +// potentially time-consuming operations from the callback. +func NewListener(name string, + minReconnectInterval time.Duration, + maxReconnectInterval time.Duration, + eventCallback EventCallbackType) *Listener { + return NewDialListener(defaultDialer{}, name, minReconnectInterval, maxReconnectInterval, eventCallback) +} + +// NewDialListener is like NewListener but it takes a Dialer. +func NewDialListener(d Dialer, + name string, + minReconnectInterval time.Duration, + maxReconnectInterval time.Duration, + eventCallback EventCallbackType) *Listener { + + l := &Listener{ + name: name, + minReconnectInterval: minReconnectInterval, + maxReconnectInterval: maxReconnectInterval, + dialer: d, + eventCallback: eventCallback, + + channels: make(map[string]struct{}), + + Notify: make(chan *Notification, 32), + } + l.reconnectCond = sync.NewCond(&l.lock) + + go l.listenerMain() + + return l +} + +// NotificationChannel returns the notification channel for this listener. +// This is the same channel as Notify, and will not be recreated during the +// life time of the Listener. +func (l *Listener) NotificationChannel() <-chan *Notification { + return l.Notify +} + +// Listen starts listening for notifications on a channel. Calls to this +// function will block until an acknowledgement has been received from the +// server. Note that Listener automatically re-establishes the connection +// after connection loss, so this function may block indefinitely if the +// connection can not be re-established. +// +// Listen will only fail in three conditions: +// 1) The channel is already open. The returned error will be +// ErrChannelAlreadyOpen. +// 2) The query was executed on the remote server, but PostgreSQL returned an +// error message in response to the query. The returned error will be a +// pq.Error containing the information the server supplied. +// 3) Close is called on the Listener before the request could be completed. +// +// The channel name is case-sensitive. +func (l *Listener) Listen(channel string) error { + l.lock.Lock() + defer l.lock.Unlock() + + if l.isClosed { + return errListenerClosed + } + + // The server allows you to issue a LISTEN on a channel which is already + // open, but it seems useful to be able to detect this case to spot for + // mistakes in application logic. If the application genuinely does't + // care, it can check the exported error and ignore it. + _, exists := l.channels[channel] + if exists { + return ErrChannelAlreadyOpen + } + + if l.cn != nil { + // If gotResponse is true but error is set, the query was executed on + // the remote server, but resulted in an error. This should be + // relatively rare, so it's fine if we just pass the error to our + // caller. However, if gotResponse is false, we could not complete the + // query on the remote server and our underlying connection is about + // to go away, so we only add relname to l.channels, and wait for + // resync() to take care of the rest. + gotResponse, err := l.cn.Listen(channel) + if gotResponse && err != nil { + return err + } + } + + l.channels[channel] = struct{}{} + for l.cn == nil { + l.reconnectCond.Wait() + // we let go of the mutex for a while + if l.isClosed { + return errListenerClosed + } + } + + return nil +} + +// Unlisten removes a channel from the Listener's channel list. Returns +// ErrChannelNotOpen if the Listener is not listening on the specified channel. +// Returns immediately with no error if there is no connection. Note that you +// might still get notifications for this channel even after Unlisten has +// returned. +// +// The channel name is case-sensitive. +func (l *Listener) Unlisten(channel string) error { + l.lock.Lock() + defer l.lock.Unlock() + + if l.isClosed { + return errListenerClosed + } + + // Similarly to LISTEN, this is not an error in Postgres, but it seems + // useful to distinguish from the normal conditions. + _, exists := l.channels[channel] + if !exists { + return ErrChannelNotOpen + } + + if l.cn != nil { + // Similarly to Listen (see comment in that function), the caller + // should only be bothered with an error if it came from the backend as + // a response to our query. + gotResponse, err := l.cn.Unlisten(channel) + if gotResponse && err != nil { + return err + } + } + + // Don't bother waiting for resync if there's no connection. + delete(l.channels, channel) + return nil +} + +// UnlistenAll removes all channels from the Listener's channel list. Returns +// immediately with no error if there is no connection. Note that you might +// still get notifications for any of the deleted channels even after +// UnlistenAll has returned. +func (l *Listener) UnlistenAll() error { + l.lock.Lock() + defer l.lock.Unlock() + + if l.isClosed { + return errListenerClosed + } + + if l.cn != nil { + // Similarly to Listen (see comment in that function), the caller + // should only be bothered with an error if it came from the backend as + // a response to our query. + gotResponse, err := l.cn.UnlistenAll() + if gotResponse && err != nil { + return err + } + } + + // Don't bother waiting for resync if there's no connection. + l.channels = make(map[string]struct{}) + return nil +} + +// Ping the remote server to make sure it's alive. Non-nil return value means +// that there is no active connection. +func (l *Listener) Ping() error { + l.lock.Lock() + defer l.lock.Unlock() + + if l.isClosed { + return errListenerClosed + } + if l.cn == nil { + return errors.New("no connection") + } + + return l.cn.Ping() +} + +// Clean up after losing the server connection. Returns l.cn.Err(), which +// should have the reason the connection was lost. +func (l *Listener) disconnectCleanup() error { + l.lock.Lock() + defer l.lock.Unlock() + + // sanity check; can't look at Err() until the channel has been closed + select { + case _, ok := <-l.connNotificationChan: + if ok { + panic("connNotificationChan not closed") + } + default: + panic("connNotificationChan not closed") + } + + err := l.cn.Err() + l.cn.Close() + l.cn = nil + return err +} + +// Synchronize the list of channels we want to be listening on with the server +// after the connection has been established. +func (l *Listener) resync(cn *ListenerConn, notificationChan <-chan *Notification) error { + doneChan := make(chan error) + go func(notificationChan <-chan *Notification) { + for channel := range l.channels { + // If we got a response, return that error to our caller as it's + // going to be more descriptive than cn.Err(). + gotResponse, err := cn.Listen(channel) + if gotResponse && err != nil { + doneChan <- err + return + } + + // If we couldn't reach the server, wait for notificationChan to + // close and then return the error message from the connection, as + // per ListenerConn's interface. + if err != nil { + for range notificationChan { + } + doneChan <- cn.Err() + return + } + } + doneChan <- nil + }(notificationChan) + + // Ignore notifications while synchronization is going on to avoid + // deadlocks. We have to send a nil notification over Notify anyway as + // we can't possibly know which notifications (if any) were lost while + // the connection was down, so there's no reason to try and process + // these messages at all. + for { + select { + case _, ok := <-notificationChan: + if !ok { + notificationChan = nil + } + + case err := <-doneChan: + return err + } + } +} + +// caller should NOT be holding l.lock +func (l *Listener) closed() bool { + l.lock.Lock() + defer l.lock.Unlock() + + return l.isClosed +} + +func (l *Listener) connect() error { + notificationChan := make(chan *Notification, 32) + cn, err := newDialListenerConn(l.dialer, l.name, notificationChan) + if err != nil { + return err + } + + l.lock.Lock() + defer l.lock.Unlock() + + err = l.resync(cn, notificationChan) + if err != nil { + cn.Close() + return err + } + + l.cn = cn + l.connNotificationChan = notificationChan + l.reconnectCond.Broadcast() + + return nil +} + +// Close disconnects the Listener from the database and shuts it down. +// Subsequent calls to its methods will return an error. Close returns an +// error if the connection has already been closed. +func (l *Listener) Close() error { + l.lock.Lock() + defer l.lock.Unlock() + + if l.isClosed { + return errListenerClosed + } + + if l.cn != nil { + l.cn.Close() + } + l.isClosed = true + + // Unblock calls to Listen() + l.reconnectCond.Broadcast() + + return nil +} + +func (l *Listener) emitEvent(event ListenerEventType, err error) { + if l.eventCallback != nil { + l.eventCallback(event, err) + } +} + +// Main logic here: maintain a connection to the server when possible, wait +// for notifications and emit events. +func (l *Listener) listenerConnLoop() { + var nextReconnect time.Time + + reconnectInterval := l.minReconnectInterval + for { + for { + err := l.connect() + if err == nil { + break + } + + if l.closed() { + return + } + l.emitEvent(ListenerEventConnectionAttemptFailed, err) + + time.Sleep(reconnectInterval) + reconnectInterval *= 2 + if reconnectInterval > l.maxReconnectInterval { + reconnectInterval = l.maxReconnectInterval + } + } + + if nextReconnect.IsZero() { + l.emitEvent(ListenerEventConnected, nil) + } else { + l.emitEvent(ListenerEventReconnected, nil) + l.Notify <- nil + } + + reconnectInterval = l.minReconnectInterval + nextReconnect = time.Now().Add(reconnectInterval) + + for { + notification, ok := <-l.connNotificationChan + if !ok { + // lost connection, loop again + break + } + l.Notify <- notification + } + + err := l.disconnectCleanup() + if l.closed() { + return + } + l.emitEvent(ListenerEventDisconnected, err) + + time.Sleep(time.Until(nextReconnect)) + } +} + +func (l *Listener) listenerMain() { + l.listenerConnLoop() + close(l.Notify) +} diff --git a/vendor/github.com/lib/pq/oid/doc.go b/vendor/github.com/lib/pq/oid/doc.go new file mode 100644 index 00000000000..caaede2489d --- /dev/null +++ b/vendor/github.com/lib/pq/oid/doc.go @@ -0,0 +1,6 @@ +// Package oid contains OID constants +// as defined by the Postgres server. +package oid + +// Oid is a Postgres Object ID. +type Oid uint32 diff --git a/vendor/github.com/lib/pq/oid/types.go b/vendor/github.com/lib/pq/oid/types.go new file mode 100644 index 00000000000..ecc84c2c862 --- /dev/null +++ b/vendor/github.com/lib/pq/oid/types.go @@ -0,0 +1,343 @@ +// Code generated by gen.go. DO NOT EDIT. + +package oid + +const ( + T_bool Oid = 16 + T_bytea Oid = 17 + T_char Oid = 18 + T_name Oid = 19 + T_int8 Oid = 20 + T_int2 Oid = 21 + T_int2vector Oid = 22 + T_int4 Oid = 23 + T_regproc Oid = 24 + T_text Oid = 25 + T_oid Oid = 26 + T_tid Oid = 27 + T_xid Oid = 28 + T_cid Oid = 29 + T_oidvector Oid = 30 + T_pg_ddl_command Oid = 32 + T_pg_type Oid = 71 + T_pg_attribute Oid = 75 + T_pg_proc Oid = 81 + T_pg_class Oid = 83 + T_json Oid = 114 + T_xml Oid = 142 + T__xml Oid = 143 + T_pg_node_tree Oid = 194 + T__json Oid = 199 + T_smgr Oid = 210 + T_index_am_handler Oid = 325 + T_point Oid = 600 + T_lseg Oid = 601 + T_path Oid = 602 + T_box Oid = 603 + T_polygon Oid = 604 + T_line Oid = 628 + T__line Oid = 629 + T_cidr Oid = 650 + T__cidr Oid = 651 + T_float4 Oid = 700 + T_float8 Oid = 701 + T_abstime Oid = 702 + T_reltime Oid = 703 + T_tinterval Oid = 704 + T_unknown Oid = 705 + T_circle Oid = 718 + T__circle Oid = 719 + T_money Oid = 790 + T__money Oid = 791 + T_macaddr Oid = 829 + T_inet Oid = 869 + T__bool Oid = 1000 + T__bytea Oid = 1001 + T__char Oid = 1002 + T__name Oid = 1003 + T__int2 Oid = 1005 + T__int2vector Oid = 1006 + T__int4 Oid = 1007 + T__regproc Oid = 1008 + T__text Oid = 1009 + T__tid Oid = 1010 + T__xid Oid = 1011 + T__cid Oid = 1012 + T__oidvector Oid = 1013 + T__bpchar Oid = 1014 + T__varchar Oid = 1015 + T__int8 Oid = 1016 + T__point Oid = 1017 + T__lseg Oid = 1018 + T__path Oid = 1019 + T__box Oid = 1020 + T__float4 Oid = 1021 + T__float8 Oid = 1022 + T__abstime Oid = 1023 + T__reltime Oid = 1024 + T__tinterval Oid = 1025 + T__polygon Oid = 1027 + T__oid Oid = 1028 + T_aclitem Oid = 1033 + T__aclitem Oid = 1034 + T__macaddr Oid = 1040 + T__inet Oid = 1041 + T_bpchar Oid = 1042 + T_varchar Oid = 1043 + T_date Oid = 1082 + T_time Oid = 1083 + T_timestamp Oid = 1114 + T__timestamp Oid = 1115 + T__date Oid = 1182 + T__time Oid = 1183 + T_timestamptz Oid = 1184 + T__timestamptz Oid = 1185 + T_interval Oid = 1186 + T__interval Oid = 1187 + T__numeric Oid = 1231 + T_pg_database Oid = 1248 + T__cstring Oid = 1263 + T_timetz Oid = 1266 + T__timetz Oid = 1270 + T_bit Oid = 1560 + T__bit Oid = 1561 + T_varbit Oid = 1562 + T__varbit Oid = 1563 + T_numeric Oid = 1700 + T_refcursor Oid = 1790 + T__refcursor Oid = 2201 + T_regprocedure Oid = 2202 + T_regoper Oid = 2203 + T_regoperator Oid = 2204 + T_regclass Oid = 2205 + T_regtype Oid = 2206 + T__regprocedure Oid = 2207 + T__regoper Oid = 2208 + T__regoperator Oid = 2209 + T__regclass Oid = 2210 + T__regtype Oid = 2211 + T_record Oid = 2249 + T_cstring Oid = 2275 + T_any Oid = 2276 + T_anyarray Oid = 2277 + T_void Oid = 2278 + T_trigger Oid = 2279 + T_language_handler Oid = 2280 + T_internal Oid = 2281 + T_opaque Oid = 2282 + T_anyelement Oid = 2283 + T__record Oid = 2287 + T_anynonarray Oid = 2776 + T_pg_authid Oid = 2842 + T_pg_auth_members Oid = 2843 + T__txid_snapshot Oid = 2949 + T_uuid Oid = 2950 + T__uuid Oid = 2951 + T_txid_snapshot Oid = 2970 + T_fdw_handler Oid = 3115 + T_pg_lsn Oid = 3220 + T__pg_lsn Oid = 3221 + T_tsm_handler Oid = 3310 + T_anyenum Oid = 3500 + T_tsvector Oid = 3614 + T_tsquery Oid = 3615 + T_gtsvector Oid = 3642 + T__tsvector Oid = 3643 + T__gtsvector Oid = 3644 + T__tsquery Oid = 3645 + T_regconfig Oid = 3734 + T__regconfig Oid = 3735 + T_regdictionary Oid = 3769 + T__regdictionary Oid = 3770 + T_jsonb Oid = 3802 + T__jsonb Oid = 3807 + T_anyrange Oid = 3831 + T_event_trigger Oid = 3838 + T_int4range Oid = 3904 + T__int4range Oid = 3905 + T_numrange Oid = 3906 + T__numrange Oid = 3907 + T_tsrange Oid = 3908 + T__tsrange Oid = 3909 + T_tstzrange Oid = 3910 + T__tstzrange Oid = 3911 + T_daterange Oid = 3912 + T__daterange Oid = 3913 + T_int8range Oid = 3926 + T__int8range Oid = 3927 + T_pg_shseclabel Oid = 4066 + T_regnamespace Oid = 4089 + T__regnamespace Oid = 4090 + T_regrole Oid = 4096 + T__regrole Oid = 4097 +) + +var TypeName = map[Oid]string{ + T_bool: "BOOL", + T_bytea: "BYTEA", + T_char: "CHAR", + T_name: "NAME", + T_int8: "INT8", + T_int2: "INT2", + T_int2vector: "INT2VECTOR", + T_int4: "INT4", + T_regproc: "REGPROC", + T_text: "TEXT", + T_oid: "OID", + T_tid: "TID", + T_xid: "XID", + T_cid: "CID", + T_oidvector: "OIDVECTOR", + T_pg_ddl_command: "PG_DDL_COMMAND", + T_pg_type: "PG_TYPE", + T_pg_attribute: "PG_ATTRIBUTE", + T_pg_proc: "PG_PROC", + T_pg_class: "PG_CLASS", + T_json: "JSON", + T_xml: "XML", + T__xml: "_XML", + T_pg_node_tree: "PG_NODE_TREE", + T__json: "_JSON", + T_smgr: "SMGR", + T_index_am_handler: "INDEX_AM_HANDLER", + T_point: "POINT", + T_lseg: "LSEG", + T_path: "PATH", + T_box: "BOX", + T_polygon: "POLYGON", + T_line: "LINE", + T__line: "_LINE", + T_cidr: "CIDR", + T__cidr: "_CIDR", + T_float4: "FLOAT4", + T_float8: "FLOAT8", + T_abstime: "ABSTIME", + T_reltime: "RELTIME", + T_tinterval: "TINTERVAL", + T_unknown: "UNKNOWN", + T_circle: "CIRCLE", + T__circle: "_CIRCLE", + T_money: "MONEY", + T__money: "_MONEY", + T_macaddr: "MACADDR", + T_inet: "INET", + T__bool: "_BOOL", + T__bytea: "_BYTEA", + T__char: "_CHAR", + T__name: "_NAME", + T__int2: "_INT2", + T__int2vector: "_INT2VECTOR", + T__int4: "_INT4", + T__regproc: "_REGPROC", + T__text: "_TEXT", + T__tid: "_TID", + T__xid: "_XID", + T__cid: "_CID", + T__oidvector: "_OIDVECTOR", + T__bpchar: "_BPCHAR", + T__varchar: "_VARCHAR", + T__int8: "_INT8", + T__point: "_POINT", + T__lseg: "_LSEG", + T__path: "_PATH", + T__box: "_BOX", + T__float4: "_FLOAT4", + T__float8: "_FLOAT8", + T__abstime: "_ABSTIME", + T__reltime: "_RELTIME", + T__tinterval: "_TINTERVAL", + T__polygon: "_POLYGON", + T__oid: "_OID", + T_aclitem: "ACLITEM", + T__aclitem: "_ACLITEM", + T__macaddr: "_MACADDR", + T__inet: "_INET", + T_bpchar: "BPCHAR", + T_varchar: "VARCHAR", + T_date: "DATE", + T_time: "TIME", + T_timestamp: "TIMESTAMP", + T__timestamp: "_TIMESTAMP", + T__date: "_DATE", + T__time: "_TIME", + T_timestamptz: "TIMESTAMPTZ", + T__timestamptz: "_TIMESTAMPTZ", + T_interval: "INTERVAL", + T__interval: "_INTERVAL", + T__numeric: "_NUMERIC", + T_pg_database: "PG_DATABASE", + T__cstring: "_CSTRING", + T_timetz: "TIMETZ", + T__timetz: "_TIMETZ", + T_bit: "BIT", + T__bit: "_BIT", + T_varbit: "VARBIT", + T__varbit: "_VARBIT", + T_numeric: "NUMERIC", + T_refcursor: "REFCURSOR", + T__refcursor: "_REFCURSOR", + T_regprocedure: "REGPROCEDURE", + T_regoper: "REGOPER", + T_regoperator: "REGOPERATOR", + T_regclass: "REGCLASS", + T_regtype: "REGTYPE", + T__regprocedure: "_REGPROCEDURE", + T__regoper: "_REGOPER", + T__regoperator: "_REGOPERATOR", + T__regclass: "_REGCLASS", + T__regtype: "_REGTYPE", + T_record: "RECORD", + T_cstring: "CSTRING", + T_any: "ANY", + T_anyarray: "ANYARRAY", + T_void: "VOID", + T_trigger: "TRIGGER", + T_language_handler: "LANGUAGE_HANDLER", + T_internal: "INTERNAL", + T_opaque: "OPAQUE", + T_anyelement: "ANYELEMENT", + T__record: "_RECORD", + T_anynonarray: "ANYNONARRAY", + T_pg_authid: "PG_AUTHID", + T_pg_auth_members: "PG_AUTH_MEMBERS", + T__txid_snapshot: "_TXID_SNAPSHOT", + T_uuid: "UUID", + T__uuid: "_UUID", + T_txid_snapshot: "TXID_SNAPSHOT", + T_fdw_handler: "FDW_HANDLER", + T_pg_lsn: "PG_LSN", + T__pg_lsn: "_PG_LSN", + T_tsm_handler: "TSM_HANDLER", + T_anyenum: "ANYENUM", + T_tsvector: "TSVECTOR", + T_tsquery: "TSQUERY", + T_gtsvector: "GTSVECTOR", + T__tsvector: "_TSVECTOR", + T__gtsvector: "_GTSVECTOR", + T__tsquery: "_TSQUERY", + T_regconfig: "REGCONFIG", + T__regconfig: "_REGCONFIG", + T_regdictionary: "REGDICTIONARY", + T__regdictionary: "_REGDICTIONARY", + T_jsonb: "JSONB", + T__jsonb: "_JSONB", + T_anyrange: "ANYRANGE", + T_event_trigger: "EVENT_TRIGGER", + T_int4range: "INT4RANGE", + T__int4range: "_INT4RANGE", + T_numrange: "NUMRANGE", + T__numrange: "_NUMRANGE", + T_tsrange: "TSRANGE", + T__tsrange: "_TSRANGE", + T_tstzrange: "TSTZRANGE", + T__tstzrange: "_TSTZRANGE", + T_daterange: "DATERANGE", + T__daterange: "_DATERANGE", + T_int8range: "INT8RANGE", + T__int8range: "_INT8RANGE", + T_pg_shseclabel: "PG_SHSECLABEL", + T_regnamespace: "REGNAMESPACE", + T__regnamespace: "_REGNAMESPACE", + T_regrole: "REGROLE", + T__regrole: "_REGROLE", +} diff --git a/vendor/github.com/lib/pq/rows.go b/vendor/github.com/lib/pq/rows.go new file mode 100644 index 00000000000..c6aa5b9a36a --- /dev/null +++ b/vendor/github.com/lib/pq/rows.go @@ -0,0 +1,93 @@ +package pq + +import ( + "math" + "reflect" + "time" + + "github.com/lib/pq/oid" +) + +const headerSize = 4 + +type fieldDesc struct { + // The object ID of the data type. + OID oid.Oid + // The data type size (see pg_type.typlen). + // Note that negative values denote variable-width types. + Len int + // The type modifier (see pg_attribute.atttypmod). + // The meaning of the modifier is type-specific. + Mod int +} + +func (fd fieldDesc) Type() reflect.Type { + switch fd.OID { + case oid.T_int8: + return reflect.TypeOf(int64(0)) + case oid.T_int4: + return reflect.TypeOf(int32(0)) + case oid.T_int2: + return reflect.TypeOf(int16(0)) + case oid.T_varchar, oid.T_text: + return reflect.TypeOf("") + case oid.T_bool: + return reflect.TypeOf(false) + case oid.T_date, oid.T_time, oid.T_timetz, oid.T_timestamp, oid.T_timestamptz: + return reflect.TypeOf(time.Time{}) + case oid.T_bytea: + return reflect.TypeOf([]byte(nil)) + default: + return reflect.TypeOf(new(interface{})).Elem() + } +} + +func (fd fieldDesc) Name() string { + return oid.TypeName[fd.OID] +} + +func (fd fieldDesc) Length() (length int64, ok bool) { + switch fd.OID { + case oid.T_text, oid.T_bytea: + return math.MaxInt64, true + case oid.T_varchar, oid.T_bpchar: + return int64(fd.Mod - headerSize), true + default: + return 0, false + } +} + +func (fd fieldDesc) PrecisionScale() (precision, scale int64, ok bool) { + switch fd.OID { + case oid.T_numeric, oid.T__numeric: + mod := fd.Mod - headerSize + precision = int64((mod >> 16) & 0xffff) + scale = int64(mod & 0xffff) + return precision, scale, true + default: + return 0, 0, false + } +} + +// ColumnTypeScanType returns the value type that can be used to scan types into. +func (rs *rows) ColumnTypeScanType(index int) reflect.Type { + return rs.colTyps[index].Type() +} + +// ColumnTypeDatabaseTypeName return the database system type name. +func (rs *rows) ColumnTypeDatabaseTypeName(index int) string { + return rs.colTyps[index].Name() +} + +// ColumnTypeLength returns the length of the column type if the column is a +// variable length type. If the column is not a variable length type ok +// should return false. +func (rs *rows) ColumnTypeLength(index int) (length int64, ok bool) { + return rs.colTyps[index].Length() +} + +// ColumnTypePrecisionScale should return the precision and scale for decimal +// types. If not applicable, ok should be false. +func (rs *rows) ColumnTypePrecisionScale(index int) (precision, scale int64, ok bool) { + return rs.colTyps[index].PrecisionScale() +} diff --git a/vendor/github.com/lib/pq/scram/scram.go b/vendor/github.com/lib/pq/scram/scram.go new file mode 100644 index 00000000000..477216b6008 --- /dev/null +++ b/vendor/github.com/lib/pq/scram/scram.go @@ -0,0 +1,264 @@ +// Copyright (c) 2014 - Gustavo Niemeyer +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// 2. 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. +// +// 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. + +// Package scram implements a SCRAM-{SHA-1,etc} client per RFC5802. +// +// http://tools.ietf.org/html/rfc5802 +// +package scram + +import ( + "bytes" + "crypto/hmac" + "crypto/rand" + "encoding/base64" + "fmt" + "hash" + "strconv" + "strings" +) + +// Client implements a SCRAM-* client (SCRAM-SHA-1, SCRAM-SHA-256, etc). +// +// A Client may be used within a SASL conversation with logic resembling: +// +// var in []byte +// var client = scram.NewClient(sha1.New, user, pass) +// for client.Step(in) { +// out := client.Out() +// // send out to server +// in := serverOut +// } +// if client.Err() != nil { +// // auth failed +// } +// +type Client struct { + newHash func() hash.Hash + + user string + pass string + step int + out bytes.Buffer + err error + + clientNonce []byte + serverNonce []byte + saltedPass []byte + authMsg bytes.Buffer +} + +// NewClient returns a new SCRAM-* client with the provided hash algorithm. +// +// For SCRAM-SHA-256, for example, use: +// +// client := scram.NewClient(sha256.New, user, pass) +// +func NewClient(newHash func() hash.Hash, user, pass string) *Client { + c := &Client{ + newHash: newHash, + user: user, + pass: pass, + } + c.out.Grow(256) + c.authMsg.Grow(256) + return c +} + +// Out returns the data to be sent to the server in the current step. +func (c *Client) Out() []byte { + if c.out.Len() == 0 { + return nil + } + return c.out.Bytes() +} + +// Err returns the error that occurred, or nil if there were no errors. +func (c *Client) Err() error { + return c.err +} + +// SetNonce sets the client nonce to the provided value. +// If not set, the nonce is generated automatically out of crypto/rand on the first step. +func (c *Client) SetNonce(nonce []byte) { + c.clientNonce = nonce +} + +var escaper = strings.NewReplacer("=", "=3D", ",", "=2C") + +// Step processes the incoming data from the server and makes the +// next round of data for the server available via Client.Out. +// Step returns false if there are no errors and more data is +// still expected. +func (c *Client) Step(in []byte) bool { + c.out.Reset() + if c.step > 2 || c.err != nil { + return false + } + c.step++ + switch c.step { + case 1: + c.err = c.step1(in) + case 2: + c.err = c.step2(in) + case 3: + c.err = c.step3(in) + } + return c.step > 2 || c.err != nil +} + +func (c *Client) step1(in []byte) error { + if len(c.clientNonce) == 0 { + const nonceLen = 16 + buf := make([]byte, nonceLen+b64.EncodedLen(nonceLen)) + if _, err := rand.Read(buf[:nonceLen]); err != nil { + return fmt.Errorf("cannot read random SCRAM-SHA-256 nonce from operating system: %v", err) + } + c.clientNonce = buf[nonceLen:] + b64.Encode(c.clientNonce, buf[:nonceLen]) + } + c.authMsg.WriteString("n=") + escaper.WriteString(&c.authMsg, c.user) + c.authMsg.WriteString(",r=") + c.authMsg.Write(c.clientNonce) + + c.out.WriteString("n,,") + c.out.Write(c.authMsg.Bytes()) + return nil +} + +var b64 = base64.StdEncoding + +func (c *Client) step2(in []byte) error { + c.authMsg.WriteByte(',') + c.authMsg.Write(in) + + fields := bytes.Split(in, []byte(",")) + if len(fields) != 3 { + return fmt.Errorf("expected 3 fields in first SCRAM-SHA-256 server message, got %d: %q", len(fields), in) + } + if !bytes.HasPrefix(fields[0], []byte("r=")) || len(fields[0]) < 2 { + return fmt.Errorf("server sent an invalid SCRAM-SHA-256 nonce: %q", fields[0]) + } + if !bytes.HasPrefix(fields[1], []byte("s=")) || len(fields[1]) < 6 { + return fmt.Errorf("server sent an invalid SCRAM-SHA-256 salt: %q", fields[1]) + } + if !bytes.HasPrefix(fields[2], []byte("i=")) || len(fields[2]) < 6 { + return fmt.Errorf("server sent an invalid SCRAM-SHA-256 iteration count: %q", fields[2]) + } + + c.serverNonce = fields[0][2:] + if !bytes.HasPrefix(c.serverNonce, c.clientNonce) { + return fmt.Errorf("server SCRAM-SHA-256 nonce is not prefixed by client nonce: got %q, want %q+\"...\"", c.serverNonce, c.clientNonce) + } + + salt := make([]byte, b64.DecodedLen(len(fields[1][2:]))) + n, err := b64.Decode(salt, fields[1][2:]) + if err != nil { + return fmt.Errorf("cannot decode SCRAM-SHA-256 salt sent by server: %q", fields[1]) + } + salt = salt[:n] + iterCount, err := strconv.Atoi(string(fields[2][2:])) + if err != nil { + return fmt.Errorf("server sent an invalid SCRAM-SHA-256 iteration count: %q", fields[2]) + } + c.saltPassword(salt, iterCount) + + c.authMsg.WriteString(",c=biws,r=") + c.authMsg.Write(c.serverNonce) + + c.out.WriteString("c=biws,r=") + c.out.Write(c.serverNonce) + c.out.WriteString(",p=") + c.out.Write(c.clientProof()) + return nil +} + +func (c *Client) step3(in []byte) error { + var isv, ise bool + var fields = bytes.Split(in, []byte(",")) + if len(fields) == 1 { + isv = bytes.HasPrefix(fields[0], []byte("v=")) + ise = bytes.HasPrefix(fields[0], []byte("e=")) + } + if ise { + return fmt.Errorf("SCRAM-SHA-256 authentication error: %s", fields[0][2:]) + } else if !isv { + return fmt.Errorf("unsupported SCRAM-SHA-256 final message from server: %q", in) + } + if !bytes.Equal(c.serverSignature(), fields[0][2:]) { + return fmt.Errorf("cannot authenticate SCRAM-SHA-256 server signature: %q", fields[0][2:]) + } + return nil +} + +func (c *Client) saltPassword(salt []byte, iterCount int) { + mac := hmac.New(c.newHash, []byte(c.pass)) + mac.Write(salt) + mac.Write([]byte{0, 0, 0, 1}) + ui := mac.Sum(nil) + hi := make([]byte, len(ui)) + copy(hi, ui) + for i := 1; i < iterCount; i++ { + mac.Reset() + mac.Write(ui) + mac.Sum(ui[:0]) + for j, b := range ui { + hi[j] ^= b + } + } + c.saltedPass = hi +} + +func (c *Client) clientProof() []byte { + mac := hmac.New(c.newHash, c.saltedPass) + mac.Write([]byte("Client Key")) + clientKey := mac.Sum(nil) + hash := c.newHash() + hash.Write(clientKey) + storedKey := hash.Sum(nil) + mac = hmac.New(c.newHash, storedKey) + mac.Write(c.authMsg.Bytes()) + clientProof := mac.Sum(nil) + for i, b := range clientKey { + clientProof[i] ^= b + } + clientProof64 := make([]byte, b64.EncodedLen(len(clientProof))) + b64.Encode(clientProof64, clientProof) + return clientProof64 +} + +func (c *Client) serverSignature() []byte { + mac := hmac.New(c.newHash, c.saltedPass) + mac.Write([]byte("Server Key")) + serverKey := mac.Sum(nil) + + mac = hmac.New(c.newHash, serverKey) + mac.Write(c.authMsg.Bytes()) + serverSignature := mac.Sum(nil) + + encoded := make([]byte, b64.EncodedLen(len(serverSignature))) + b64.Encode(encoded, serverSignature) + return encoded +} diff --git a/vendor/github.com/lib/pq/ssl.go b/vendor/github.com/lib/pq/ssl.go new file mode 100644 index 00000000000..36b61ba45be --- /dev/null +++ b/vendor/github.com/lib/pq/ssl.go @@ -0,0 +1,204 @@ +package pq + +import ( + "crypto/tls" + "crypto/x509" + "io/ioutil" + "net" + "os" + "os/user" + "path/filepath" + "strings" +) + +// ssl generates a function to upgrade a net.Conn based on the "sslmode" and +// related settings. The function is nil when no upgrade should take place. +func ssl(o values) (func(net.Conn) (net.Conn, error), error) { + verifyCaOnly := false + tlsConf := tls.Config{} + switch mode := o["sslmode"]; mode { + // "require" is the default. + case "", "require": + // We must skip TLS's own verification since it requires full + // verification since Go 1.3. + tlsConf.InsecureSkipVerify = true + + // From http://www.postgresql.org/docs/current/static/libpq-ssl.html: + // + // Note: For backwards compatibility with earlier versions of + // PostgreSQL, if a root CA file exists, the behavior of + // sslmode=require will be the same as that of verify-ca, meaning the + // server certificate is validated against the CA. Relying on this + // behavior is discouraged, and applications that need certificate + // validation should always use verify-ca or verify-full. + if sslrootcert, ok := o["sslrootcert"]; ok { + if _, err := os.Stat(sslrootcert); err == nil { + verifyCaOnly = true + } else { + delete(o, "sslrootcert") + } + } + case "verify-ca": + // We must skip TLS's own verification since it requires full + // verification since Go 1.3. + tlsConf.InsecureSkipVerify = true + verifyCaOnly = true + case "verify-full": + tlsConf.ServerName = o["host"] + case "disable": + return nil, nil + default: + return nil, fmterrorf(`unsupported sslmode %q; only "require" (default), "verify-full", "verify-ca", and "disable" supported`, mode) + } + + // Set Server Name Indication (SNI), if enabled by connection parameters. + // By default SNI is on, any value which is not starting with "1" disables + // SNI -- that is the same check vanilla libpq uses. + if sslsni := o["sslsni"]; sslsni == "" || strings.HasPrefix(sslsni, "1") { + // RFC 6066 asks to not set SNI if the host is a literal IP address (IPv4 + // or IPv6). This check is coded already crypto.tls.hostnameInSNI, so + // just always set ServerName here and let crypto/tls do the filtering. + tlsConf.ServerName = o["host"] + } + + err := sslClientCertificates(&tlsConf, o) + if err != nil { + return nil, err + } + err = sslCertificateAuthority(&tlsConf, o) + if err != nil { + return nil, err + } + + // Accept renegotiation requests initiated by the backend. + // + // Renegotiation was deprecated then removed from PostgreSQL 9.5, but + // the default configuration of older versions has it enabled. Redshift + // also initiates renegotiations and cannot be reconfigured. + tlsConf.Renegotiation = tls.RenegotiateFreelyAsClient + + return func(conn net.Conn) (net.Conn, error) { + client := tls.Client(conn, &tlsConf) + if verifyCaOnly { + err := sslVerifyCertificateAuthority(client, &tlsConf) + if err != nil { + return nil, err + } + } + return client, nil + }, nil +} + +// sslClientCertificates adds the certificate specified in the "sslcert" and +// "sslkey" settings, or if they aren't set, from the .postgresql directory +// in the user's home directory. The configured files must exist and have +// the correct permissions. +func sslClientCertificates(tlsConf *tls.Config, o values) error { + sslinline := o["sslinline"] + if sslinline == "true" { + cert, err := tls.X509KeyPair([]byte(o["sslcert"]), []byte(o["sslkey"])) + if err != nil { + return err + } + tlsConf.Certificates = []tls.Certificate{cert} + return nil + } + + // user.Current() might fail when cross-compiling. We have to ignore the + // error and continue without home directory defaults, since we wouldn't + // know from where to load them. + user, _ := user.Current() + + // In libpq, the client certificate is only loaded if the setting is not blank. + // + // https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L1036-L1037 + sslcert := o["sslcert"] + if len(sslcert) == 0 && user != nil { + sslcert = filepath.Join(user.HomeDir, ".postgresql", "postgresql.crt") + } + // https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L1045 + if len(sslcert) == 0 { + return nil + } + // https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L1050:L1054 + if _, err := os.Stat(sslcert); os.IsNotExist(err) { + return nil + } else if err != nil { + return err + } + + // In libpq, the ssl key is only loaded if the setting is not blank. + // + // https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L1123-L1222 + sslkey := o["sslkey"] + if len(sslkey) == 0 && user != nil { + sslkey = filepath.Join(user.HomeDir, ".postgresql", "postgresql.key") + } + + if len(sslkey) > 0 { + if err := sslKeyPermissions(sslkey); err != nil { + return err + } + } + + cert, err := tls.LoadX509KeyPair(sslcert, sslkey) + if err != nil { + return err + } + + tlsConf.Certificates = []tls.Certificate{cert} + return nil +} + +// sslCertificateAuthority adds the RootCA specified in the "sslrootcert" setting. +func sslCertificateAuthority(tlsConf *tls.Config, o values) error { + // In libpq, the root certificate is only loaded if the setting is not blank. + // + // https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L950-L951 + if sslrootcert := o["sslrootcert"]; len(sslrootcert) > 0 { + tlsConf.RootCAs = x509.NewCertPool() + + sslinline := o["sslinline"] + + var cert []byte + if sslinline == "true" { + cert = []byte(sslrootcert) + } else { + var err error + cert, err = ioutil.ReadFile(sslrootcert) + if err != nil { + return err + } + } + + if !tlsConf.RootCAs.AppendCertsFromPEM(cert) { + return fmterrorf("couldn't parse pem in sslrootcert") + } + } + + return nil +} + +// sslVerifyCertificateAuthority carries out a TLS handshake to the server and +// verifies the presented certificate against the CA, i.e. the one specified in +// sslrootcert or the system CA if sslrootcert was not specified. +func sslVerifyCertificateAuthority(client *tls.Conn, tlsConf *tls.Config) error { + err := client.Handshake() + if err != nil { + return err + } + certs := client.ConnectionState().PeerCertificates + opts := x509.VerifyOptions{ + DNSName: client.ConnectionState().ServerName, + Intermediates: x509.NewCertPool(), + Roots: tlsConf.RootCAs, + } + for i, cert := range certs { + if i == 0 { + continue + } + opts.Intermediates.AddCert(cert) + } + _, err = certs[0].Verify(opts) + return err +} diff --git a/vendor/github.com/lib/pq/ssl_permissions.go b/vendor/github.com/lib/pq/ssl_permissions.go new file mode 100644 index 00000000000..d587f102edb --- /dev/null +++ b/vendor/github.com/lib/pq/ssl_permissions.go @@ -0,0 +1,93 @@ +//go:build !windows +// +build !windows + +package pq + +import ( + "errors" + "os" + "syscall" +) + +const ( + rootUserID = uint32(0) + + // The maximum permissions that a private key file owned by a regular user + // is allowed to have. This translates to u=rw. + maxUserOwnedKeyPermissions os.FileMode = 0600 + + // The maximum permissions that a private key file owned by root is allowed + // to have. This translates to u=rw,g=r. + maxRootOwnedKeyPermissions os.FileMode = 0640 +) + +var ( + errSSLKeyHasUnacceptableUserPermissions = errors.New("permissions for files not owned by root should be u=rw (0600) or less") + errSSLKeyHasUnacceptableRootPermissions = errors.New("permissions for root owned files should be u=rw,g=r (0640) or less") +) + +// sslKeyPermissions checks the permissions on user-supplied ssl key files. +// The key file should have very little access. +// +// libpq does not check key file permissions on Windows. +func sslKeyPermissions(sslkey string) error { + info, err := os.Stat(sslkey) + if err != nil { + return err + } + + err = hasCorrectPermissions(info) + + // return ErrSSLKeyHasWorldPermissions for backwards compatability with + // existing code. + if err == errSSLKeyHasUnacceptableUserPermissions || err == errSSLKeyHasUnacceptableRootPermissions { + err = ErrSSLKeyHasWorldPermissions + } + return err +} + +// hasCorrectPermissions checks the file info (and the unix-specific stat_t +// output) to verify that the permissions on the file are correct. +// +// If the file is owned by the same user the process is running as, +// the file should only have 0600 (u=rw). If the file is owned by root, +// and the group matches the group that the process is running in, the +// permissions cannot be more than 0640 (u=rw,g=r). The file should +// never have world permissions. +// +// Returns an error when the permission check fails. +func hasCorrectPermissions(info os.FileInfo) error { + // if file's permission matches 0600, allow access. + userPermissionMask := (os.FileMode(0777) ^ maxUserOwnedKeyPermissions) + + // regardless of if we're running as root or not, 0600 is acceptable, + // so we return if we match the regular user permission mask. + if info.Mode().Perm()&userPermissionMask == 0 { + return nil + } + + // We need to pull the Unix file information to get the file's owner. + // If we can't access it, there's some sort of operating system level error + // and we should fail rather than attempting to use faulty information. + sysInfo := info.Sys() + if sysInfo == nil { + return ErrSSLKeyUnknownOwnership + } + + unixStat, ok := sysInfo.(*syscall.Stat_t) + if !ok { + return ErrSSLKeyUnknownOwnership + } + + // if the file is owned by root, we allow 0640 (u=rw,g=r) to match what + // Postgres does. + if unixStat.Uid == rootUserID { + rootPermissionMask := (os.FileMode(0777) ^ maxRootOwnedKeyPermissions) + if info.Mode().Perm()&rootPermissionMask != 0 { + return errSSLKeyHasUnacceptableRootPermissions + } + return nil + } + + return errSSLKeyHasUnacceptableUserPermissions +} diff --git a/vendor/github.com/lib/pq/ssl_windows.go b/vendor/github.com/lib/pq/ssl_windows.go new file mode 100644 index 00000000000..73663c8f157 --- /dev/null +++ b/vendor/github.com/lib/pq/ssl_windows.go @@ -0,0 +1,10 @@ +//go:build windows +// +build windows + +package pq + +// sslKeyPermissions checks the permissions on user-supplied ssl key files. +// The key file should have very little access. +// +// libpq does not check key file permissions on Windows. +func sslKeyPermissions(string) error { return nil } diff --git a/vendor/github.com/lib/pq/url.go b/vendor/github.com/lib/pq/url.go new file mode 100644 index 00000000000..aec6e95be8b --- /dev/null +++ b/vendor/github.com/lib/pq/url.go @@ -0,0 +1,76 @@ +package pq + +import ( + "fmt" + "net" + nurl "net/url" + "sort" + "strings" +) + +// ParseURL no longer needs to be used by clients of this library since supplying a URL as a +// connection string to sql.Open() is now supported: +// +// sql.Open("postgres", "postgres://bob:secret@1.2.3.4:5432/mydb?sslmode=verify-full") +// +// It remains exported here for backwards-compatibility. +// +// ParseURL converts a url to a connection string for driver.Open. +// Example: +// +// "postgres://bob:secret@1.2.3.4:5432/mydb?sslmode=verify-full" +// +// converts to: +// +// "user=bob password=secret host=1.2.3.4 port=5432 dbname=mydb sslmode=verify-full" +// +// A minimal example: +// +// "postgres://" +// +// This will be blank, causing driver.Open to use all of the defaults +func ParseURL(url string) (string, error) { + u, err := nurl.Parse(url) + if err != nil { + return "", err + } + + if u.Scheme != "postgres" && u.Scheme != "postgresql" { + return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme) + } + + var kvs []string + escaper := strings.NewReplacer(`'`, `\'`, `\`, `\\`) + accrue := func(k, v string) { + if v != "" { + kvs = append(kvs, k+"='"+escaper.Replace(v)+"'") + } + } + + if u.User != nil { + v := u.User.Username() + accrue("user", v) + + v, _ = u.User.Password() + accrue("password", v) + } + + if host, port, err := net.SplitHostPort(u.Host); err != nil { + accrue("host", u.Host) + } else { + accrue("host", host) + accrue("port", port) + } + + if u.Path != "" { + accrue("dbname", u.Path[1:]) + } + + q := u.Query() + for k := range q { + accrue(k, q.Get(k)) + } + + sort.Strings(kvs) // Makes testing easier (not a performance concern) + return strings.Join(kvs, " "), nil +} diff --git a/vendor/github.com/lib/pq/user_other.go b/vendor/github.com/lib/pq/user_other.go new file mode 100644 index 00000000000..3dae8f5572b --- /dev/null +++ b/vendor/github.com/lib/pq/user_other.go @@ -0,0 +1,10 @@ +// Package pq is a pure Go Postgres driver for the database/sql package. + +//go:build js || android || hurd || zos +// +build js android hurd zos + +package pq + +func userCurrent() (string, error) { + return "", ErrCouldNotDetectUsername +} diff --git a/vendor/github.com/lib/pq/user_posix.go b/vendor/github.com/lib/pq/user_posix.go new file mode 100644 index 00000000000..5f2d439bc42 --- /dev/null +++ b/vendor/github.com/lib/pq/user_posix.go @@ -0,0 +1,25 @@ +// Package pq is a pure Go Postgres driver for the database/sql package. + +//go:build aix || darwin || dragonfly || freebsd || (linux && !android) || nacl || netbsd || openbsd || plan9 || solaris || rumprun || illumos +// +build aix darwin dragonfly freebsd linux,!android nacl netbsd openbsd plan9 solaris rumprun illumos + +package pq + +import ( + "os" + "os/user" +) + +func userCurrent() (string, error) { + u, err := user.Current() + if err == nil { + return u.Username, nil + } + + name := os.Getenv("USER") + if name != "" { + return name, nil + } + + return "", ErrCouldNotDetectUsername +} diff --git a/vendor/github.com/lib/pq/user_windows.go b/vendor/github.com/lib/pq/user_windows.go new file mode 100644 index 00000000000..2b691267b97 --- /dev/null +++ b/vendor/github.com/lib/pq/user_windows.go @@ -0,0 +1,27 @@ +// Package pq is a pure Go Postgres driver for the database/sql package. +package pq + +import ( + "path/filepath" + "syscall" +) + +// Perform Windows user name lookup identically to libpq. +// +// The PostgreSQL code makes use of the legacy Win32 function +// GetUserName, and that function has not been imported into stock Go. +// GetUserNameEx is available though, the difference being that a +// wider range of names are available. To get the output to be the +// same as GetUserName, only the base (or last) component of the +// result is returned. +func userCurrent() (string, error) { + pw_name := make([]uint16, 128) + pwname_size := uint32(len(pw_name)) - 1 + err := syscall.GetUserNameEx(syscall.NameSamCompatible, &pw_name[0], &pwname_size) + if err != nil { + return "", ErrCouldNotDetectUsername + } + s := syscall.UTF16ToString(pw_name) + u := filepath.Base(s) + return u, nil +} diff --git a/vendor/github.com/lib/pq/uuid.go b/vendor/github.com/lib/pq/uuid.go new file mode 100644 index 00000000000..9a1b9e0748e --- /dev/null +++ b/vendor/github.com/lib/pq/uuid.go @@ -0,0 +1,23 @@ +package pq + +import ( + "encoding/hex" + "fmt" +) + +// decodeUUIDBinary interprets the binary format of a uuid, returning it in text format. +func decodeUUIDBinary(src []byte) ([]byte, error) { + if len(src) != 16 { + return nil, fmt.Errorf("pq: unable to decode uuid; bad length: %d", len(src)) + } + + dst := make([]byte, 36) + dst[8], dst[13], dst[18], dst[23] = '-', '-', '-', '-' + hex.Encode(dst[0:], src[0:4]) + hex.Encode(dst[9:], src[4:6]) + hex.Encode(dst[14:], src[6:8]) + hex.Encode(dst[19:], src[8:10]) + hex.Encode(dst[24:], src[10:16]) + + return dst, nil +} diff --git a/vendor/github.com/openshift/assisted-service/LICENSE b/vendor/github.com/openshift/assisted-service/LICENSE new file mode 100644 index 00000000000..261eeb9e9f8 --- /dev/null +++ b/vendor/github.com/openshift/assisted-service/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/openshift/assisted-service/api/hiveextension/v1beta1/agentclusterinstall_types.go b/vendor/github.com/openshift/assisted-service/api/hiveextension/v1beta1/agentclusterinstall_types.go index e1ab7c82b02..8ef92a34ada 100644 --- a/vendor/github.com/openshift/assisted-service/api/hiveextension/v1beta1/agentclusterinstall_types.go +++ b/vendor/github.com/openshift/assisted-service/api/hiveextension/v1beta1/agentclusterinstall_types.go @@ -8,11 +8,11 @@ import ( ) const ( - ClusterSpecSyncedCondition string = "SpecSynced" + ClusterSpecSyncedCondition hivev1.ClusterInstallConditionType = "SpecSynced" - ClusterCompletedCondition string = hivev1.ClusterInstallCompleted + ClusterCompletedCondition = hivev1.ClusterInstallCompleted - ClusterRequirementsMetCondition string = hivev1.ClusterInstallRequirementsMet + ClusterRequirementsMetCondition = hivev1.ClusterInstallRequirementsMet ClusterReadyReason string = "ClusterIsReady" ClusterReadyMsg string = "The cluster is ready to begin the installation" ClusterNotReadyReason string = "ClusterNotReady" @@ -30,19 +30,19 @@ const ( ClusterAdditionalAgentsReason string = "AdditionalAgents" ClusterAdditionalAgentsMsg string = "The cluster currently requires exactly %d agents but have %d registered" - ClusterValidatedCondition string = "Validated" - ClusterValidationsOKMsg string = "The cluster's validations are passing" - ClusterValidationsUnknownMsg string = "The cluster's validations have not yet been calculated" - ClusterValidationsFailingMsg string = "The cluster's validations are failing:" - ClusterValidationsUserPendingMsg string = "The cluster's validations are pending for user:" + ClusterValidatedCondition hivev1.ClusterInstallConditionType = "Validated" + ClusterValidationsOKMsg string = "The cluster's validations are passing" + ClusterValidationsUnknownMsg string = "The cluster's validations have not yet been calculated" + ClusterValidationsFailingMsg string = "The cluster's validations are failing:" + ClusterValidationsUserPendingMsg string = "The cluster's validations are pending for user:" - ClusterFailedCondition string = hivev1.ClusterInstallFailed + ClusterFailedCondition = hivev1.ClusterInstallFailed ClusterFailedReason string = "InstallationFailed" ClusterFailedMsg string = "The installation failed:" ClusterNotFailedReason string = "InstallationNotFailed" ClusterNotFailedMsg string = "The installation has not failed" - ClusterStoppedCondition string = hivev1.ClusterInstallStopped + ClusterStoppedCondition = hivev1.ClusterInstallStopped ClusterStoppedFailedReason string = "InstallationFailed" ClusterStoppedFailedMsg string = "The installation has stopped due to error" ClusterStoppedCanceledReason string = "InstallationCancelled" @@ -79,6 +79,11 @@ const ( ClusterBackendErrorMsg string = "The Spec could not be synced due to backend error:" ClusterInputErrorReason string = "InputError" ClusterInputErrorMsg string = "The Spec could not be synced due to an input error:" + + ClusterLastInstallationPreparationFailedOKReason string = "There is no failing prior preparation attempt" + ClusterLastInstallationPreparationFailedErrorReason string = "The last installation preparation failed" + ClusterLastInstallationPreparationPending string = "Cluster preparation has never been performed for this cluster" + ClusterLastInstallationPreparationFailedCondition hivev1.ClusterInstallConditionType = "LastInstallationPreparationFailed" ) // +genclient @@ -380,6 +385,18 @@ const ( // +kubebuilder:validation:Enum="";BareMetal;None;VSphere;Nutanix;External type PlatformType string +// CloudControllerManager describes the type of cloud controller manager to be enabled. +// +kubebuilder:validation:Enum="";BareMetal;None;VSphere;Nutanix;External +type CloudControllerManager string + +const ( + // CloudControllerManagerTypeExternal specifies that an external cloud provider is to be configured. + CloudControllerManagerTypeExternal = "External" + + // CloudControllerManagerTypeNone specifies that no cloud provider is to be configured. + CloudControllerManagerTypeNone = "" +) + // ExternalPlatformSpec holds the desired state for the generic External infrastructure provider. type ExternalPlatformSpec struct { // PlatformName holds the arbitrary string representing the infrastructure provider name, expected to be set at the installation time. @@ -389,6 +406,13 @@ type ExternalPlatformSpec struct { // +kubebuilder:validation:XValidation:rule="oldSelf == 'Unknown' || self == oldSelf",message="platform name cannot be changed once set" // +optional PlatformName string `json:"platformName,omitempty"` + + // CloudControllerManager when set to external, this property will enable an external cloud provider. + // +kubebuilder:default:="" + // +default="" + // +kubebuilder:validation:Enum="";External + // +optional + CloudControllerManager CloudControllerManager `json:"cloudControllerManager,omitempty"` } const ( diff --git a/vendor/github.com/openshift/assisted-service/api/v1beta1/agent_types.go b/vendor/github.com/openshift/assisted-service/api/v1beta1/agent_types.go index bbcc9eacf23..03fd8fdac74 100644 --- a/vendor/github.com/openshift/assisted-service/api/v1beta1/agent_types.go +++ b/vendor/github.com/openshift/assisted-service/api/v1beta1/agent_types.go @@ -194,6 +194,8 @@ type AgentSpec struct { IgnitionConfigOverrides string `json:"ignitionConfigOverrides,omitempty"` // IgnitionEndpointTokenReference references a secret containing an Authorization Bearer token to fetch the ignition from ignition_endpoint_url. IgnitionEndpointTokenReference *IgnitionEndpointTokenReference `json:"ignitionEndpointTokenReference,omitempty"` + // IgnitionEndpointHTTPHeaders are the additional HTTP headers used when fetching the ignition. + IgnitionEndpointHTTPHeaders map[string]string `json:"ignitionEndpointHTTPHeaders,omitempty"` // NodeLabels are the labels to be applied on the node associated with this agent NodeLabels map[string]string `json:"nodeLabels,omitempty"` } @@ -225,6 +227,13 @@ type HostNTPSources struct { SourceState models.SourceState `json:"sourceState,omitempty"` } +type AgentDeprovisionInfo struct { + ClusterName string `json:"cluster_name,omitempty"` + ClusterNamespace string `json:"cluster_namespace,omitempty"` + NodeName string `json:"node_name,omitempty"` + Message string `json:"message,omitempty"` +} + // AgentStatus defines the observed state of Agent type AgentStatus struct { Bootstrap bool `json:"bootstrap,omitempty"` @@ -245,6 +254,10 @@ type AgentStatus struct { // InstallationDiskID is the disk that will be used for the installation. // +optional InstallationDiskID string `json:"installation_disk_id,omitempty"` + + // DeprovisionInfo stores data related to the agent's previous cluster binding in order to clean up when the agent re-registers + // +optional + DeprovisionInfo *AgentDeprovisionInfo `json:"deprovision_info,omitempty"` } type DebugInfo struct { diff --git a/vendor/github.com/openshift/assisted-service/api/v1beta1/agentserviceconfig_types.go b/vendor/github.com/openshift/assisted-service/api/v1beta1/agentserviceconfig_types.go index 68163ba2af6..92d96aee54e 100644 --- a/vendor/github.com/openshift/assisted-service/api/v1beta1/agentserviceconfig_types.go +++ b/vendor/github.com/openshift/assisted-service/api/v1beta1/agentserviceconfig_types.go @@ -104,21 +104,66 @@ type AgentServiceConfigSpec struct { IPXEHTTPRoute string `json:"iPXEHTTPRoute,omitempty"` // UnauthenticatedRegistries is a list of registries from which container images can be pulled // without authentication. They will be appended to the default list (quay.io, - // registry.svc.ci.openshift.org). Any registry on this list will not require credentials + // registry.ci.openshift.org). Any registry on this list will not require credentials // to be in the pull secret validated by the assisted-service. //+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="List of container registries without authentication" // +optional UnauthenticatedRegistries []string `json:"unauthenticatedRegistries,omitempty"` + + // OSImageCACertRef is a reference to a config map containing a certificate authority certificate + // this is an optional certificate to allow a user to add a certificate authority for a HTTPS source of images + // this certificate will be used by the assisted-image-service when pulling OS images. + //+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="OS Image CA Cert ConfigMap reference" + // +optional + OSImageCACertRef *corev1.LocalObjectReference `json:"OSImageCACertRef,omitempty"` + + // OSImageAdditionalParamsRef is a reference to a secret containing a headers and query parameters to be used during OS image fetch. + //+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="OS Images additional parameters reference" + // +optional + OSImageAdditionalParamsRef *corev1.LocalObjectReference `json:"OSImageAdditionalParamsRef,omitempty"` + + // Ingress contains configuration for the ingress resources. + // Has no effect when running on an OpenShift cluster. + //+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Ingress" + // +optional + Ingress *Ingress `json:"ingress,omitempty"` +} + +type Ingress struct { + // AssistedServiceHostname is the hostname to be assigned to the assisted-service ingress. + // Has no effect when running on an OpenShift cluster. + //+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Assisted Service hostname" + AssistedServiceHostname string `json:"assistedServiceHostname"` + + // ImageServiceHostname is the hostname to be assigned to the assisted-image-service ingress. + // Has no effect when running on an OpenShift cluster. + //+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Assisted Image Service hostname" + ImageServiceHostname string `json:"imageServiceHostname"` + + // ClassName is the name of the ingress class to be used when configuring ingress resources. + // Has no effect when running on an OpenShift cluster. + //+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Class Name" + // +optional + ClassName *string `json:"className,omitempty"` } // ConditionType related to our reconcile loop in addition to all the reasons // why ConditionStatus could be true or false. const ( + // ConditionReconcileCompleted reports on whether or not the local cluster is managed. + ConditionLocalClusterManaged conditionsv1.ConditionType = "LocalClusterManaged" // ConditionReconcileCompleted reports whether reconcile completed without error. ConditionReconcileCompleted conditionsv1.ConditionType = "ReconcileCompleted" // ConditionDeploymentsHealthy reports whether deployments are healthy. ConditionDeploymentsHealthy conditionsv1.ConditionType = "DeploymentsHealthy" - + // ReasonLocalClusterImportNotEnabled when the import of local cluster is not enabled. + ReasonLocalClusterImportNotEnabled string = "Local cluster import is not enabled" + // ReasonLocalClusterEntitiesCreated when the local cluster is managed. + ReasonLocalClusterManaged string = "Local cluster is managed." + // ReasonLocalClusterEntitiesRemoved when the local cluster is not managed. + ReasonLocalClusterNotManaged string = "Local cluster is not managed." + // ReasonUnableToDetermineLocalClusterManagedStatus when unable to determine the status of local cluster entities. + ReasonUnableToDetermineLocalClusterManagedStatus string = "Unable to determine local cluster managed status." // ReasonReconcileSucceeded when the reconcile completes all operations without error. ReasonReconcileSucceeded string = "ReconcileSucceeded" // ReasonDeploymentSucceeded when configuring/deploying the assisted-service deployment completed without errors. @@ -181,11 +226,21 @@ const ( ReasonSpokeClientCreationFailure string = "ReasonSpokeClientCreationFailure" // ReasonKonnectivityAgentFailure when there was a failure creating the namespace. ReasonKonnectivityAgentFailure string = "KonnectivityAgentFailure" + // ReasonOSImageCACertRefFailure when there has been a failure resolving the OS image CA using OSImageCACertRef. + ReasonOSImageCACertRefFailure string = "OSImageCACertRefFailure" + // ReasonMonitoringFailure indicates there was a failure monitoring operand status + ReasonMonitoringFailure string = "MonitoringFailure" + // ReasonKubernetesIngressMissing indicates the user has not provided the required configuration for kubernetes ingress + ReasonKubernetesIngressMissing string = "KubernetesIngressConfigMissing" + // ReasonCertificateFailure indicates that the required certificates could not be created + ReasonCertificateFailure string = "CertificateConfigurationFailure" // IPXEHTTPRouteEnabled is expected value in IPXEHTTPRoute to enable the route IPXEHTTPRouteEnabled string = "enabled" // IPXEHTTPRouteEnabled is expected value in IPXEHTTPRoute to disable the route IPXEHTTPRouteDisabled string = "disabled" + // ReasonOSImageAdditionalParamsRefFailure when there has been a failure resolving the OS image additional params secret using OSImageAdditionalParamsRef. + ReasonOSImageAdditionalParamsRefFailure string = "ReasonOSImageAdditionalParamsRefFailure" ) // AgentServiceConfigStatus defines the observed state of AgentServiceConfig diff --git a/vendor/github.com/openshift/assisted-service/api/v1beta1/infraenv_types.go b/vendor/github.com/openshift/assisted-service/api/v1beta1/infraenv_types.go index f2996718120..aeb28a5d82f 100644 --- a/vendor/github.com/openshift/assisted-service/api/v1beta1/infraenv_types.go +++ b/vendor/github.com/openshift/assisted-service/api/v1beta1/infraenv_types.go @@ -172,6 +172,8 @@ type InfraEnvDebugInfo struct { // EventsURL specifies an HTTP/S URL that contains InfraEnv events // +optional EventsURL string `json:"eventsURL"` + // StaticNetworkDownloadURL specifies an HTTP/S URL that contains the static network config + StaticNetworkDownloadURL string `json:"staticNetworkDownloadURL,omitempty"` } type BootArtifacts struct { diff --git a/vendor/github.com/openshift/assisted-service/api/v1beta1/zz_generated.deepcopy.go b/vendor/github.com/openshift/assisted-service/api/v1beta1/zz_generated.deepcopy.go index bdf6251e93a..eabce2368b3 100644 --- a/vendor/github.com/openshift/assisted-service/api/v1beta1/zz_generated.deepcopy.go +++ b/vendor/github.com/openshift/assisted-service/api/v1beta1/zz_generated.deepcopy.go @@ -152,6 +152,21 @@ func (in *AgentClassificationStatus) DeepCopy() *AgentClassificationStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AgentDeprovisionInfo) DeepCopyInto(out *AgentDeprovisionInfo) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgentDeprovisionInfo. +func (in *AgentDeprovisionInfo) DeepCopy() *AgentDeprovisionInfo { + if in == nil { + return nil + } + out := new(AgentDeprovisionInfo) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AgentList) DeepCopyInto(out *AgentList) { *out = *in @@ -273,6 +288,21 @@ func (in *AgentServiceConfigSpec) DeepCopyInto(out *AgentServiceConfigSpec) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.OSImageCACertRef != nil { + in, out := &in.OSImageCACertRef, &out.OSImageCACertRef + *out = new(corev1.LocalObjectReference) + **out = **in + } + if in.OSImageAdditionalParamsRef != nil { + in, out := &in.OSImageAdditionalParamsRef, &out.OSImageAdditionalParamsRef + *out = new(corev1.LocalObjectReference) + **out = **in + } + if in.Ingress != nil { + in, out := &in.Ingress, &out.Ingress + *out = new(Ingress) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgentServiceConfigSpec. @@ -320,6 +350,13 @@ func (in *AgentSpec) DeepCopyInto(out *AgentSpec) { *out = new(IgnitionEndpointTokenReference) **out = **in } + if in.IgnitionEndpointHTTPHeaders != nil { + in, out := &in.IgnitionEndpointHTTPHeaders, &out.IgnitionEndpointHTTPHeaders + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } if in.NodeLabels != nil { in, out := &in.NodeLabels, &out.NodeLabels *out = make(map[string]string, len(*in)) @@ -372,6 +409,11 @@ func (in *AgentStatus) DeepCopyInto(out *AgentStatus) { (*out)[key] = outVal } } + if in.DeprovisionInfo != nil { + in, out := &in.DeprovisionInfo, &out.DeprovisionInfo + *out = new(AgentDeprovisionInfo) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgentStatus. @@ -920,6 +962,26 @@ func (in *InfraEnvStatus) DeepCopy() *InfraEnvStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Ingress) DeepCopyInto(out *Ingress) { + *out = *in + if in.ClassName != nil { + in, out := &in.ClassName, &out.ClassName + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Ingress. +func (in *Ingress) DeepCopy() *Ingress { + if in == nil { + return nil + } + out := new(Ingress) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Interface) DeepCopyInto(out *Interface) { *out = *in diff --git a/vendor/github.com/openshift/assisted-service/client/events/events_client.go b/vendor/github.com/openshift/assisted-service/client/events/events_client.go index c8b34b78462..bb7480741fb 100644 --- a/vendor/github.com/openshift/assisted-service/client/events/events_client.go +++ b/vendor/github.com/openshift/assisted-service/client/events/events_client.go @@ -20,6 +20,9 @@ type API interface { /* V2ListEvents Lists events for a cluster.*/ V2ListEvents(ctx context.Context, params *V2ListEventsParams) (*V2ListEventsOK, error) + /* + V2TriggerEvent Add new assisted installer event.*/ + V2TriggerEvent(ctx context.Context, params *V2TriggerEventParams) (*V2TriggerEventCreated, error) } // New creates a new events API client. @@ -64,3 +67,28 @@ func (a *Client) V2ListEvents(ctx context.Context, params *V2ListEventsParams) ( return result.(*V2ListEventsOK), nil } + +/* +V2TriggerEvent Add new assisted installer event. +*/ +func (a *Client) V2TriggerEvent(ctx context.Context, params *V2TriggerEventParams) (*V2TriggerEventCreated, error) { + + result, err := a.transport.Submit(&runtime.ClientOperation{ + ID: "v2TriggerEvent", + Method: "POST", + PathPattern: "/v2/events", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"http", "https"}, + Params: params, + Reader: &V2TriggerEventReader{formats: a.formats}, + AuthInfo: a.authInfo, + Context: ctx, + Client: params.HTTPClient, + }) + if err != nil { + return nil, err + } + return result.(*V2TriggerEventCreated), nil + +} diff --git a/vendor/github.com/openshift/assisted-service/client/events/v2_trigger_event_parameters.go b/vendor/github.com/openshift/assisted-service/client/events/v2_trigger_event_parameters.go new file mode 100644 index 00000000000..f9db05a6e1d --- /dev/null +++ b/vendor/github.com/openshift/assisted-service/client/events/v2_trigger_event_parameters.go @@ -0,0 +1,153 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package events + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" + + "github.com/openshift/assisted-service/models" +) + +// NewV2TriggerEventParams creates a new V2TriggerEventParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewV2TriggerEventParams() *V2TriggerEventParams { + return &V2TriggerEventParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewV2TriggerEventParamsWithTimeout creates a new V2TriggerEventParams object +// with the ability to set a timeout on a request. +func NewV2TriggerEventParamsWithTimeout(timeout time.Duration) *V2TriggerEventParams { + return &V2TriggerEventParams{ + timeout: timeout, + } +} + +// NewV2TriggerEventParamsWithContext creates a new V2TriggerEventParams object +// with the ability to set a context for a request. +func NewV2TriggerEventParamsWithContext(ctx context.Context) *V2TriggerEventParams { + return &V2TriggerEventParams{ + Context: ctx, + } +} + +// NewV2TriggerEventParamsWithHTTPClient creates a new V2TriggerEventParams object +// with the ability to set a custom HTTPClient for a request. +func NewV2TriggerEventParamsWithHTTPClient(client *http.Client) *V2TriggerEventParams { + return &V2TriggerEventParams{ + HTTPClient: client, + } +} + +/* +V2TriggerEventParams contains all the parameters to send to the API endpoint + + for the v2 trigger event operation. + + Typically these are written to a http.Request. +*/ +type V2TriggerEventParams struct { + + /* TriggerEventParams. + + The event to be created. + */ + TriggerEventParams *models.Event + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the v2 trigger event params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *V2TriggerEventParams) WithDefaults() *V2TriggerEventParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the v2 trigger event params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *V2TriggerEventParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the v2 trigger event params +func (o *V2TriggerEventParams) WithTimeout(timeout time.Duration) *V2TriggerEventParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the v2 trigger event params +func (o *V2TriggerEventParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the v2 trigger event params +func (o *V2TriggerEventParams) WithContext(ctx context.Context) *V2TriggerEventParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the v2 trigger event params +func (o *V2TriggerEventParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the v2 trigger event params +func (o *V2TriggerEventParams) WithHTTPClient(client *http.Client) *V2TriggerEventParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the v2 trigger event params +func (o *V2TriggerEventParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithTriggerEventParams adds the triggerEventParams to the v2 trigger event params +func (o *V2TriggerEventParams) WithTriggerEventParams(triggerEventParams *models.Event) *V2TriggerEventParams { + o.SetTriggerEventParams(triggerEventParams) + return o +} + +// SetTriggerEventParams adds the triggerEventParams to the v2 trigger event params +func (o *V2TriggerEventParams) SetTriggerEventParams(triggerEventParams *models.Event) { + o.TriggerEventParams = triggerEventParams +} + +// WriteToRequest writes these params to a swagger request +func (o *V2TriggerEventParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + if o.TriggerEventParams != nil { + if err := r.SetBodyParam(o.TriggerEventParams); err != nil { + return err + } + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/vendor/github.com/openshift/assisted-service/client/events/v2_trigger_event_responses.go b/vendor/github.com/openshift/assisted-service/client/events/v2_trigger_event_responses.go new file mode 100644 index 00000000000..5f99aeff662 --- /dev/null +++ b/vendor/github.com/openshift/assisted-service/client/events/v2_trigger_event_responses.go @@ -0,0 +1,707 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package events + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + "github.com/openshift/assisted-service/models" +) + +// V2TriggerEventReader is a Reader for the V2TriggerEvent structure. +type V2TriggerEventReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *V2TriggerEventReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 201: + result := NewV2TriggerEventCreated() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + case 400: + result := NewV2TriggerEventBadRequest() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + case 401: + result := NewV2TriggerEventUnauthorized() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + case 403: + result := NewV2TriggerEventForbidden() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + case 404: + result := NewV2TriggerEventNotFound() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + case 405: + result := NewV2TriggerEventMethodNotAllowed() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + case 409: + result := NewV2TriggerEventConflict() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + case 500: + result := NewV2TriggerEventInternalServerError() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + case 501: + result := NewV2TriggerEventNotImplemented() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + case 503: + result := NewV2TriggerEventServiceUnavailable() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + default: + return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code()) + } +} + +// NewV2TriggerEventCreated creates a V2TriggerEventCreated with default headers values +func NewV2TriggerEventCreated() *V2TriggerEventCreated { + return &V2TriggerEventCreated{} +} + +/* +V2TriggerEventCreated describes a response with status code 201, with default header values. + +Success. +*/ +type V2TriggerEventCreated struct { +} + +// IsSuccess returns true when this v2 trigger event created response has a 2xx status code +func (o *V2TriggerEventCreated) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this v2 trigger event created response has a 3xx status code +func (o *V2TriggerEventCreated) IsRedirect() bool { + return false +} + +// IsClientError returns true when this v2 trigger event created response has a 4xx status code +func (o *V2TriggerEventCreated) IsClientError() bool { + return false +} + +// IsServerError returns true when this v2 trigger event created response has a 5xx status code +func (o *V2TriggerEventCreated) IsServerError() bool { + return false +} + +// IsCode returns true when this v2 trigger event created response a status code equal to that given +func (o *V2TriggerEventCreated) IsCode(code int) bool { + return code == 201 +} + +func (o *V2TriggerEventCreated) Error() string { + return fmt.Sprintf("[POST /v2/events][%d] v2TriggerEventCreated ", 201) +} + +func (o *V2TriggerEventCreated) String() string { + return fmt.Sprintf("[POST /v2/events][%d] v2TriggerEventCreated ", 201) +} + +func (o *V2TriggerEventCreated) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + return nil +} + +// NewV2TriggerEventBadRequest creates a V2TriggerEventBadRequest with default headers values +func NewV2TriggerEventBadRequest() *V2TriggerEventBadRequest { + return &V2TriggerEventBadRequest{} +} + +/* +V2TriggerEventBadRequest describes a response with status code 400, with default header values. + +Error. +*/ +type V2TriggerEventBadRequest struct { + Payload *models.Error +} + +// IsSuccess returns true when this v2 trigger event bad request response has a 2xx status code +func (o *V2TriggerEventBadRequest) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this v2 trigger event bad request response has a 3xx status code +func (o *V2TriggerEventBadRequest) IsRedirect() bool { + return false +} + +// IsClientError returns true when this v2 trigger event bad request response has a 4xx status code +func (o *V2TriggerEventBadRequest) IsClientError() bool { + return true +} + +// IsServerError returns true when this v2 trigger event bad request response has a 5xx status code +func (o *V2TriggerEventBadRequest) IsServerError() bool { + return false +} + +// IsCode returns true when this v2 trigger event bad request response a status code equal to that given +func (o *V2TriggerEventBadRequest) IsCode(code int) bool { + return code == 400 +} + +func (o *V2TriggerEventBadRequest) Error() string { + return fmt.Sprintf("[POST /v2/events][%d] v2TriggerEventBadRequest %+v", 400, o.Payload) +} + +func (o *V2TriggerEventBadRequest) String() string { + return fmt.Sprintf("[POST /v2/events][%d] v2TriggerEventBadRequest %+v", 400, o.Payload) +} + +func (o *V2TriggerEventBadRequest) GetPayload() *models.Error { + return o.Payload +} + +func (o *V2TriggerEventBadRequest) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.Error) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewV2TriggerEventUnauthorized creates a V2TriggerEventUnauthorized with default headers values +func NewV2TriggerEventUnauthorized() *V2TriggerEventUnauthorized { + return &V2TriggerEventUnauthorized{} +} + +/* +V2TriggerEventUnauthorized describes a response with status code 401, with default header values. + +Unauthorized. +*/ +type V2TriggerEventUnauthorized struct { + Payload *models.InfraError +} + +// IsSuccess returns true when this v2 trigger event unauthorized response has a 2xx status code +func (o *V2TriggerEventUnauthorized) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this v2 trigger event unauthorized response has a 3xx status code +func (o *V2TriggerEventUnauthorized) IsRedirect() bool { + return false +} + +// IsClientError returns true when this v2 trigger event unauthorized response has a 4xx status code +func (o *V2TriggerEventUnauthorized) IsClientError() bool { + return true +} + +// IsServerError returns true when this v2 trigger event unauthorized response has a 5xx status code +func (o *V2TriggerEventUnauthorized) IsServerError() bool { + return false +} + +// IsCode returns true when this v2 trigger event unauthorized response a status code equal to that given +func (o *V2TriggerEventUnauthorized) IsCode(code int) bool { + return code == 401 +} + +func (o *V2TriggerEventUnauthorized) Error() string { + return fmt.Sprintf("[POST /v2/events][%d] v2TriggerEventUnauthorized %+v", 401, o.Payload) +} + +func (o *V2TriggerEventUnauthorized) String() string { + return fmt.Sprintf("[POST /v2/events][%d] v2TriggerEventUnauthorized %+v", 401, o.Payload) +} + +func (o *V2TriggerEventUnauthorized) GetPayload() *models.InfraError { + return o.Payload +} + +func (o *V2TriggerEventUnauthorized) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.InfraError) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewV2TriggerEventForbidden creates a V2TriggerEventForbidden with default headers values +func NewV2TriggerEventForbidden() *V2TriggerEventForbidden { + return &V2TriggerEventForbidden{} +} + +/* +V2TriggerEventForbidden describes a response with status code 403, with default header values. + +Forbidden. +*/ +type V2TriggerEventForbidden struct { + Payload *models.InfraError +} + +// IsSuccess returns true when this v2 trigger event forbidden response has a 2xx status code +func (o *V2TriggerEventForbidden) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this v2 trigger event forbidden response has a 3xx status code +func (o *V2TriggerEventForbidden) IsRedirect() bool { + return false +} + +// IsClientError returns true when this v2 trigger event forbidden response has a 4xx status code +func (o *V2TriggerEventForbidden) IsClientError() bool { + return true +} + +// IsServerError returns true when this v2 trigger event forbidden response has a 5xx status code +func (o *V2TriggerEventForbidden) IsServerError() bool { + return false +} + +// IsCode returns true when this v2 trigger event forbidden response a status code equal to that given +func (o *V2TriggerEventForbidden) IsCode(code int) bool { + return code == 403 +} + +func (o *V2TriggerEventForbidden) Error() string { + return fmt.Sprintf("[POST /v2/events][%d] v2TriggerEventForbidden %+v", 403, o.Payload) +} + +func (o *V2TriggerEventForbidden) String() string { + return fmt.Sprintf("[POST /v2/events][%d] v2TriggerEventForbidden %+v", 403, o.Payload) +} + +func (o *V2TriggerEventForbidden) GetPayload() *models.InfraError { + return o.Payload +} + +func (o *V2TriggerEventForbidden) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.InfraError) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewV2TriggerEventNotFound creates a V2TriggerEventNotFound with default headers values +func NewV2TriggerEventNotFound() *V2TriggerEventNotFound { + return &V2TriggerEventNotFound{} +} + +/* +V2TriggerEventNotFound describes a response with status code 404, with default header values. + +Error. +*/ +type V2TriggerEventNotFound struct { + Payload *models.Error +} + +// IsSuccess returns true when this v2 trigger event not found response has a 2xx status code +func (o *V2TriggerEventNotFound) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this v2 trigger event not found response has a 3xx status code +func (o *V2TriggerEventNotFound) IsRedirect() bool { + return false +} + +// IsClientError returns true when this v2 trigger event not found response has a 4xx status code +func (o *V2TriggerEventNotFound) IsClientError() bool { + return true +} + +// IsServerError returns true when this v2 trigger event not found response has a 5xx status code +func (o *V2TriggerEventNotFound) IsServerError() bool { + return false +} + +// IsCode returns true when this v2 trigger event not found response a status code equal to that given +func (o *V2TriggerEventNotFound) IsCode(code int) bool { + return code == 404 +} + +func (o *V2TriggerEventNotFound) Error() string { + return fmt.Sprintf("[POST /v2/events][%d] v2TriggerEventNotFound %+v", 404, o.Payload) +} + +func (o *V2TriggerEventNotFound) String() string { + return fmt.Sprintf("[POST /v2/events][%d] v2TriggerEventNotFound %+v", 404, o.Payload) +} + +func (o *V2TriggerEventNotFound) GetPayload() *models.Error { + return o.Payload +} + +func (o *V2TriggerEventNotFound) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.Error) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewV2TriggerEventMethodNotAllowed creates a V2TriggerEventMethodNotAllowed with default headers values +func NewV2TriggerEventMethodNotAllowed() *V2TriggerEventMethodNotAllowed { + return &V2TriggerEventMethodNotAllowed{} +} + +/* +V2TriggerEventMethodNotAllowed describes a response with status code 405, with default header values. + +Method Not Allowed. +*/ +type V2TriggerEventMethodNotAllowed struct { + Payload *models.Error +} + +// IsSuccess returns true when this v2 trigger event method not allowed response has a 2xx status code +func (o *V2TriggerEventMethodNotAllowed) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this v2 trigger event method not allowed response has a 3xx status code +func (o *V2TriggerEventMethodNotAllowed) IsRedirect() bool { + return false +} + +// IsClientError returns true when this v2 trigger event method not allowed response has a 4xx status code +func (o *V2TriggerEventMethodNotAllowed) IsClientError() bool { + return true +} + +// IsServerError returns true when this v2 trigger event method not allowed response has a 5xx status code +func (o *V2TriggerEventMethodNotAllowed) IsServerError() bool { + return false +} + +// IsCode returns true when this v2 trigger event method not allowed response a status code equal to that given +func (o *V2TriggerEventMethodNotAllowed) IsCode(code int) bool { + return code == 405 +} + +func (o *V2TriggerEventMethodNotAllowed) Error() string { + return fmt.Sprintf("[POST /v2/events][%d] v2TriggerEventMethodNotAllowed %+v", 405, o.Payload) +} + +func (o *V2TriggerEventMethodNotAllowed) String() string { + return fmt.Sprintf("[POST /v2/events][%d] v2TriggerEventMethodNotAllowed %+v", 405, o.Payload) +} + +func (o *V2TriggerEventMethodNotAllowed) GetPayload() *models.Error { + return o.Payload +} + +func (o *V2TriggerEventMethodNotAllowed) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.Error) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewV2TriggerEventConflict creates a V2TriggerEventConflict with default headers values +func NewV2TriggerEventConflict() *V2TriggerEventConflict { + return &V2TriggerEventConflict{} +} + +/* +V2TriggerEventConflict describes a response with status code 409, with default header values. + +Cluster cannot accept new agents due to its current state. +*/ +type V2TriggerEventConflict struct { + Payload *models.Error +} + +// IsSuccess returns true when this v2 trigger event conflict response has a 2xx status code +func (o *V2TriggerEventConflict) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this v2 trigger event conflict response has a 3xx status code +func (o *V2TriggerEventConflict) IsRedirect() bool { + return false +} + +// IsClientError returns true when this v2 trigger event conflict response has a 4xx status code +func (o *V2TriggerEventConflict) IsClientError() bool { + return true +} + +// IsServerError returns true when this v2 trigger event conflict response has a 5xx status code +func (o *V2TriggerEventConflict) IsServerError() bool { + return false +} + +// IsCode returns true when this v2 trigger event conflict response a status code equal to that given +func (o *V2TriggerEventConflict) IsCode(code int) bool { + return code == 409 +} + +func (o *V2TriggerEventConflict) Error() string { + return fmt.Sprintf("[POST /v2/events][%d] v2TriggerEventConflict %+v", 409, o.Payload) +} + +func (o *V2TriggerEventConflict) String() string { + return fmt.Sprintf("[POST /v2/events][%d] v2TriggerEventConflict %+v", 409, o.Payload) +} + +func (o *V2TriggerEventConflict) GetPayload() *models.Error { + return o.Payload +} + +func (o *V2TriggerEventConflict) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.Error) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewV2TriggerEventInternalServerError creates a V2TriggerEventInternalServerError with default headers values +func NewV2TriggerEventInternalServerError() *V2TriggerEventInternalServerError { + return &V2TriggerEventInternalServerError{} +} + +/* +V2TriggerEventInternalServerError describes a response with status code 500, with default header values. + +Error. +*/ +type V2TriggerEventInternalServerError struct { + Payload *models.Error +} + +// IsSuccess returns true when this v2 trigger event internal server error response has a 2xx status code +func (o *V2TriggerEventInternalServerError) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this v2 trigger event internal server error response has a 3xx status code +func (o *V2TriggerEventInternalServerError) IsRedirect() bool { + return false +} + +// IsClientError returns true when this v2 trigger event internal server error response has a 4xx status code +func (o *V2TriggerEventInternalServerError) IsClientError() bool { + return false +} + +// IsServerError returns true when this v2 trigger event internal server error response has a 5xx status code +func (o *V2TriggerEventInternalServerError) IsServerError() bool { + return true +} + +// IsCode returns true when this v2 trigger event internal server error response a status code equal to that given +func (o *V2TriggerEventInternalServerError) IsCode(code int) bool { + return code == 500 +} + +func (o *V2TriggerEventInternalServerError) Error() string { + return fmt.Sprintf("[POST /v2/events][%d] v2TriggerEventInternalServerError %+v", 500, o.Payload) +} + +func (o *V2TriggerEventInternalServerError) String() string { + return fmt.Sprintf("[POST /v2/events][%d] v2TriggerEventInternalServerError %+v", 500, o.Payload) +} + +func (o *V2TriggerEventInternalServerError) GetPayload() *models.Error { + return o.Payload +} + +func (o *V2TriggerEventInternalServerError) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.Error) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewV2TriggerEventNotImplemented creates a V2TriggerEventNotImplemented with default headers values +func NewV2TriggerEventNotImplemented() *V2TriggerEventNotImplemented { + return &V2TriggerEventNotImplemented{} +} + +/* +V2TriggerEventNotImplemented describes a response with status code 501, with default header values. + +Not implemented. +*/ +type V2TriggerEventNotImplemented struct { + Payload *models.Error +} + +// IsSuccess returns true when this v2 trigger event not implemented response has a 2xx status code +func (o *V2TriggerEventNotImplemented) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this v2 trigger event not implemented response has a 3xx status code +func (o *V2TriggerEventNotImplemented) IsRedirect() bool { + return false +} + +// IsClientError returns true when this v2 trigger event not implemented response has a 4xx status code +func (o *V2TriggerEventNotImplemented) IsClientError() bool { + return false +} + +// IsServerError returns true when this v2 trigger event not implemented response has a 5xx status code +func (o *V2TriggerEventNotImplemented) IsServerError() bool { + return true +} + +// IsCode returns true when this v2 trigger event not implemented response a status code equal to that given +func (o *V2TriggerEventNotImplemented) IsCode(code int) bool { + return code == 501 +} + +func (o *V2TriggerEventNotImplemented) Error() string { + return fmt.Sprintf("[POST /v2/events][%d] v2TriggerEventNotImplemented %+v", 501, o.Payload) +} + +func (o *V2TriggerEventNotImplemented) String() string { + return fmt.Sprintf("[POST /v2/events][%d] v2TriggerEventNotImplemented %+v", 501, o.Payload) +} + +func (o *V2TriggerEventNotImplemented) GetPayload() *models.Error { + return o.Payload +} + +func (o *V2TriggerEventNotImplemented) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.Error) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewV2TriggerEventServiceUnavailable creates a V2TriggerEventServiceUnavailable with default headers values +func NewV2TriggerEventServiceUnavailable() *V2TriggerEventServiceUnavailable { + return &V2TriggerEventServiceUnavailable{} +} + +/* +V2TriggerEventServiceUnavailable describes a response with status code 503, with default header values. + +Unavailable. +*/ +type V2TriggerEventServiceUnavailable struct { + Payload *models.Error +} + +// IsSuccess returns true when this v2 trigger event service unavailable response has a 2xx status code +func (o *V2TriggerEventServiceUnavailable) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this v2 trigger event service unavailable response has a 3xx status code +func (o *V2TriggerEventServiceUnavailable) IsRedirect() bool { + return false +} + +// IsClientError returns true when this v2 trigger event service unavailable response has a 4xx status code +func (o *V2TriggerEventServiceUnavailable) IsClientError() bool { + return false +} + +// IsServerError returns true when this v2 trigger event service unavailable response has a 5xx status code +func (o *V2TriggerEventServiceUnavailable) IsServerError() bool { + return true +} + +// IsCode returns true when this v2 trigger event service unavailable response a status code equal to that given +func (o *V2TriggerEventServiceUnavailable) IsCode(code int) bool { + return code == 503 +} + +func (o *V2TriggerEventServiceUnavailable) Error() string { + return fmt.Sprintf("[POST /v2/events][%d] v2TriggerEventServiceUnavailable %+v", 503, o.Payload) +} + +func (o *V2TriggerEventServiceUnavailable) String() string { + return fmt.Sprintf("[POST /v2/events][%d] v2TriggerEventServiceUnavailable %+v", 503, o.Payload) +} + +func (o *V2TriggerEventServiceUnavailable) GetPayload() *models.Error { + return o.Payload +} + +func (o *V2TriggerEventServiceUnavailable) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.Error) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} diff --git a/vendor/github.com/openshift/assisted-service/client/installer/get_supported_features_parameters.go b/vendor/github.com/openshift/assisted-service/client/installer/get_supported_features_parameters.go index d48f60ce4ae..3e511a49aee 100644 --- a/vendor/github.com/openshift/assisted-service/client/installer/get_supported_features_parameters.go +++ b/vendor/github.com/openshift/assisted-service/client/installer/get_supported_features_parameters.go @@ -69,6 +69,12 @@ type GetSupportedFeaturesParams struct { */ CPUArchitecture *string + /* ExternalPlatformName. + + External platform name when platform type is set to external. The value of this parameter will be ignored if platform_type is not external. + */ + ExternalPlatformName *string + /* OpenshiftVersion. Version of the OpenShift cluster. @@ -156,6 +162,17 @@ func (o *GetSupportedFeaturesParams) SetCPUArchitecture(cPUArchitecture *string) o.CPUArchitecture = cPUArchitecture } +// WithExternalPlatformName adds the externalPlatformName to the get supported features params +func (o *GetSupportedFeaturesParams) WithExternalPlatformName(externalPlatformName *string) *GetSupportedFeaturesParams { + o.SetExternalPlatformName(externalPlatformName) + return o +} + +// SetExternalPlatformName adds the externalPlatformName to the get supported features params +func (o *GetSupportedFeaturesParams) SetExternalPlatformName(externalPlatformName *string) { + o.ExternalPlatformName = externalPlatformName +} + // WithOpenshiftVersion adds the openshiftVersion to the get supported features params func (o *GetSupportedFeaturesParams) WithOpenshiftVersion(openshiftVersion string) *GetSupportedFeaturesParams { o.SetOpenshiftVersion(openshiftVersion) @@ -203,6 +220,23 @@ func (o *GetSupportedFeaturesParams) WriteToRequest(r runtime.ClientRequest, reg } } + if o.ExternalPlatformName != nil { + + // query param external_platform_name + var qrExternalPlatformName string + + if o.ExternalPlatformName != nil { + qrExternalPlatformName = *o.ExternalPlatformName + } + qExternalPlatformName := qrExternalPlatformName + if qExternalPlatformName != "" { + + if err := r.SetQueryParam("external_platform_name", qExternalPlatformName); err != nil { + return err + } + } + } + // query param openshift_version qrOpenshiftVersion := o.OpenshiftVersion qOpenshiftVersion := qrOpenshiftVersion diff --git a/vendor/github.com/openshift/assisted-service/client/installer/installer_client.go b/vendor/github.com/openshift/assisted-service/client/installer/installer_client.go index f5ffdb1f456..ebde8f2af08 100644 --- a/vendor/github.com/openshift/assisted-service/client/installer/installer_client.go +++ b/vendor/github.com/openshift/assisted-service/client/installer/installer_client.go @@ -180,6 +180,9 @@ type API interface { /* V2SetIgnoredValidations Register the validations which are to be ignored for this cluster.*/ V2SetIgnoredValidations(ctx context.Context, params *V2SetIgnoredValidationsParams) (*V2SetIgnoredValidationsCreated, error) + /* + V2UpdateClusterFinalizingProgress Update installation finalizing progress.*/ + V2UpdateClusterFinalizingProgress(ctx context.Context, params *V2UpdateClusterFinalizingProgressParams) (*V2UpdateClusterFinalizingProgressOK, error) /* V2UpdateClusterInstallConfig Override values in the install config.*/ V2UpdateClusterInstallConfig(ctx context.Context, params *V2UpdateClusterInstallConfigParams) (*V2UpdateClusterInstallConfigCreated, error) @@ -1557,6 +1560,31 @@ func (a *Client) V2SetIgnoredValidations(ctx context.Context, params *V2SetIgnor } +/* +V2UpdateClusterFinalizingProgress Update installation finalizing progress. +*/ +func (a *Client) V2UpdateClusterFinalizingProgress(ctx context.Context, params *V2UpdateClusterFinalizingProgressParams) (*V2UpdateClusterFinalizingProgressOK, error) { + + result, err := a.transport.Submit(&runtime.ClientOperation{ + ID: "v2UpdateClusterFinalizingProgress", + Method: "PUT", + PathPattern: "/v2/clusters/{cluster_id}/progress", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"http", "https"}, + Params: params, + Reader: &V2UpdateClusterFinalizingProgressReader{formats: a.formats}, + AuthInfo: a.authInfo, + Context: ctx, + Client: params.HTTPClient, + }) + if err != nil { + return nil, err + } + return result.(*V2UpdateClusterFinalizingProgressOK), nil + +} + /* V2UpdateClusterInstallConfig Override values in the install config. */ diff --git a/vendor/github.com/openshift/assisted-service/client/installer/v2_update_cluster_finalizing_progress_parameters.go b/vendor/github.com/openshift/assisted-service/client/installer/v2_update_cluster_finalizing_progress_parameters.go new file mode 100644 index 00000000000..4ee918d2390 --- /dev/null +++ b/vendor/github.com/openshift/assisted-service/client/installer/v2_update_cluster_finalizing_progress_parameters.go @@ -0,0 +1,177 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package installer + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" + + "github.com/openshift/assisted-service/models" +) + +// NewV2UpdateClusterFinalizingProgressParams creates a new V2UpdateClusterFinalizingProgressParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewV2UpdateClusterFinalizingProgressParams() *V2UpdateClusterFinalizingProgressParams { + return &V2UpdateClusterFinalizingProgressParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewV2UpdateClusterFinalizingProgressParamsWithTimeout creates a new V2UpdateClusterFinalizingProgressParams object +// with the ability to set a timeout on a request. +func NewV2UpdateClusterFinalizingProgressParamsWithTimeout(timeout time.Duration) *V2UpdateClusterFinalizingProgressParams { + return &V2UpdateClusterFinalizingProgressParams{ + timeout: timeout, + } +} + +// NewV2UpdateClusterFinalizingProgressParamsWithContext creates a new V2UpdateClusterFinalizingProgressParams object +// with the ability to set a context for a request. +func NewV2UpdateClusterFinalizingProgressParamsWithContext(ctx context.Context) *V2UpdateClusterFinalizingProgressParams { + return &V2UpdateClusterFinalizingProgressParams{ + Context: ctx, + } +} + +// NewV2UpdateClusterFinalizingProgressParamsWithHTTPClient creates a new V2UpdateClusterFinalizingProgressParams object +// with the ability to set a custom HTTPClient for a request. +func NewV2UpdateClusterFinalizingProgressParamsWithHTTPClient(client *http.Client) *V2UpdateClusterFinalizingProgressParams { + return &V2UpdateClusterFinalizingProgressParams{ + HTTPClient: client, + } +} + +/* +V2UpdateClusterFinalizingProgressParams contains all the parameters to send to the API endpoint + + for the v2 update cluster finalizing progress operation. + + Typically these are written to a http.Request. +*/ +type V2UpdateClusterFinalizingProgressParams struct { + + /* ClusterID. + + The cluster being updated. + + Format: uuid + */ + ClusterID strfmt.UUID + + /* FinalizingProgress. + + New progress value. + */ + FinalizingProgress *models.ClusterFinalizingProgress + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the v2 update cluster finalizing progress params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *V2UpdateClusterFinalizingProgressParams) WithDefaults() *V2UpdateClusterFinalizingProgressParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the v2 update cluster finalizing progress params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *V2UpdateClusterFinalizingProgressParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the v2 update cluster finalizing progress params +func (o *V2UpdateClusterFinalizingProgressParams) WithTimeout(timeout time.Duration) *V2UpdateClusterFinalizingProgressParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the v2 update cluster finalizing progress params +func (o *V2UpdateClusterFinalizingProgressParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the v2 update cluster finalizing progress params +func (o *V2UpdateClusterFinalizingProgressParams) WithContext(ctx context.Context) *V2UpdateClusterFinalizingProgressParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the v2 update cluster finalizing progress params +func (o *V2UpdateClusterFinalizingProgressParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the v2 update cluster finalizing progress params +func (o *V2UpdateClusterFinalizingProgressParams) WithHTTPClient(client *http.Client) *V2UpdateClusterFinalizingProgressParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the v2 update cluster finalizing progress params +func (o *V2UpdateClusterFinalizingProgressParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithClusterID adds the clusterID to the v2 update cluster finalizing progress params +func (o *V2UpdateClusterFinalizingProgressParams) WithClusterID(clusterID strfmt.UUID) *V2UpdateClusterFinalizingProgressParams { + o.SetClusterID(clusterID) + return o +} + +// SetClusterID adds the clusterId to the v2 update cluster finalizing progress params +func (o *V2UpdateClusterFinalizingProgressParams) SetClusterID(clusterID strfmt.UUID) { + o.ClusterID = clusterID +} + +// WithFinalizingProgress adds the finalizingProgress to the v2 update cluster finalizing progress params +func (o *V2UpdateClusterFinalizingProgressParams) WithFinalizingProgress(finalizingProgress *models.ClusterFinalizingProgress) *V2UpdateClusterFinalizingProgressParams { + o.SetFinalizingProgress(finalizingProgress) + return o +} + +// SetFinalizingProgress adds the finalizingProgress to the v2 update cluster finalizing progress params +func (o *V2UpdateClusterFinalizingProgressParams) SetFinalizingProgress(finalizingProgress *models.ClusterFinalizingProgress) { + o.FinalizingProgress = finalizingProgress +} + +// WriteToRequest writes these params to a swagger request +func (o *V2UpdateClusterFinalizingProgressParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + + // path param cluster_id + if err := r.SetPathParam("cluster_id", o.ClusterID.String()); err != nil { + return err + } + if o.FinalizingProgress != nil { + if err := r.SetBodyParam(o.FinalizingProgress); err != nil { + return err + } + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/vendor/github.com/openshift/assisted-service/client/installer/v2_update_cluster_finalizing_progress_responses.go b/vendor/github.com/openshift/assisted-service/client/installer/v2_update_cluster_finalizing_progress_responses.go new file mode 100644 index 00000000000..9fa9146a8b2 --- /dev/null +++ b/vendor/github.com/openshift/assisted-service/client/installer/v2_update_cluster_finalizing_progress_responses.go @@ -0,0 +1,500 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package installer + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + "github.com/openshift/assisted-service/models" +) + +// V2UpdateClusterFinalizingProgressReader is a Reader for the V2UpdateClusterFinalizingProgress structure. +type V2UpdateClusterFinalizingProgressReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *V2UpdateClusterFinalizingProgressReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewV2UpdateClusterFinalizingProgressOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + case 401: + result := NewV2UpdateClusterFinalizingProgressUnauthorized() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + case 403: + result := NewV2UpdateClusterFinalizingProgressForbidden() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + case 404: + result := NewV2UpdateClusterFinalizingProgressNotFound() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + case 405: + result := NewV2UpdateClusterFinalizingProgressMethodNotAllowed() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + case 500: + result := NewV2UpdateClusterFinalizingProgressInternalServerError() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + case 503: + result := NewV2UpdateClusterFinalizingProgressServiceUnavailable() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + default: + return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code()) + } +} + +// NewV2UpdateClusterFinalizingProgressOK creates a V2UpdateClusterFinalizingProgressOK with default headers values +func NewV2UpdateClusterFinalizingProgressOK() *V2UpdateClusterFinalizingProgressOK { + return &V2UpdateClusterFinalizingProgressOK{} +} + +/* +V2UpdateClusterFinalizingProgressOK describes a response with status code 200, with default header values. + +Update install progress. +*/ +type V2UpdateClusterFinalizingProgressOK struct { +} + +// IsSuccess returns true when this v2 update cluster finalizing progress o k response has a 2xx status code +func (o *V2UpdateClusterFinalizingProgressOK) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this v2 update cluster finalizing progress o k response has a 3xx status code +func (o *V2UpdateClusterFinalizingProgressOK) IsRedirect() bool { + return false +} + +// IsClientError returns true when this v2 update cluster finalizing progress o k response has a 4xx status code +func (o *V2UpdateClusterFinalizingProgressOK) IsClientError() bool { + return false +} + +// IsServerError returns true when this v2 update cluster finalizing progress o k response has a 5xx status code +func (o *V2UpdateClusterFinalizingProgressOK) IsServerError() bool { + return false +} + +// IsCode returns true when this v2 update cluster finalizing progress o k response a status code equal to that given +func (o *V2UpdateClusterFinalizingProgressOK) IsCode(code int) bool { + return code == 200 +} + +func (o *V2UpdateClusterFinalizingProgressOK) Error() string { + return fmt.Sprintf("[PUT /v2/clusters/{cluster_id}/progress][%d] v2UpdateClusterFinalizingProgressOK ", 200) +} + +func (o *V2UpdateClusterFinalizingProgressOK) String() string { + return fmt.Sprintf("[PUT /v2/clusters/{cluster_id}/progress][%d] v2UpdateClusterFinalizingProgressOK ", 200) +} + +func (o *V2UpdateClusterFinalizingProgressOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + return nil +} + +// NewV2UpdateClusterFinalizingProgressUnauthorized creates a V2UpdateClusterFinalizingProgressUnauthorized with default headers values +func NewV2UpdateClusterFinalizingProgressUnauthorized() *V2UpdateClusterFinalizingProgressUnauthorized { + return &V2UpdateClusterFinalizingProgressUnauthorized{} +} + +/* +V2UpdateClusterFinalizingProgressUnauthorized describes a response with status code 401, with default header values. + +Unauthorized. +*/ +type V2UpdateClusterFinalizingProgressUnauthorized struct { + Payload *models.InfraError +} + +// IsSuccess returns true when this v2 update cluster finalizing progress unauthorized response has a 2xx status code +func (o *V2UpdateClusterFinalizingProgressUnauthorized) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this v2 update cluster finalizing progress unauthorized response has a 3xx status code +func (o *V2UpdateClusterFinalizingProgressUnauthorized) IsRedirect() bool { + return false +} + +// IsClientError returns true when this v2 update cluster finalizing progress unauthorized response has a 4xx status code +func (o *V2UpdateClusterFinalizingProgressUnauthorized) IsClientError() bool { + return true +} + +// IsServerError returns true when this v2 update cluster finalizing progress unauthorized response has a 5xx status code +func (o *V2UpdateClusterFinalizingProgressUnauthorized) IsServerError() bool { + return false +} + +// IsCode returns true when this v2 update cluster finalizing progress unauthorized response a status code equal to that given +func (o *V2UpdateClusterFinalizingProgressUnauthorized) IsCode(code int) bool { + return code == 401 +} + +func (o *V2UpdateClusterFinalizingProgressUnauthorized) Error() string { + return fmt.Sprintf("[PUT /v2/clusters/{cluster_id}/progress][%d] v2UpdateClusterFinalizingProgressUnauthorized %+v", 401, o.Payload) +} + +func (o *V2UpdateClusterFinalizingProgressUnauthorized) String() string { + return fmt.Sprintf("[PUT /v2/clusters/{cluster_id}/progress][%d] v2UpdateClusterFinalizingProgressUnauthorized %+v", 401, o.Payload) +} + +func (o *V2UpdateClusterFinalizingProgressUnauthorized) GetPayload() *models.InfraError { + return o.Payload +} + +func (o *V2UpdateClusterFinalizingProgressUnauthorized) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.InfraError) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewV2UpdateClusterFinalizingProgressForbidden creates a V2UpdateClusterFinalizingProgressForbidden with default headers values +func NewV2UpdateClusterFinalizingProgressForbidden() *V2UpdateClusterFinalizingProgressForbidden { + return &V2UpdateClusterFinalizingProgressForbidden{} +} + +/* +V2UpdateClusterFinalizingProgressForbidden describes a response with status code 403, with default header values. + +Forbidden. +*/ +type V2UpdateClusterFinalizingProgressForbidden struct { + Payload *models.InfraError +} + +// IsSuccess returns true when this v2 update cluster finalizing progress forbidden response has a 2xx status code +func (o *V2UpdateClusterFinalizingProgressForbidden) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this v2 update cluster finalizing progress forbidden response has a 3xx status code +func (o *V2UpdateClusterFinalizingProgressForbidden) IsRedirect() bool { + return false +} + +// IsClientError returns true when this v2 update cluster finalizing progress forbidden response has a 4xx status code +func (o *V2UpdateClusterFinalizingProgressForbidden) IsClientError() bool { + return true +} + +// IsServerError returns true when this v2 update cluster finalizing progress forbidden response has a 5xx status code +func (o *V2UpdateClusterFinalizingProgressForbidden) IsServerError() bool { + return false +} + +// IsCode returns true when this v2 update cluster finalizing progress forbidden response a status code equal to that given +func (o *V2UpdateClusterFinalizingProgressForbidden) IsCode(code int) bool { + return code == 403 +} + +func (o *V2UpdateClusterFinalizingProgressForbidden) Error() string { + return fmt.Sprintf("[PUT /v2/clusters/{cluster_id}/progress][%d] v2UpdateClusterFinalizingProgressForbidden %+v", 403, o.Payload) +} + +func (o *V2UpdateClusterFinalizingProgressForbidden) String() string { + return fmt.Sprintf("[PUT /v2/clusters/{cluster_id}/progress][%d] v2UpdateClusterFinalizingProgressForbidden %+v", 403, o.Payload) +} + +func (o *V2UpdateClusterFinalizingProgressForbidden) GetPayload() *models.InfraError { + return o.Payload +} + +func (o *V2UpdateClusterFinalizingProgressForbidden) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.InfraError) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewV2UpdateClusterFinalizingProgressNotFound creates a V2UpdateClusterFinalizingProgressNotFound with default headers values +func NewV2UpdateClusterFinalizingProgressNotFound() *V2UpdateClusterFinalizingProgressNotFound { + return &V2UpdateClusterFinalizingProgressNotFound{} +} + +/* +V2UpdateClusterFinalizingProgressNotFound describes a response with status code 404, with default header values. + +Error. +*/ +type V2UpdateClusterFinalizingProgressNotFound struct { + Payload *models.Error +} + +// IsSuccess returns true when this v2 update cluster finalizing progress not found response has a 2xx status code +func (o *V2UpdateClusterFinalizingProgressNotFound) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this v2 update cluster finalizing progress not found response has a 3xx status code +func (o *V2UpdateClusterFinalizingProgressNotFound) IsRedirect() bool { + return false +} + +// IsClientError returns true when this v2 update cluster finalizing progress not found response has a 4xx status code +func (o *V2UpdateClusterFinalizingProgressNotFound) IsClientError() bool { + return true +} + +// IsServerError returns true when this v2 update cluster finalizing progress not found response has a 5xx status code +func (o *V2UpdateClusterFinalizingProgressNotFound) IsServerError() bool { + return false +} + +// IsCode returns true when this v2 update cluster finalizing progress not found response a status code equal to that given +func (o *V2UpdateClusterFinalizingProgressNotFound) IsCode(code int) bool { + return code == 404 +} + +func (o *V2UpdateClusterFinalizingProgressNotFound) Error() string { + return fmt.Sprintf("[PUT /v2/clusters/{cluster_id}/progress][%d] v2UpdateClusterFinalizingProgressNotFound %+v", 404, o.Payload) +} + +func (o *V2UpdateClusterFinalizingProgressNotFound) String() string { + return fmt.Sprintf("[PUT /v2/clusters/{cluster_id}/progress][%d] v2UpdateClusterFinalizingProgressNotFound %+v", 404, o.Payload) +} + +func (o *V2UpdateClusterFinalizingProgressNotFound) GetPayload() *models.Error { + return o.Payload +} + +func (o *V2UpdateClusterFinalizingProgressNotFound) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.Error) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewV2UpdateClusterFinalizingProgressMethodNotAllowed creates a V2UpdateClusterFinalizingProgressMethodNotAllowed with default headers values +func NewV2UpdateClusterFinalizingProgressMethodNotAllowed() *V2UpdateClusterFinalizingProgressMethodNotAllowed { + return &V2UpdateClusterFinalizingProgressMethodNotAllowed{} +} + +/* +V2UpdateClusterFinalizingProgressMethodNotAllowed describes a response with status code 405, with default header values. + +Method Not Allowed. +*/ +type V2UpdateClusterFinalizingProgressMethodNotAllowed struct { + Payload *models.Error +} + +// IsSuccess returns true when this v2 update cluster finalizing progress method not allowed response has a 2xx status code +func (o *V2UpdateClusterFinalizingProgressMethodNotAllowed) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this v2 update cluster finalizing progress method not allowed response has a 3xx status code +func (o *V2UpdateClusterFinalizingProgressMethodNotAllowed) IsRedirect() bool { + return false +} + +// IsClientError returns true when this v2 update cluster finalizing progress method not allowed response has a 4xx status code +func (o *V2UpdateClusterFinalizingProgressMethodNotAllowed) IsClientError() bool { + return true +} + +// IsServerError returns true when this v2 update cluster finalizing progress method not allowed response has a 5xx status code +func (o *V2UpdateClusterFinalizingProgressMethodNotAllowed) IsServerError() bool { + return false +} + +// IsCode returns true when this v2 update cluster finalizing progress method not allowed response a status code equal to that given +func (o *V2UpdateClusterFinalizingProgressMethodNotAllowed) IsCode(code int) bool { + return code == 405 +} + +func (o *V2UpdateClusterFinalizingProgressMethodNotAllowed) Error() string { + return fmt.Sprintf("[PUT /v2/clusters/{cluster_id}/progress][%d] v2UpdateClusterFinalizingProgressMethodNotAllowed %+v", 405, o.Payload) +} + +func (o *V2UpdateClusterFinalizingProgressMethodNotAllowed) String() string { + return fmt.Sprintf("[PUT /v2/clusters/{cluster_id}/progress][%d] v2UpdateClusterFinalizingProgressMethodNotAllowed %+v", 405, o.Payload) +} + +func (o *V2UpdateClusterFinalizingProgressMethodNotAllowed) GetPayload() *models.Error { + return o.Payload +} + +func (o *V2UpdateClusterFinalizingProgressMethodNotAllowed) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.Error) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewV2UpdateClusterFinalizingProgressInternalServerError creates a V2UpdateClusterFinalizingProgressInternalServerError with default headers values +func NewV2UpdateClusterFinalizingProgressInternalServerError() *V2UpdateClusterFinalizingProgressInternalServerError { + return &V2UpdateClusterFinalizingProgressInternalServerError{} +} + +/* +V2UpdateClusterFinalizingProgressInternalServerError describes a response with status code 500, with default header values. + +Error. +*/ +type V2UpdateClusterFinalizingProgressInternalServerError struct { + Payload *models.Error +} + +// IsSuccess returns true when this v2 update cluster finalizing progress internal server error response has a 2xx status code +func (o *V2UpdateClusterFinalizingProgressInternalServerError) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this v2 update cluster finalizing progress internal server error response has a 3xx status code +func (o *V2UpdateClusterFinalizingProgressInternalServerError) IsRedirect() bool { + return false +} + +// IsClientError returns true when this v2 update cluster finalizing progress internal server error response has a 4xx status code +func (o *V2UpdateClusterFinalizingProgressInternalServerError) IsClientError() bool { + return false +} + +// IsServerError returns true when this v2 update cluster finalizing progress internal server error response has a 5xx status code +func (o *V2UpdateClusterFinalizingProgressInternalServerError) IsServerError() bool { + return true +} + +// IsCode returns true when this v2 update cluster finalizing progress internal server error response a status code equal to that given +func (o *V2UpdateClusterFinalizingProgressInternalServerError) IsCode(code int) bool { + return code == 500 +} + +func (o *V2UpdateClusterFinalizingProgressInternalServerError) Error() string { + return fmt.Sprintf("[PUT /v2/clusters/{cluster_id}/progress][%d] v2UpdateClusterFinalizingProgressInternalServerError %+v", 500, o.Payload) +} + +func (o *V2UpdateClusterFinalizingProgressInternalServerError) String() string { + return fmt.Sprintf("[PUT /v2/clusters/{cluster_id}/progress][%d] v2UpdateClusterFinalizingProgressInternalServerError %+v", 500, o.Payload) +} + +func (o *V2UpdateClusterFinalizingProgressInternalServerError) GetPayload() *models.Error { + return o.Payload +} + +func (o *V2UpdateClusterFinalizingProgressInternalServerError) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.Error) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewV2UpdateClusterFinalizingProgressServiceUnavailable creates a V2UpdateClusterFinalizingProgressServiceUnavailable with default headers values +func NewV2UpdateClusterFinalizingProgressServiceUnavailable() *V2UpdateClusterFinalizingProgressServiceUnavailable { + return &V2UpdateClusterFinalizingProgressServiceUnavailable{} +} + +/* +V2UpdateClusterFinalizingProgressServiceUnavailable describes a response with status code 503, with default header values. + +Unavailable. +*/ +type V2UpdateClusterFinalizingProgressServiceUnavailable struct { + Payload *models.Error +} + +// IsSuccess returns true when this v2 update cluster finalizing progress service unavailable response has a 2xx status code +func (o *V2UpdateClusterFinalizingProgressServiceUnavailable) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this v2 update cluster finalizing progress service unavailable response has a 3xx status code +func (o *V2UpdateClusterFinalizingProgressServiceUnavailable) IsRedirect() bool { + return false +} + +// IsClientError returns true when this v2 update cluster finalizing progress service unavailable response has a 4xx status code +func (o *V2UpdateClusterFinalizingProgressServiceUnavailable) IsClientError() bool { + return false +} + +// IsServerError returns true when this v2 update cluster finalizing progress service unavailable response has a 5xx status code +func (o *V2UpdateClusterFinalizingProgressServiceUnavailable) IsServerError() bool { + return true +} + +// IsCode returns true when this v2 update cluster finalizing progress service unavailable response a status code equal to that given +func (o *V2UpdateClusterFinalizingProgressServiceUnavailable) IsCode(code int) bool { + return code == 503 +} + +func (o *V2UpdateClusterFinalizingProgressServiceUnavailable) Error() string { + return fmt.Sprintf("[PUT /v2/clusters/{cluster_id}/progress][%d] v2UpdateClusterFinalizingProgressServiceUnavailable %+v", 503, o.Payload) +} + +func (o *V2UpdateClusterFinalizingProgressServiceUnavailable) String() string { + return fmt.Sprintf("[PUT /v2/clusters/{cluster_id}/progress][%d] v2UpdateClusterFinalizingProgressServiceUnavailable %+v", 503, o.Payload) +} + +func (o *V2UpdateClusterFinalizingProgressServiceUnavailable) GetPayload() *models.Error { + return o.Payload +} + +func (o *V2UpdateClusterFinalizingProgressServiceUnavailable) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.Error) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} diff --git a/vendor/github.com/openshift/assisted-service/client/manifests/v2_list_cluster_manifests_parameters.go b/vendor/github.com/openshift/assisted-service/client/manifests/v2_list_cluster_manifests_parameters.go index 51445875b73..1d40fa9f783 100644 --- a/vendor/github.com/openshift/assisted-service/client/manifests/v2_list_cluster_manifests_parameters.go +++ b/vendor/github.com/openshift/assisted-service/client/manifests/v2_list_cluster_manifests_parameters.go @@ -14,6 +14,7 @@ import ( "github.com/go-openapi/runtime" cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" ) // NewV2ListClusterManifestsParams creates a new V2ListClusterManifestsParams object, @@ -69,6 +70,12 @@ type V2ListClusterManifestsParams struct { */ ClusterID strfmt.UUID + /* IncludeSystemGenerated. + + Include system generated manifests in results? Default is false. + */ + IncludeSystemGenerated *bool + timeout time.Duration Context context.Context HTTPClient *http.Client @@ -86,7 +93,18 @@ func (o *V2ListClusterManifestsParams) WithDefaults() *V2ListClusterManifestsPar // // All values with no default are reset to their zero value. func (o *V2ListClusterManifestsParams) SetDefaults() { - // no default values defined for this parameter + var ( + includeSystemGeneratedDefault = bool(false) + ) + + val := V2ListClusterManifestsParams{ + IncludeSystemGenerated: &includeSystemGeneratedDefault, + } + + val.timeout = o.timeout + val.Context = o.Context + val.HTTPClient = o.HTTPClient + *o = val } // WithTimeout adds the timeout to the v2 list cluster manifests params @@ -133,6 +151,17 @@ func (o *V2ListClusterManifestsParams) SetClusterID(clusterID strfmt.UUID) { o.ClusterID = clusterID } +// WithIncludeSystemGenerated adds the includeSystemGenerated to the v2 list cluster manifests params +func (o *V2ListClusterManifestsParams) WithIncludeSystemGenerated(includeSystemGenerated *bool) *V2ListClusterManifestsParams { + o.SetIncludeSystemGenerated(includeSystemGenerated) + return o +} + +// SetIncludeSystemGenerated adds the includeSystemGenerated to the v2 list cluster manifests params +func (o *V2ListClusterManifestsParams) SetIncludeSystemGenerated(includeSystemGenerated *bool) { + o.IncludeSystemGenerated = includeSystemGenerated +} + // WriteToRequest writes these params to a swagger request func (o *V2ListClusterManifestsParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { @@ -146,6 +175,23 @@ func (o *V2ListClusterManifestsParams) WriteToRequest(r runtime.ClientRequest, r return err } + if o.IncludeSystemGenerated != nil { + + // query param include_system_generated + var qrIncludeSystemGenerated bool + + if o.IncludeSystemGenerated != nil { + qrIncludeSystemGenerated = *o.IncludeSystemGenerated + } + qIncludeSystemGenerated := swag.FormatBool(qrIncludeSystemGenerated) + if qIncludeSystemGenerated != "" { + + if err := r.SetQueryParam("include_system_generated", qIncludeSystemGenerated); err != nil { + return err + } + } + } + if len(res) > 0 { return errors.CompositeValidationError(res...) } diff --git a/vendor/github.com/openshift/assisted-service/client/versions/v2_list_release_sources_parameters.go b/vendor/github.com/openshift/assisted-service/client/versions/v2_list_release_sources_parameters.go new file mode 100644 index 00000000000..f80a78d7927 --- /dev/null +++ b/vendor/github.com/openshift/assisted-service/client/versions/v2_list_release_sources_parameters.go @@ -0,0 +1,128 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package versions + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" +) + +// NewV2ListReleaseSourcesParams creates a new V2ListReleaseSourcesParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewV2ListReleaseSourcesParams() *V2ListReleaseSourcesParams { + return &V2ListReleaseSourcesParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewV2ListReleaseSourcesParamsWithTimeout creates a new V2ListReleaseSourcesParams object +// with the ability to set a timeout on a request. +func NewV2ListReleaseSourcesParamsWithTimeout(timeout time.Duration) *V2ListReleaseSourcesParams { + return &V2ListReleaseSourcesParams{ + timeout: timeout, + } +} + +// NewV2ListReleaseSourcesParamsWithContext creates a new V2ListReleaseSourcesParams object +// with the ability to set a context for a request. +func NewV2ListReleaseSourcesParamsWithContext(ctx context.Context) *V2ListReleaseSourcesParams { + return &V2ListReleaseSourcesParams{ + Context: ctx, + } +} + +// NewV2ListReleaseSourcesParamsWithHTTPClient creates a new V2ListReleaseSourcesParams object +// with the ability to set a custom HTTPClient for a request. +func NewV2ListReleaseSourcesParamsWithHTTPClient(client *http.Client) *V2ListReleaseSourcesParams { + return &V2ListReleaseSourcesParams{ + HTTPClient: client, + } +} + +/* +V2ListReleaseSourcesParams contains all the parameters to send to the API endpoint + + for the v2 list release sources operation. + + Typically these are written to a http.Request. +*/ +type V2ListReleaseSourcesParams struct { + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the v2 list release sources params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *V2ListReleaseSourcesParams) WithDefaults() *V2ListReleaseSourcesParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the v2 list release sources params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *V2ListReleaseSourcesParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the v2 list release sources params +func (o *V2ListReleaseSourcesParams) WithTimeout(timeout time.Duration) *V2ListReleaseSourcesParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the v2 list release sources params +func (o *V2ListReleaseSourcesParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the v2 list release sources params +func (o *V2ListReleaseSourcesParams) WithContext(ctx context.Context) *V2ListReleaseSourcesParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the v2 list release sources params +func (o *V2ListReleaseSourcesParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the v2 list release sources params +func (o *V2ListReleaseSourcesParams) WithHTTPClient(client *http.Client) *V2ListReleaseSourcesParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the v2 list release sources params +func (o *V2ListReleaseSourcesParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WriteToRequest writes these params to a swagger request +func (o *V2ListReleaseSourcesParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/vendor/github.com/openshift/assisted-service/client/versions/v2_list_release_sources_responses.go b/vendor/github.com/openshift/assisted-service/client/versions/v2_list_release_sources_responses.go new file mode 100644 index 00000000000..ff186714b2c --- /dev/null +++ b/vendor/github.com/openshift/assisted-service/client/versions/v2_list_release_sources_responses.go @@ -0,0 +1,96 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package versions + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + "github.com/openshift/assisted-service/models" +) + +// V2ListReleaseSourcesReader is a Reader for the V2ListReleaseSources structure. +type V2ListReleaseSourcesReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *V2ListReleaseSourcesReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewV2ListReleaseSourcesOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + default: + return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code()) + } +} + +// NewV2ListReleaseSourcesOK creates a V2ListReleaseSourcesOK with default headers values +func NewV2ListReleaseSourcesOK() *V2ListReleaseSourcesOK { + return &V2ListReleaseSourcesOK{} +} + +/* +V2ListReleaseSourcesOK describes a response with status code 200, with default header values. + +Success. +*/ +type V2ListReleaseSourcesOK struct { + Payload models.ReleaseSources +} + +// IsSuccess returns true when this v2 list release sources o k response has a 2xx status code +func (o *V2ListReleaseSourcesOK) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this v2 list release sources o k response has a 3xx status code +func (o *V2ListReleaseSourcesOK) IsRedirect() bool { + return false +} + +// IsClientError returns true when this v2 list release sources o k response has a 4xx status code +func (o *V2ListReleaseSourcesOK) IsClientError() bool { + return false +} + +// IsServerError returns true when this v2 list release sources o k response has a 5xx status code +func (o *V2ListReleaseSourcesOK) IsServerError() bool { + return false +} + +// IsCode returns true when this v2 list release sources o k response a status code equal to that given +func (o *V2ListReleaseSourcesOK) IsCode(code int) bool { + return code == 200 +} + +func (o *V2ListReleaseSourcesOK) Error() string { + return fmt.Sprintf("[GET /v2/release-sources][%d] v2ListReleaseSourcesOK %+v", 200, o.Payload) +} + +func (o *V2ListReleaseSourcesOK) String() string { + return fmt.Sprintf("[GET /v2/release-sources][%d] v2ListReleaseSourcesOK %+v", 200, o.Payload) +} + +func (o *V2ListReleaseSourcesOK) GetPayload() models.ReleaseSources { + return o.Payload +} + +func (o *V2ListReleaseSourcesOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} diff --git a/vendor/github.com/openshift/assisted-service/client/versions/v2_list_supported_openshift_versions_parameters.go b/vendor/github.com/openshift/assisted-service/client/versions/v2_list_supported_openshift_versions_parameters.go index 6f869f404ed..2af48ec9770 100644 --- a/vendor/github.com/openshift/assisted-service/client/versions/v2_list_supported_openshift_versions_parameters.go +++ b/vendor/github.com/openshift/assisted-service/client/versions/v2_list_supported_openshift_versions_parameters.go @@ -14,6 +14,7 @@ import ( "github.com/go-openapi/runtime" cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" ) // NewV2ListSupportedOpenshiftVersionsParams creates a new V2ListSupportedOpenshiftVersionsParams object, @@ -60,6 +61,19 @@ V2ListSupportedOpenshiftVersionsParams contains all the parameters to send to th Typically these are written to a http.Request. */ type V2ListSupportedOpenshiftVersionsParams struct { + + /* OnlyLatest. + + If true, returns only the latest version for each minor. + */ + OnlyLatest *bool + + /* Version. + + Retrieves only the versions that contain the specified substring in their display name. + */ + Version *string + timeout time.Duration Context context.Context HTTPClient *http.Client @@ -113,6 +127,28 @@ func (o *V2ListSupportedOpenshiftVersionsParams) SetHTTPClient(client *http.Clie o.HTTPClient = client } +// WithOnlyLatest adds the onlyLatest to the v2 list supported openshift versions params +func (o *V2ListSupportedOpenshiftVersionsParams) WithOnlyLatest(onlyLatest *bool) *V2ListSupportedOpenshiftVersionsParams { + o.SetOnlyLatest(onlyLatest) + return o +} + +// SetOnlyLatest adds the onlyLatest to the v2 list supported openshift versions params +func (o *V2ListSupportedOpenshiftVersionsParams) SetOnlyLatest(onlyLatest *bool) { + o.OnlyLatest = onlyLatest +} + +// WithVersion adds the version to the v2 list supported openshift versions params +func (o *V2ListSupportedOpenshiftVersionsParams) WithVersion(version *string) *V2ListSupportedOpenshiftVersionsParams { + o.SetVersion(version) + return o +} + +// SetVersion adds the version to the v2 list supported openshift versions params +func (o *V2ListSupportedOpenshiftVersionsParams) SetVersion(version *string) { + o.Version = version +} + // WriteToRequest writes these params to a swagger request func (o *V2ListSupportedOpenshiftVersionsParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { @@ -121,6 +157,40 @@ func (o *V2ListSupportedOpenshiftVersionsParams) WriteToRequest(r runtime.Client } var res []error + if o.OnlyLatest != nil { + + // query param only_latest + var qrOnlyLatest bool + + if o.OnlyLatest != nil { + qrOnlyLatest = *o.OnlyLatest + } + qOnlyLatest := swag.FormatBool(qrOnlyLatest) + if qOnlyLatest != "" { + + if err := r.SetQueryParam("only_latest", qOnlyLatest); err != nil { + return err + } + } + } + + if o.Version != nil { + + // query param version + var qrVersion string + + if o.Version != nil { + qrVersion = *o.Version + } + qVersion := qrVersion + if qVersion != "" { + + if err := r.SetQueryParam("version", qVersion); err != nil { + return err + } + } + } + if len(res) > 0 { return errors.CompositeValidationError(res...) } diff --git a/vendor/github.com/openshift/assisted-service/client/versions/versions_client.go b/vendor/github.com/openshift/assisted-service/client/versions/versions_client.go index 6365c153f5a..42947354c21 100644 --- a/vendor/github.com/openshift/assisted-service/client/versions/versions_client.go +++ b/vendor/github.com/openshift/assisted-service/client/versions/versions_client.go @@ -20,6 +20,9 @@ type API interface { /* V2ListComponentVersions List of component versions.*/ V2ListComponentVersions(ctx context.Context, params *V2ListComponentVersionsParams) (*V2ListComponentVersionsOK, error) + /* + V2ListReleaseSources Retrieves openshift release sources configuration.*/ + V2ListReleaseSources(ctx context.Context, params *V2ListReleaseSourcesParams) (*V2ListReleaseSourcesOK, error) /* V2ListSupportedOpenshiftVersions Retrieves the list of OpenShift supported versions.*/ V2ListSupportedOpenshiftVersions(ctx context.Context, params *V2ListSupportedOpenshiftVersionsParams) (*V2ListSupportedOpenshiftVersionsOK, error) @@ -68,6 +71,31 @@ func (a *Client) V2ListComponentVersions(ctx context.Context, params *V2ListComp } +/* +V2ListReleaseSources Retrieves openshift release sources configuration. +*/ +func (a *Client) V2ListReleaseSources(ctx context.Context, params *V2ListReleaseSourcesParams) (*V2ListReleaseSourcesOK, error) { + + result, err := a.transport.Submit(&runtime.ClientOperation{ + ID: "v2ListReleaseSources", + Method: "GET", + PathPattern: "/v2/release-sources", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"http", "https"}, + Params: params, + Reader: &V2ListReleaseSourcesReader{formats: a.formats}, + AuthInfo: a.authInfo, + Context: ctx, + Client: params.HTTPClient, + }) + if err != nil { + return nil, err + } + return result.(*V2ListReleaseSourcesOK), nil + +} + /* V2ListSupportedOpenshiftVersions Retrieves the list of OpenShift supported versions. */ diff --git a/vendor/github.com/openshift/assisted-service/models/api_vip_connectivity_additional_request_header.go b/vendor/github.com/openshift/assisted-service/models/api_vip_connectivity_additional_request_header.go new file mode 100644 index 00000000000..96e283efbe4 --- /dev/null +++ b/vendor/github.com/openshift/assisted-service/models/api_vip_connectivity_additional_request_header.go @@ -0,0 +1,53 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// APIVipConnectivityAdditionalRequestHeader api vip connectivity additional request header +// +// swagger:model api_vip_connectivity_additional_request_header +type APIVipConnectivityAdditionalRequestHeader struct { + + // Value of the header's key when making a request + Key string `json:"key,omitempty"` + + // The value corresponding to the header key + Value string `json:"value,omitempty"` +} + +// Validate validates this api vip connectivity additional request header +func (m *APIVipConnectivityAdditionalRequestHeader) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this api vip connectivity additional request header based on context it is used +func (m *APIVipConnectivityAdditionalRequestHeader) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *APIVipConnectivityAdditionalRequestHeader) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *APIVipConnectivityAdditionalRequestHeader) UnmarshalBinary(b []byte) error { + var res APIVipConnectivityAdditionalRequestHeader + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/vendor/github.com/openshift/assisted-service/models/api_vip_connectivity_request.go b/vendor/github.com/openshift/assisted-service/models/api_vip_connectivity_request.go index c29a608100b..a817154cd77 100644 --- a/vendor/github.com/openshift/assisted-service/models/api_vip_connectivity_request.go +++ b/vendor/github.com/openshift/assisted-service/models/api_vip_connectivity_request.go @@ -7,6 +7,7 @@ package models import ( "context" + "strconv" "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" @@ -22,9 +23,12 @@ type APIVipConnectivityRequest struct { // A CA certficate to be used when contacting the URL via https. CaCertificate *string `json:"ca_certificate,omitempty"` - // A string which will be used as Authorization Bearer token to fetch the ignition from ignition_endpoint_url. + // A string which will be used as Authorization Bearer token to fetch the ignition from ignition_endpoint_url (DEPRECATED use request_headers to pass this token). IgnitionEndpointToken *string `json:"ignition_endpoint_token,omitempty"` + // Additional request headers to include when fetching the ignition from ignition_endpoint_url. + RequestHeaders []*APIVipConnectivityAdditionalRequestHeader `json:"request_headers,omitempty"` + // URL address of the API. // Required: true URL *string `json:"url"` @@ -37,6 +41,10 @@ type APIVipConnectivityRequest struct { func (m *APIVipConnectivityRequest) Validate(formats strfmt.Registry) error { var res []error + if err := m.validateRequestHeaders(formats); err != nil { + res = append(res, err) + } + if err := m.validateURL(formats); err != nil { res = append(res, err) } @@ -47,6 +55,32 @@ func (m *APIVipConnectivityRequest) Validate(formats strfmt.Registry) error { return nil } +func (m *APIVipConnectivityRequest) validateRequestHeaders(formats strfmt.Registry) error { + if swag.IsZero(m.RequestHeaders) { // not required + return nil + } + + for i := 0; i < len(m.RequestHeaders); i++ { + if swag.IsZero(m.RequestHeaders[i]) { // not required + continue + } + + if m.RequestHeaders[i] != nil { + if err := m.RequestHeaders[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("request_headers" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("request_headers" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + func (m *APIVipConnectivityRequest) validateURL(formats strfmt.Registry) error { if err := validate.Required("url", "body", m.URL); err != nil { @@ -56,8 +90,37 @@ func (m *APIVipConnectivityRequest) validateURL(formats strfmt.Registry) error { return nil } -// ContextValidate validates this api vip connectivity request based on context it is used +// ContextValidate validate this api vip connectivity request based on the context it is used func (m *APIVipConnectivityRequest) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateRequestHeaders(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *APIVipConnectivityRequest) contextValidateRequestHeaders(ctx context.Context, formats strfmt.Registry) error { + + for i := 0; i < len(m.RequestHeaders); i++ { + + if m.RequestHeaders[i] != nil { + if err := m.RequestHeaders[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("request_headers" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("request_headers" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + return nil } diff --git a/vendor/github.com/openshift/assisted-service/models/boot.go b/vendor/github.com/openshift/assisted-service/models/boot.go index 361ebd0a9c1..8b7defc9531 100644 --- a/vendor/github.com/openshift/assisted-service/models/boot.go +++ b/vendor/github.com/openshift/assisted-service/models/boot.go @@ -17,6 +17,9 @@ import ( // swagger:model boot type Boot struct { + // command line + CommandLine string `json:"command_line,omitempty"` + // current boot mode CurrentBootMode string `json:"current_boot_mode,omitempty"` diff --git a/vendor/github.com/openshift/assisted-service/models/cluster.go b/vendor/github.com/openshift/assisted-service/models/cluster.go index ae27513916a..2ef415d2019 100644 --- a/vendor/github.com/openshift/assisted-service/models/cluster.go +++ b/vendor/github.com/openshift/assisted-service/models/cluster.go @@ -30,10 +30,6 @@ type Cluster struct { // Format: uuid AmsSubscriptionID strfmt.UUID `json:"ams_subscription_id,omitempty"` - // (DEPRECATED) The virtual IP used to reach the OpenShift cluster's API. - // Pattern: ^(?:(?:(?:[0-9]{1,3}\.){3}[0-9]{1,3})|(?:(?:[0-9a-fA-F]*:[0-9a-fA-F]*){2,}))$ - APIVip string `json:"api_vip,omitempty"` - // The domain name used to reach the OpenShift cluster API. APIVipDNSName *string `json:"api_vip_dns_name,omitempty"` @@ -147,10 +143,6 @@ type Cluster struct { // reflect the actual cluster they represent Imported *bool `json:"imported,omitempty"` - // (DEPRECATED) The virtual IP used for cluster ingress traffic. - // Pattern: ^(?:(?:(?:[0-9]{1,3}\.){3}[0-9]{1,3})|(?:(?:[0-9a-fA-F]*:[0-9a-fA-F]*){2,}))$ - IngressVip string `json:"ingress_vip,omitempty"` - // The virtual IPs used for cluster ingress traffic. Enter one IP address for single-stack clusters, or up to two for dual-stack clusters (at most one IP address per IP stack used). The order of stacks should be the same as order of subnets in Cluster Networks, Service Networks, and Machine Networks. IngressVips []*IngressVip `json:"ingress_vips" gorm:"foreignkey:ClusterID;references:ID"` @@ -176,6 +168,9 @@ type Cluster struct { // Enum: [Cluster AddHostsCluster] Kind *string `json:"kind"` + // last installation preparation + LastInstallationPreparation LastInstallationPreparation `json:"last-installation-preparation,omitempty" gorm:"embedded;embeddedPrefix:last_installation_preparation_"` + // The progress of log collection or empty if logs are not applicable LogsInfo LogsState `json:"logs_info,omitempty" gorm:"type:varchar(2048)"` @@ -212,6 +207,9 @@ type Cluster struct { // org id OrgID string `json:"org_id,omitempty"` + // Indication if organization soft timeouts is enabled for the cluster. + OrgSoftTimeoutsEnabled bool `json:"org_soft_timeouts_enabled,omitempty"` + // platform Platform *Platform `json:"platform,omitempty" gorm:"embedded;embeddedPrefix:platform_"` @@ -286,10 +284,6 @@ func (m *Cluster) Validate(formats strfmt.Registry) error { res = append(res, err) } - if err := m.validateAPIVip(formats); err != nil { - res = append(res, err) - } - if err := m.validateAPIVips(formats); err != nil { res = append(res, err) } @@ -358,10 +352,6 @@ func (m *Cluster) Validate(formats strfmt.Registry) error { res = append(res, err) } - if err := m.validateIngressVip(formats); err != nil { - res = append(res, err) - } - if err := m.validateIngressVips(formats); err != nil { res = append(res, err) } @@ -378,6 +368,10 @@ func (m *Cluster) Validate(formats strfmt.Registry) error { res = append(res, err) } + if err := m.validateLastInstallationPreparation(formats); err != nil { + res = append(res, err) + } + if err := m.validateLogsInfo(formats); err != nil { res = append(res, err) } @@ -452,18 +446,6 @@ func (m *Cluster) validateAmsSubscriptionID(formats strfmt.Registry) error { return nil } -func (m *Cluster) validateAPIVip(formats strfmt.Registry) error { - if swag.IsZero(m.APIVip) { // not required - return nil - } - - if err := validate.Pattern("api_vip", "body", m.APIVip, `^(?:(?:(?:[0-9]{1,3}\.){3}[0-9]{1,3})|(?:(?:[0-9a-fA-F]*:[0-9a-fA-F]*){2,}))$`); err != nil { - return err - } - - return nil -} - func (m *Cluster) validateAPIVips(formats strfmt.Registry) error { if swag.IsZero(m.APIVips) { // not required return nil @@ -856,18 +838,6 @@ func (m *Cluster) validateImageInfo(formats strfmt.Registry) error { return nil } -func (m *Cluster) validateIngressVip(formats strfmt.Registry) error { - if swag.IsZero(m.IngressVip) { // not required - return nil - } - - if err := validate.Pattern("ingress_vip", "body", m.IngressVip, `^(?:(?:(?:[0-9]{1,3}\.){3}[0-9]{1,3})|(?:(?:[0-9a-fA-F]*:[0-9a-fA-F]*){2,}))$`); err != nil { - return err - } - - return nil -} - func (m *Cluster) validateIngressVips(formats strfmt.Registry) error { if swag.IsZero(m.IngressVips) { // not required return nil @@ -961,6 +931,23 @@ func (m *Cluster) validateKind(formats strfmt.Registry) error { return nil } +func (m *Cluster) validateLastInstallationPreparation(formats strfmt.Registry) error { + if swag.IsZero(m.LastInstallationPreparation) { // not required + return nil + } + + if err := m.LastInstallationPreparation.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("last-installation-preparation") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("last-installation-preparation") + } + return err + } + + return nil +} + func (m *Cluster) validateLogsInfo(formats strfmt.Registry) error { if swag.IsZero(m.LogsInfo) { // not required return nil @@ -1311,6 +1298,10 @@ func (m *Cluster) ContextValidate(ctx context.Context, formats strfmt.Registry) res = append(res, err) } + if err := m.contextValidateLastInstallationPreparation(ctx, formats); err != nil { + res = append(res, err) + } + if err := m.contextValidateLogsInfo(ctx, formats); err != nil { res = append(res, err) } @@ -1489,6 +1480,20 @@ func (m *Cluster) contextValidateIngressVips(ctx context.Context, formats strfmt return nil } +func (m *Cluster) contextValidateLastInstallationPreparation(ctx context.Context, formats strfmt.Registry) error { + + if err := m.LastInstallationPreparation.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("last-installation-preparation") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("last-installation-preparation") + } + return err + } + + return nil +} + func (m *Cluster) contextValidateLogsInfo(ctx context.Context, formats strfmt.Registry) error { if err := m.LogsInfo.ContextValidate(ctx, formats); err != nil { diff --git a/vendor/github.com/openshift/assisted-service/models/cluster_create_params.go b/vendor/github.com/openshift/assisted-service/models/cluster_create_params.go index 432ab0ed9a2..3eeade9fcdc 100644 --- a/vendor/github.com/openshift/assisted-service/models/cluster_create_params.go +++ b/vendor/github.com/openshift/assisted-service/models/cluster_create_params.go @@ -24,10 +24,6 @@ type ClusterCreateParams struct { // A comma-separated list of NTP sources (name or IP) going to be added to all the hosts. AdditionalNtpSource *string `json:"additional_ntp_source,omitempty"` - // (DEPRECATED) The virtual IP used to reach the OpenShift cluster's API. - // Pattern: ^(?:(?:(?:[0-9]{1,3}\.){3}[0-9]{1,3})|(?:(?:[0-9a-fA-F]*:[0-9a-fA-F]*){2,}))?$ - APIVip string `json:"api_vip,omitempty"` - // The virtual IPs used to reach the OpenShift cluster's API. Enter one IP address for single-stack clusters, or up to two for dual-stack clusters (at most one IP address per IP stack used). The order of stacks should be the same as order of subnets in Cluster Networks, Service Networks, and Machine Networks. APIVips []*APIVip `json:"api_vips"` @@ -76,10 +72,6 @@ type ClusterCreateParams struct { // Explicit ignition endpoint overrides the default ignition endpoint. IgnitionEndpoint *IgnitionEndpoint `json:"ignition_endpoint,omitempty" gorm:"embedded;embeddedPrefix:ignition_endpoint_"` - // (DEPRECATED) The virtual IP used for cluster ingress traffic. - // Pattern: ^(?:(?:(?:[0-9]{1,3}\.){3}[0-9]{1,3})|(?:(?:[0-9a-fA-F]*:[0-9a-fA-F]*){2,}))$ - IngressVip string `json:"ingress_vip,omitempty"` - // The virtual IPs used for cluster ingress traffic. Enter one IP address for single-stack clusters, or up to two for dual-stack clusters (at most one IP address per IP stack used). The order of stacks should be the same as order of subnets in Cluster Networks, Service Networks, and Machine Networks. IngressVips []*IngressVip `json:"ingress_vips"` @@ -143,10 +135,6 @@ type ClusterCreateParams struct { func (m *ClusterCreateParams) Validate(formats strfmt.Registry) error { var res []error - if err := m.validateAPIVip(formats); err != nil { - res = append(res, err) - } - if err := m.validateAPIVips(formats); err != nil { res = append(res, err) } @@ -183,10 +171,6 @@ func (m *ClusterCreateParams) Validate(formats strfmt.Registry) error { res = append(res, err) } - if err := m.validateIngressVip(formats); err != nil { - res = append(res, err) - } - if err := m.validateIngressVips(formats); err != nil { res = append(res, err) } @@ -233,18 +217,6 @@ func (m *ClusterCreateParams) Validate(formats strfmt.Registry) error { return nil } -func (m *ClusterCreateParams) validateAPIVip(formats strfmt.Registry) error { - if swag.IsZero(m.APIVip) { // not required - return nil - } - - if err := validate.Pattern("api_vip", "body", m.APIVip, `^(?:(?:(?:[0-9]{1,3}\.){3}[0-9]{1,3})|(?:(?:[0-9a-fA-F]*:[0-9a-fA-F]*){2,}))?$`); err != nil { - return err - } - - return nil -} - func (m *ClusterCreateParams) validateAPIVips(formats strfmt.Registry) error { if swag.IsZero(m.APIVips) { // not required return nil @@ -507,18 +479,6 @@ func (m *ClusterCreateParams) validateIgnitionEndpoint(formats strfmt.Registry) return nil } -func (m *ClusterCreateParams) validateIngressVip(formats strfmt.Registry) error { - if swag.IsZero(m.IngressVip) { // not required - return nil - } - - if err := validate.Pattern("ingress_vip", "body", m.IngressVip, `^(?:(?:(?:[0-9]{1,3}\.){3}[0-9]{1,3})|(?:(?:[0-9a-fA-F]*:[0-9a-fA-F]*){2,}))$`); err != nil { - return err - } - - return nil -} - func (m *ClusterCreateParams) validateIngressVips(formats strfmt.Registry) error { if swag.IsZero(m.IngressVips) { // not required return nil diff --git a/vendor/github.com/openshift/assisted-service/models/cluster_finalizing_progress.go b/vendor/github.com/openshift/assisted-service/models/cluster_finalizing_progress.go new file mode 100644 index 00000000000..50e324845e2 --- /dev/null +++ b/vendor/github.com/openshift/assisted-service/models/cluster_finalizing_progress.go @@ -0,0 +1,100 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// ClusterFinalizingProgress cluster finalizing progress +// +// swagger:model cluster-finalizing-progress +type ClusterFinalizingProgress struct { + + // finalizing stage + FinalizingStage FinalizingStage `json:"finalizing_stage,omitempty"` +} + +// Validate validates this cluster finalizing progress +func (m *ClusterFinalizingProgress) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateFinalizingStage(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *ClusterFinalizingProgress) validateFinalizingStage(formats strfmt.Registry) error { + if swag.IsZero(m.FinalizingStage) { // not required + return nil + } + + if err := m.FinalizingStage.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("finalizing_stage") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("finalizing_stage") + } + return err + } + + return nil +} + +// ContextValidate validate this cluster finalizing progress based on the context it is used +func (m *ClusterFinalizingProgress) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateFinalizingStage(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *ClusterFinalizingProgress) contextValidateFinalizingStage(ctx context.Context, formats strfmt.Registry) error { + + if err := m.FinalizingStage.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("finalizing_stage") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("finalizing_stage") + } + return err + } + + return nil +} + +// MarshalBinary interface implementation +func (m *ClusterFinalizingProgress) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *ClusterFinalizingProgress) UnmarshalBinary(b []byte) error { + var res ClusterFinalizingProgress + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/vendor/github.com/openshift/assisted-service/models/cluster_progress_info.go b/vendor/github.com/openshift/assisted-service/models/cluster_progress_info.go index 5a2f49283b6..74a1060f06f 100644 --- a/vendor/github.com/openshift/assisted-service/models/cluster_progress_info.go +++ b/vendor/github.com/openshift/assisted-service/models/cluster_progress_info.go @@ -8,8 +8,10 @@ package models import ( "context" + "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" + "github.com/go-openapi/validate" ) // ClusterProgressInfo cluster progress info @@ -17,9 +19,19 @@ import ( // swagger:model cluster-progress-info type ClusterProgressInfo struct { + // finalizing stage + FinalizingStage FinalizingStage `json:"finalizing_stage,omitempty"` + // finalizing stage percentage FinalizingStagePercentage int64 `json:"finalizing_stage_percentage,omitempty"` + // finalizing stage started at + // Format: date-time + FinalizingStageStartedAt strfmt.DateTime `json:"finalizing_stage_started_at,omitempty" gorm:"type:timestamp with time zone"` + + // finalizing stage timed out + FinalizingStageTimedOut bool `json:"finalizing_stage_timed_out,omitempty"` + // installing stage percentage InstallingStagePercentage int64 `json:"installing_stage_percentage,omitempty"` @@ -32,11 +44,76 @@ type ClusterProgressInfo struct { // Validate validates this cluster progress info func (m *ClusterProgressInfo) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateFinalizingStage(formats); err != nil { + res = append(res, err) + } + + if err := m.validateFinalizingStageStartedAt(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *ClusterProgressInfo) validateFinalizingStage(formats strfmt.Registry) error { + if swag.IsZero(m.FinalizingStage) { // not required + return nil + } + + if err := m.FinalizingStage.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("finalizing_stage") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("finalizing_stage") + } + return err + } + + return nil +} + +func (m *ClusterProgressInfo) validateFinalizingStageStartedAt(formats strfmt.Registry) error { + if swag.IsZero(m.FinalizingStageStartedAt) { // not required + return nil + } + + if err := validate.FormatOf("finalizing_stage_started_at", "body", "date-time", m.FinalizingStageStartedAt.String(), formats); err != nil { + return err + } + return nil } -// ContextValidate validates this cluster progress info based on context it is used +// ContextValidate validate this cluster progress info based on the context it is used func (m *ClusterProgressInfo) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateFinalizingStage(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *ClusterProgressInfo) contextValidateFinalizingStage(ctx context.Context, formats strfmt.Registry) error { + + if err := m.FinalizingStage.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("finalizing_stage") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("finalizing_stage") + } + return err + } + return nil } diff --git a/vendor/github.com/openshift/assisted-service/models/create_manifest_params.go b/vendor/github.com/openshift/assisted-service/models/create_manifest_params.go index adad97f1d89..edbaa3ab75c 100644 --- a/vendor/github.com/openshift/assisted-service/models/create_manifest_params.go +++ b/vendor/github.com/openshift/assisted-service/models/create_manifest_params.go @@ -26,7 +26,7 @@ type CreateManifestParams struct { // The name of the manifest to customize the installed OCP cluster. // Required: true - // Pattern: ^[^/]*\.(yaml|yml|json)$ + // Pattern: ^[^\/]*\.(json|ya?ml(\.patch_?[a-zA-Z0-9_]*)?)$ FileName *string `json:"file_name"` // The folder that contains the files. Manifests can be placed in 'manifests' or 'openshift' directories. @@ -71,7 +71,7 @@ func (m *CreateManifestParams) validateFileName(formats strfmt.Registry) error { return err } - if err := validate.Pattern("file_name", "body", *m.FileName, `^[^/]*\.(yaml|yml|json)$`); err != nil { + if err := validate.Pattern("file_name", "body", *m.FileName, `^[^\/]*\.(json|ya?ml(\.patch_?[a-zA-Z0-9_]*)?)$`); err != nil { return err } diff --git a/vendor/github.com/openshift/assisted-service/models/custom.go b/vendor/github.com/openshift/assisted-service/models/custom.go new file mode 100644 index 00000000000..a1ad42b71ef --- /dev/null +++ b/vendor/github.com/openshift/assisted-service/models/custom.go @@ -0,0 +1,71 @@ +// custom.go file has custom models for assisted-service that are not +// auto-generated via the swagger.yaml file due to the need for custom +// validation or fields +package models + +import ( + "context" + + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" + "github.com/openshift/assisted-service/pkg/validations" +) + +// DomainResolutionRequestDomain is a struct to hold the domain resolution request domain +type DomainResolutionRequestDomain struct { + + // The domain name that should be resolved + // Required: true + DomainName *string `json:"domain_name"` +} + +// Validate is a function required for interfaces derived from swagger models and it +// validates this domain resolution request domain +func (m *DomainResolutionRequestDomain) Validate(formats strfmt.Registry) error { + if err := m.validateDomainName(formats); err != nil { + return err + } + + return nil +} + +// validateDomainName ensures that the required DomainName field exists and that the +// DomainName is valid +func (m *DomainResolutionRequestDomain) validateDomainName(formats strfmt.Registry) error { + if err := validate.Required("domain_name", "body", m.DomainName); err != nil { + return err + } + + if _, err := validations.ValidateDomainNameFormat(*m.DomainName); err != nil { + return err + } + + return nil +} + +// The following functions (ContextValidate, MarshalBinary, UnmarshalBinary) are required for +// interfaces derived from swagger models + +// ContextValidate validates this domain resolution request domain based on context it is used +func (m *DomainResolutionRequestDomain) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *DomainResolutionRequestDomain) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *DomainResolutionRequestDomain) UnmarshalBinary(b []byte) error { + var res DomainResolutionRequestDomain + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/vendor/github.com/openshift/assisted-service/models/disk.go b/vendor/github.com/openshift/assisted-service/models/disk.go index 631c97a152b..7574de9e72b 100644 --- a/vendor/github.com/openshift/assisted-service/models/disk.go +++ b/vendor/github.com/openshift/assisted-service/models/disk.go @@ -51,12 +51,18 @@ type Disk struct { // Whether the disk appears to be an installation media or not IsInstallationMedia bool `json:"is_installation_media,omitempty"` + // iscsi + Iscsi *Iscsi `json:"iscsi,omitempty"` + // model Model string `json:"model,omitempty"` // name Name string `json:"name,omitempty"` + // partition types + PartitionTypes string `json:"partitionTypes,omitempty"` + // path Path string `json:"path,omitempty"` @@ -95,6 +101,10 @@ func (m *Disk) Validate(formats strfmt.Registry) error { res = append(res, err) } + if err := m.validateIscsi(formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { return errors.CompositeValidationError(res...) } @@ -154,6 +164,25 @@ func (m *Disk) validateIoPerf(formats strfmt.Registry) error { return nil } +func (m *Disk) validateIscsi(formats strfmt.Registry) error { + if swag.IsZero(m.Iscsi) { // not required + return nil + } + + if m.Iscsi != nil { + if err := m.Iscsi.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("iscsi") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("iscsi") + } + return err + } + } + + return nil +} + // ContextValidate validate this disk based on the context it is used func (m *Disk) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error @@ -170,6 +199,10 @@ func (m *Disk) ContextValidate(ctx context.Context, formats strfmt.Registry) err res = append(res, err) } + if err := m.contextValidateIscsi(ctx, formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { return errors.CompositeValidationError(res...) } @@ -220,6 +253,22 @@ func (m *Disk) contextValidateIoPerf(ctx context.Context, formats strfmt.Registr return nil } +func (m *Disk) contextValidateIscsi(ctx context.Context, formats strfmt.Registry) error { + + if m.Iscsi != nil { + if err := m.Iscsi.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("iscsi") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("iscsi") + } + return err + } + } + + return nil +} + // MarshalBinary interface implementation func (m *Disk) MarshalBinary() ([]byte, error) { if m == nil { diff --git a/vendor/github.com/openshift/assisted-service/models/domain_resolution_request.go b/vendor/github.com/openshift/assisted-service/models/domain_resolution_request.go index 030a4365401..13c3290d9c4 100644 --- a/vendor/github.com/openshift/assisted-service/models/domain_resolution_request.go +++ b/vendor/github.com/openshift/assisted-service/models/domain_resolution_request.go @@ -22,7 +22,7 @@ type DomainResolutionRequest struct { // domains // Required: true - Domains []*DomainResolutionRequestDomain `json:"domains"` + Domains []DomainResolutionRequestDomain `json:"domains"` } // Validate validates this domain resolution request @@ -46,19 +46,14 @@ func (m *DomainResolutionRequest) validateDomains(formats strfmt.Registry) error } for i := 0; i < len(m.Domains); i++ { - if swag.IsZero(m.Domains[i]) { // not required - continue - } - if m.Domains[i] != nil { - if err := m.Domains[i].Validate(formats); err != nil { - if ve, ok := err.(*errors.Validation); ok { - return ve.ValidateName("domains" + "." + strconv.Itoa(i)) - } else if ce, ok := err.(*errors.CompositeError); ok { - return ce.ValidateName("domains" + "." + strconv.Itoa(i)) - } - return err + if err := m.Domains[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("domains" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("domains" + "." + strconv.Itoa(i)) } + return err } } @@ -66,37 +61,8 @@ func (m *DomainResolutionRequest) validateDomains(formats strfmt.Registry) error return nil } -// ContextValidate validate this domain resolution request based on the context it is used +// ContextValidate validates this domain resolution request based on context it is used func (m *DomainResolutionRequest) ContextValidate(ctx context.Context, formats strfmt.Registry) error { - var res []error - - if err := m.contextValidateDomains(ctx, formats); err != nil { - res = append(res, err) - } - - if len(res) > 0 { - return errors.CompositeValidationError(res...) - } - return nil -} - -func (m *DomainResolutionRequest) contextValidateDomains(ctx context.Context, formats strfmt.Registry) error { - - for i := 0; i < len(m.Domains); i++ { - - if m.Domains[i] != nil { - if err := m.Domains[i].ContextValidate(ctx, formats); err != nil { - if ve, ok := err.(*errors.Validation); ok { - return ve.ValidateName("domains" + "." + strconv.Itoa(i)) - } else if ce, ok := err.(*errors.CompositeError); ok { - return ce.ValidateName("domains" + "." + strconv.Itoa(i)) - } - return err - } - } - - } - return nil } @@ -117,64 +83,3 @@ func (m *DomainResolutionRequest) UnmarshalBinary(b []byte) error { *m = res return nil } - -// DomainResolutionRequestDomain domain resolution request domain -// -// swagger:model DomainResolutionRequestDomain -type DomainResolutionRequestDomain struct { - - // The domain name that should be resolved - // Required: true - // Pattern: ^([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*[.])+[a-zA-Z]{2,}[.]?$ - DomainName *string `json:"domain_name"` -} - -// Validate validates this domain resolution request domain -func (m *DomainResolutionRequestDomain) Validate(formats strfmt.Registry) error { - var res []error - - if err := m.validateDomainName(formats); err != nil { - res = append(res, err) - } - - if len(res) > 0 { - return errors.CompositeValidationError(res...) - } - return nil -} - -func (m *DomainResolutionRequestDomain) validateDomainName(formats strfmt.Registry) error { - - if err := validate.Required("domain_name", "body", m.DomainName); err != nil { - return err - } - - if err := validate.Pattern("domain_name", "body", *m.DomainName, `^([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*[.])+[a-zA-Z]{2,}[.]?$`); err != nil { - return err - } - - return nil -} - -// ContextValidate validates this domain resolution request domain based on context it is used -func (m *DomainResolutionRequestDomain) ContextValidate(ctx context.Context, formats strfmt.Registry) error { - return nil -} - -// MarshalBinary interface implementation -func (m *DomainResolutionRequestDomain) MarshalBinary() ([]byte, error) { - if m == nil { - return nil, nil - } - return swag.WriteJSON(m) -} - -// UnmarshalBinary interface implementation -func (m *DomainResolutionRequestDomain) UnmarshalBinary(b []byte) error { - var res DomainResolutionRequestDomain - if err := swag.ReadJSON(b, &res); err != nil { - return err - } - *m = res - return nil -} diff --git a/vendor/github.com/openshift/assisted-service/models/domain_resolution_response.go b/vendor/github.com/openshift/assisted-service/models/domain_resolution_response.go index 79a1027fa36..4c8cb297a58 100644 --- a/vendor/github.com/openshift/assisted-service/models/domain_resolution_response.go +++ b/vendor/github.com/openshift/assisted-service/models/domain_resolution_response.go @@ -123,6 +123,9 @@ func (m *DomainResolutionResponse) UnmarshalBinary(b []byte) error { // swagger:model DomainResolutionResponseDomain type DomainResolutionResponseDomain struct { + // The cnames that were resolved for the domain, empty if none + Cnames []string `json:"cnames"` + // The domain that was resolved // Required: true DomainName *string `json:"domain_name"` diff --git a/vendor/github.com/openshift/assisted-service/models/feature_support_level_id.go b/vendor/github.com/openshift/assisted-service/models/feature_support_level_id.go index 36f7fc3c8ac..c5909e0af54 100644 --- a/vendor/github.com/openshift/assisted-service/models/feature_support_level_id.go +++ b/vendor/github.com/openshift/assisted-service/models/feature_support_level_id.go @@ -92,6 +92,18 @@ const ( // FeatureSupportLevelIDPLATFORMMANAGEDNETWORKING captures enum value "PLATFORM_MANAGED_NETWORKING" FeatureSupportLevelIDPLATFORMMANAGEDNETWORKING FeatureSupportLevelID = "PLATFORM_MANAGED_NETWORKING" + + // FeatureSupportLevelIDSKIPMCOREBOOT captures enum value "SKIP_MCO_REBOOT" + FeatureSupportLevelIDSKIPMCOREBOOT FeatureSupportLevelID = "SKIP_MCO_REBOOT" + + // FeatureSupportLevelIDEXTERNALPLATFORM captures enum value "EXTERNAL_PLATFORM" + FeatureSupportLevelIDEXTERNALPLATFORM FeatureSupportLevelID = "EXTERNAL_PLATFORM" + + // FeatureSupportLevelIDOVNNETWORKTYPE captures enum value "OVN_NETWORK_TYPE" + FeatureSupportLevelIDOVNNETWORKTYPE FeatureSupportLevelID = "OVN_NETWORK_TYPE" + + // FeatureSupportLevelIDSDNNETWORKTYPE captures enum value "SDN_NETWORK_TYPE" + FeatureSupportLevelIDSDNNETWORKTYPE FeatureSupportLevelID = "SDN_NETWORK_TYPE" ) // for schema @@ -99,7 +111,7 @@ var featureSupportLevelIdEnum []interface{} func init() { var res []FeatureSupportLevelID - if err := json.Unmarshal([]byte(`["SNO","VIP_AUTO_ALLOC","CUSTOM_MANIFEST","SINGLE_NODE_EXPANSION","LVM","ODF","LSO","CNV","MCE","NUTANIX_INTEGRATION","BAREMETAL_PLATFORM","NONE_PLATFORM","VSPHERE_INTEGRATION","DUAL_STACK_VIPS","CLUSTER_MANAGED_NETWORKING","USER_MANAGED_NETWORKING","MINIMAL_ISO","FULL_ISO","EXTERNAL_PLATFORM_OCI","DUAL_STACK","PLATFORM_MANAGED_NETWORKING"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["SNO","VIP_AUTO_ALLOC","CUSTOM_MANIFEST","SINGLE_NODE_EXPANSION","LVM","ODF","LSO","CNV","MCE","NUTANIX_INTEGRATION","BAREMETAL_PLATFORM","NONE_PLATFORM","VSPHERE_INTEGRATION","DUAL_STACK_VIPS","CLUSTER_MANAGED_NETWORKING","USER_MANAGED_NETWORKING","MINIMAL_ISO","FULL_ISO","EXTERNAL_PLATFORM_OCI","DUAL_STACK","PLATFORM_MANAGED_NETWORKING","SKIP_MCO_REBOOT","EXTERNAL_PLATFORM","OVN_NETWORK_TYPE","SDN_NETWORK_TYPE"]`), &res); err != nil { panic(err) } for _, v := range res { diff --git a/vendor/github.com/openshift/assisted-service/models/finalizing_stage.go b/vendor/github.com/openshift/assisted-service/models/finalizing_stage.go new file mode 100644 index 00000000000..b475cc58618 --- /dev/null +++ b/vendor/github.com/openshift/assisted-service/models/finalizing_stage.go @@ -0,0 +1,90 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "encoding/json" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/validate" +) + +// FinalizingStage Cluster finalizing stage managed by controller +// +// swagger:model finalizing-stage +type FinalizingStage string + +func NewFinalizingStage(value FinalizingStage) *FinalizingStage { + return &value +} + +// Pointer returns a pointer to a freshly-allocated FinalizingStage. +func (m FinalizingStage) Pointer() *FinalizingStage { + return &m +} + +const ( + + // FinalizingStageWaitingForClusterOperators captures enum value "Waiting for cluster operators" + FinalizingStageWaitingForClusterOperators FinalizingStage = "Waiting for cluster operators" + + // FinalizingStageAddingRouterCa captures enum value "Adding router ca" + FinalizingStageAddingRouterCa FinalizingStage = "Adding router ca" + + // FinalizingStageApplyingOlmManifests captures enum value "Applying olm manifests" + FinalizingStageApplyingOlmManifests FinalizingStage = "Applying olm manifests" + + // FinalizingStageWaitingForOlmOperatorsCsvInitialization captures enum value "Waiting for olm operators csv initialization" + FinalizingStageWaitingForOlmOperatorsCsvInitialization FinalizingStage = "Waiting for olm operators csv initialization" + + // FinalizingStageWaitingForOlmOperatorsCsv captures enum value "Waiting for olm operators csv" + FinalizingStageWaitingForOlmOperatorsCsv FinalizingStage = "Waiting for olm operators csv" + + // FinalizingStageDone captures enum value "Done" + FinalizingStageDone FinalizingStage = "Done" +) + +// for schema +var finalizingStageEnum []interface{} + +func init() { + var res []FinalizingStage + if err := json.Unmarshal([]byte(`["Waiting for cluster operators","Adding router ca","Applying olm manifests","Waiting for olm operators csv initialization","Waiting for olm operators csv","Done"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + finalizingStageEnum = append(finalizingStageEnum, v) + } +} + +func (m FinalizingStage) validateFinalizingStageEnum(path, location string, value FinalizingStage) error { + if err := validate.EnumCase(path, location, value, finalizingStageEnum, true); err != nil { + return err + } + return nil +} + +// Validate validates this finalizing stage +func (m FinalizingStage) Validate(formats strfmt.Registry) error { + var res []error + + // value enum + if err := m.validateFinalizingStageEnum("", "body", m); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// ContextValidate validates this finalizing stage based on context it is used +func (m FinalizingStage) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} diff --git a/vendor/github.com/openshift/assisted-service/models/host.go b/vendor/github.com/openshift/assisted-service/models/host.go index 10122048d26..e304fdea510 100644 --- a/vendor/github.com/openshift/assisted-service/models/host.go +++ b/vendor/github.com/openshift/assisted-service/models/host.go @@ -37,6 +37,9 @@ type Host struct { // Format: uuid ClusterID *strfmt.UUID `json:"cluster_id,omitempty" gorm:"foreignkey:Cluster"` + // Indicate that connection to assisted service was timed out when soft timeout is enabled. + ConnectionTimedOut bool `json:"connection_timed_out,omitempty"` + // connectivity Connectivity string `json:"connectivity,omitempty" gorm:"type:text"` diff --git a/vendor/github.com/openshift/assisted-service/models/host_progress_info.go b/vendor/github.com/openshift/assisted-service/models/host_progress_info.go index bdbbe4d7f80..e43104228ac 100644 --- a/vendor/github.com/openshift/assisted-service/models/host_progress_info.go +++ b/vendor/github.com/openshift/assisted-service/models/host_progress_info.go @@ -32,6 +32,9 @@ type HostProgressInfo struct { // Format: date-time StageStartedAt strfmt.DateTime `json:"stage_started_at,omitempty" gorm:"type:timestamp with time zone"` + // Indicate of the current stage has been timed out. + StageTimedOut bool `json:"stage_timed_out,omitempty"` + // Time at which the current progress stage was last updated. // Format: date-time StageUpdatedAt strfmt.DateTime `json:"stage_updated_at,omitempty" gorm:"type:timestamp with time zone"` diff --git a/vendor/github.com/openshift/assisted-service/models/host_update_params.go b/vendor/github.com/openshift/assisted-service/models/host_update_params.go index 2f8eba2e820..c14be9b4620 100644 --- a/vendor/github.com/openshift/assisted-service/models/host_update_params.go +++ b/vendor/github.com/openshift/assisted-service/models/host_update_params.go @@ -34,6 +34,9 @@ type HostUpdateParams struct { // Enum: [auto-assign master worker] HostRole *string `json:"host_role,omitempty"` + // JSON-formatted string of additional HTTP headers when fetching the ignition. + IgnitionEndpointHTTPHeaders []*IgnitionEndpointHTTPHeadersParams `json:"ignition_endpoint_http_headers"` + // A string which will be used as Authorization Bearer token to fetch the ignition from ignition_endpoint_url. IgnitionEndpointToken *string `json:"ignition_endpoint_token,omitempty"` @@ -60,6 +63,10 @@ func (m *HostUpdateParams) Validate(formats strfmt.Registry) error { res = append(res, err) } + if err := m.validateIgnitionEndpointHTTPHeaders(formats); err != nil { + res = append(res, err) + } + if err := m.validateNodeLabels(formats); err != nil { res = append(res, err) } @@ -167,6 +174,32 @@ func (m *HostUpdateParams) validateHostRole(formats strfmt.Registry) error { return nil } +func (m *HostUpdateParams) validateIgnitionEndpointHTTPHeaders(formats strfmt.Registry) error { + if swag.IsZero(m.IgnitionEndpointHTTPHeaders) { // not required + return nil + } + + for i := 0; i < len(m.IgnitionEndpointHTTPHeaders); i++ { + if swag.IsZero(m.IgnitionEndpointHTTPHeaders[i]) { // not required + continue + } + + if m.IgnitionEndpointHTTPHeaders[i] != nil { + if err := m.IgnitionEndpointHTTPHeaders[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("ignition_endpoint_http_headers" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("ignition_endpoint_http_headers" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + func (m *HostUpdateParams) validateNodeLabels(formats strfmt.Registry) error { if swag.IsZero(m.NodeLabels) { // not required return nil @@ -205,6 +238,10 @@ func (m *HostUpdateParams) ContextValidate(ctx context.Context, formats strfmt.R res = append(res, err) } + if err := m.contextValidateIgnitionEndpointHTTPHeaders(ctx, formats); err != nil { + res = append(res, err) + } + if err := m.contextValidateNodeLabels(ctx, formats); err != nil { res = append(res, err) } @@ -255,6 +292,26 @@ func (m *HostUpdateParams) contextValidateDisksSkipFormatting(ctx context.Contex return nil } +func (m *HostUpdateParams) contextValidateIgnitionEndpointHTTPHeaders(ctx context.Context, formats strfmt.Registry) error { + + for i := 0; i < len(m.IgnitionEndpointHTTPHeaders); i++ { + + if m.IgnitionEndpointHTTPHeaders[i] != nil { + if err := m.IgnitionEndpointHTTPHeaders[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("ignition_endpoint_http_headers" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("ignition_endpoint_http_headers" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + func (m *HostUpdateParams) contextValidateNodeLabels(ctx context.Context, formats strfmt.Registry) error { for i := 0; i < len(m.NodeLabels); i++ { diff --git a/vendor/github.com/openshift/assisted-service/models/ignition_endpoint_http_headers_params.go b/vendor/github.com/openshift/assisted-service/models/ignition_endpoint_http_headers_params.go new file mode 100644 index 00000000000..bf04e14a6d1 --- /dev/null +++ b/vendor/github.com/openshift/assisted-service/models/ignition_endpoint_http_headers_params.go @@ -0,0 +1,88 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// IgnitionEndpointHTTPHeadersParams ignition endpoint http headers params +// +// swagger:model ignition-endpoint-http-headers-params +type IgnitionEndpointHTTPHeadersParams struct { + + // The key for the http header's key-value pair. + // Required: true + Key *string `json:"key"` + + // The value for the http header's key-value pair. + // Required: true + Value *string `json:"value"` +} + +// Validate validates this ignition endpoint http headers params +func (m *IgnitionEndpointHTTPHeadersParams) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateKey(formats); err != nil { + res = append(res, err) + } + + if err := m.validateValue(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *IgnitionEndpointHTTPHeadersParams) validateKey(formats strfmt.Registry) error { + + if err := validate.Required("key", "body", m.Key); err != nil { + return err + } + + return nil +} + +func (m *IgnitionEndpointHTTPHeadersParams) validateValue(formats strfmt.Registry) error { + + if err := validate.Required("value", "body", m.Value); err != nil { + return err + } + + return nil +} + +// ContextValidate validates this ignition endpoint http headers params based on context it is used +func (m *IgnitionEndpointHTTPHeadersParams) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *IgnitionEndpointHTTPHeadersParams) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *IgnitionEndpointHTTPHeadersParams) UnmarshalBinary(b []byte) error { + var res IgnitionEndpointHTTPHeadersParams + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/vendor/github.com/openshift/assisted-service/models/infra_env_update_params.go b/vendor/github.com/openshift/assisted-service/models/infra_env_update_params.go index fa7c2188547..a80b0fbb3a9 100644 --- a/vendor/github.com/openshift/assisted-service/models/infra_env_update_params.go +++ b/vendor/github.com/openshift/assisted-service/models/infra_env_update_params.go @@ -36,6 +36,9 @@ type InfraEnvUpdateParams struct { // kernel arguments KernelArguments KernelArguments `json:"kernel_arguments"` + // Version of the OS image + OpenshiftVersion *string `json:"openshift_version,omitempty"` + // proxy Proxy *Proxy `json:"proxy,omitempty" gorm:"embedded;embeddedPrefix:proxy_"` diff --git a/vendor/github.com/openshift/assisted-service/models/install_cmd_request.go b/vendor/github.com/openshift/assisted-service/models/install_cmd_request.go index ea633209df2..4a9b4eae6eb 100644 --- a/vendor/github.com/openshift/assisted-service/models/install_cmd_request.go +++ b/vendor/github.com/openshift/assisted-service/models/install_cmd_request.go @@ -41,6 +41,9 @@ type InstallCmdRequest struct { // List of disks to format DisksToFormat []string `json:"disks_to_format"` + // If true, assisted service will attempt to skip MCO reboot + EnableSkipMcoReboot bool `json:"enable_skip_mco_reboot,omitempty"` + // Guaranteed availability of the installed cluster. 'Full' installs a Highly-Available cluster // over multiple master nodes whereas 'None' installs a full cluster over one node. // @@ -72,6 +75,9 @@ type InstallCmdRequest struct { // Must-gather images to use MustGatherImage string `json:"must_gather_image,omitempty"` + // If true, notify number of reboots by assisted controller + NotifyNumReboots bool `json:"notify_num_reboots,omitempty"` + // Version of the OpenShift cluster. OpenshiftVersion string `json:"openshift_version,omitempty"` diff --git a/vendor/github.com/openshift/assisted-service/models/iscsi.go b/vendor/github.com/openshift/assisted-service/models/iscsi.go new file mode 100644 index 00000000000..865d1ca20b3 --- /dev/null +++ b/vendor/github.com/openshift/assisted-service/models/iscsi.go @@ -0,0 +1,50 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// Iscsi iscsi +// +// swagger:model iscsi +type Iscsi struct { + + // Host IP address used to reach iSCSI target + HostIPAddress string `json:"host_ip_address,omitempty"` +} + +// Validate validates this iscsi +func (m *Iscsi) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this iscsi based on context it is used +func (m *Iscsi) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *Iscsi) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *Iscsi) UnmarshalBinary(b []byte) error { + var res Iscsi + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/vendor/github.com/openshift/assisted-service/models/last_installation_preparation.go b/vendor/github.com/openshift/assisted-service/models/last_installation_preparation.go new file mode 100644 index 00000000000..e2008cb4777 --- /dev/null +++ b/vendor/github.com/openshift/assisted-service/models/last_installation_preparation.go @@ -0,0 +1,111 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "encoding/json" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// LastInstallationPreparation Gives the status of the last installation preparation (if any) +// +// swagger:model last-installation-preparation +type LastInstallationPreparation struct { + + // The reason for the preparation status if applicable + Reason string `json:"reason,omitempty"` + + // The last installation preparation status + // Enum: [not_started failed success] + Status string `json:"status,omitempty"` +} + +// Validate validates this last installation preparation +func (m *LastInstallationPreparation) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateStatus(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +var lastInstallationPreparationTypeStatusPropEnum []interface{} + +func init() { + var res []string + if err := json.Unmarshal([]byte(`["not_started","failed","success"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + lastInstallationPreparationTypeStatusPropEnum = append(lastInstallationPreparationTypeStatusPropEnum, v) + } +} + +const ( + + // LastInstallationPreparationStatusNotStarted captures enum value "not_started" + LastInstallationPreparationStatusNotStarted string = "not_started" + + // LastInstallationPreparationStatusFailed captures enum value "failed" + LastInstallationPreparationStatusFailed string = "failed" + + // LastInstallationPreparationStatusSuccess captures enum value "success" + LastInstallationPreparationStatusSuccess string = "success" +) + +// prop value enum +func (m *LastInstallationPreparation) validateStatusEnum(path, location string, value string) error { + if err := validate.EnumCase(path, location, value, lastInstallationPreparationTypeStatusPropEnum, true); err != nil { + return err + } + return nil +} + +func (m *LastInstallationPreparation) validateStatus(formats strfmt.Registry) error { + if swag.IsZero(m.Status) { // not required + return nil + } + + // value enum + if err := m.validateStatusEnum("status", "body", m.Status); err != nil { + return err + } + + return nil +} + +// ContextValidate validates this last installation preparation based on context it is used +func (m *LastInstallationPreparation) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *LastInstallationPreparation) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *LastInstallationPreparation) UnmarshalBinary(b []byte) error { + var res LastInstallationPreparation + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/vendor/github.com/openshift/assisted-service/models/manifest.go b/vendor/github.com/openshift/assisted-service/models/manifest.go index df07b411db1..2e61abf46e6 100644 --- a/vendor/github.com/openshift/assisted-service/models/manifest.go +++ b/vendor/github.com/openshift/assisted-service/models/manifest.go @@ -26,6 +26,10 @@ type Manifest struct { // The folder that contains the files. Manifests can be placed in 'manifests' or 'openshift' directories. // Enum: [manifests openshift] Folder string `json:"folder,omitempty"` + + // Describes whether manifest is sourced from a user or created by the system. + // Enum: [user system] + ManifestSource string `json:"manifest_source,omitempty"` } // Validate validates this manifest @@ -36,6 +40,10 @@ func (m *Manifest) Validate(formats strfmt.Registry) error { res = append(res, err) } + if err := m.validateManifestSource(formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { return errors.CompositeValidationError(res...) } @@ -84,6 +92,48 @@ func (m *Manifest) validateFolder(formats strfmt.Registry) error { return nil } +var manifestTypeManifestSourcePropEnum []interface{} + +func init() { + var res []string + if err := json.Unmarshal([]byte(`["user","system"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + manifestTypeManifestSourcePropEnum = append(manifestTypeManifestSourcePropEnum, v) + } +} + +const ( + + // ManifestManifestSourceUser captures enum value "user" + ManifestManifestSourceUser string = "user" + + // ManifestManifestSourceSystem captures enum value "system" + ManifestManifestSourceSystem string = "system" +) + +// prop value enum +func (m *Manifest) validateManifestSourceEnum(path, location string, value string) error { + if err := validate.EnumCase(path, location, value, manifestTypeManifestSourcePropEnum, true); err != nil { + return err + } + return nil +} + +func (m *Manifest) validateManifestSource(formats strfmt.Registry) error { + if swag.IsZero(m.ManifestSource) { // not required + return nil + } + + // value enum + if err := m.validateManifestSourceEnum("manifest_source", "body", m.ManifestSource); err != nil { + return err + } + + return nil +} + // ContextValidate validates this manifest based on context it is used func (m *Manifest) ContextValidate(ctx context.Context, formats strfmt.Registry) error { return nil diff --git a/vendor/github.com/openshift/assisted-service/models/openshift_version.go b/vendor/github.com/openshift/assisted-service/models/openshift_version.go index 2af83b7f0d4..390dcefab9d 100644 --- a/vendor/github.com/openshift/assisted-service/models/openshift_version.go +++ b/vendor/github.com/openshift/assisted-service/models/openshift_version.go @@ -33,7 +33,7 @@ type OpenshiftVersion struct { // Level of support of the version. // Required: true - // Enum: [beta production maintenance] + // Enum: [beta production maintenance end-of-life] SupportLevel *string `json:"support_level"` } @@ -81,7 +81,7 @@ var openshiftVersionTypeSupportLevelPropEnum []interface{} func init() { var res []string - if err := json.Unmarshal([]byte(`["beta","production","maintenance"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["beta","production","maintenance","end-of-life"]`), &res); err != nil { panic(err) } for _, v := range res { @@ -99,6 +99,9 @@ const ( // OpenshiftVersionSupportLevelMaintenance captures enum value "maintenance" OpenshiftVersionSupportLevelMaintenance string = "maintenance" + + // OpenshiftVersionSupportLevelEndOfLife captures enum value "end-of-life" + OpenshiftVersionSupportLevelEndOfLife string = "end-of-life" ) // prop value enum diff --git a/vendor/github.com/openshift/assisted-service/models/platform.go b/vendor/github.com/openshift/assisted-service/models/platform.go index b12e113d831..d4772a7d596 100644 --- a/vendor/github.com/openshift/assisted-service/models/platform.go +++ b/vendor/github.com/openshift/assisted-service/models/platform.go @@ -19,10 +19,8 @@ import ( // swagger:model platform type Platform struct { - // Used by the service to indicate that the platform-specific components are not included in - // OpenShift and must be provided as manifests separately. - // Read Only: true - IsExternal *bool `json:"is_external,omitempty"` + // external + External *PlatformExternal `json:"external,omitempty" gorm:"embedded;embeddedPrefix:external_"` // type // Required: true @@ -33,6 +31,10 @@ type Platform struct { func (m *Platform) Validate(formats strfmt.Registry) error { var res []error + if err := m.validateExternal(formats); err != nil { + res = append(res, err) + } + if err := m.validateType(formats); err != nil { res = append(res, err) } @@ -43,6 +45,25 @@ func (m *Platform) Validate(formats strfmt.Registry) error { return nil } +func (m *Platform) validateExternal(formats strfmt.Registry) error { + if swag.IsZero(m.External) { // not required + return nil + } + + if m.External != nil { + if err := m.External.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("external") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("external") + } + return err + } + } + + return nil +} + func (m *Platform) validateType(formats strfmt.Registry) error { if err := validate.Required("type", "body", m.Type); err != nil { @@ -71,7 +92,7 @@ func (m *Platform) validateType(formats strfmt.Registry) error { func (m *Platform) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error - if err := m.contextValidateIsExternal(ctx, formats); err != nil { + if err := m.contextValidateExternal(ctx, formats); err != nil { res = append(res, err) } @@ -85,10 +106,17 @@ func (m *Platform) ContextValidate(ctx context.Context, formats strfmt.Registry) return nil } -func (m *Platform) contextValidateIsExternal(ctx context.Context, formats strfmt.Registry) error { +func (m *Platform) contextValidateExternal(ctx context.Context, formats strfmt.Registry) error { - if err := validate.ReadOnly(ctx, "is_external", "body", m.IsExternal); err != nil { - return err + if m.External != nil { + if err := m.External.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("external") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("external") + } + return err + } } return nil diff --git a/vendor/github.com/openshift/assisted-service/models/platform_external.go b/vendor/github.com/openshift/assisted-service/models/platform_external.go new file mode 100644 index 00000000000..3816691273b --- /dev/null +++ b/vendor/github.com/openshift/assisted-service/models/platform_external.go @@ -0,0 +1,125 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "encoding/json" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// PlatformExternal Configuration used when installing with an external platform type. +// +// swagger:model platform_external +type PlatformExternal struct { + + // When set to external, this property will enable an external cloud provider. + // Enum: [ External] + CloudControllerManager *string `json:"cloud_controller_manager,omitempty"` + + // Holds the arbitrary string representing the infrastructure provider name. + // Min Length: 1 + PlatformName *string `json:"platform_name,omitempty"` +} + +// Validate validates this platform external +func (m *PlatformExternal) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateCloudControllerManager(formats); err != nil { + res = append(res, err) + } + + if err := m.validatePlatformName(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +var platformExternalTypeCloudControllerManagerPropEnum []interface{} + +func init() { + var res []string + if err := json.Unmarshal([]byte(`["","External"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + platformExternalTypeCloudControllerManagerPropEnum = append(platformExternalTypeCloudControllerManagerPropEnum, v) + } +} + +const ( + + // PlatformExternalCloudControllerManagerEmpty captures enum value "" + PlatformExternalCloudControllerManagerEmpty string = "" + + // PlatformExternalCloudControllerManagerExternal captures enum value "External" + PlatformExternalCloudControllerManagerExternal string = "External" +) + +// prop value enum +func (m *PlatformExternal) validateCloudControllerManagerEnum(path, location string, value string) error { + if err := validate.EnumCase(path, location, value, platformExternalTypeCloudControllerManagerPropEnum, true); err != nil { + return err + } + return nil +} + +func (m *PlatformExternal) validateCloudControllerManager(formats strfmt.Registry) error { + if swag.IsZero(m.CloudControllerManager) { // not required + return nil + } + + // value enum + if err := m.validateCloudControllerManagerEnum("cloud_controller_manager", "body", *m.CloudControllerManager); err != nil { + return err + } + + return nil +} + +func (m *PlatformExternal) validatePlatformName(formats strfmt.Registry) error { + if swag.IsZero(m.PlatformName) { // not required + return nil + } + + if err := validate.MinLength("platform_name", "body", *m.PlatformName, 1); err != nil { + return err + } + + return nil +} + +// ContextValidate validates this platform external based on context it is used +func (m *PlatformExternal) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *PlatformExternal) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *PlatformExternal) UnmarshalBinary(b []byte) error { + var res PlatformExternal + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/vendor/github.com/openshift/assisted-service/models/platform_type.go b/vendor/github.com/openshift/assisted-service/models/platform_type.go index b110d6ecafc..24c9d816731 100644 --- a/vendor/github.com/openshift/assisted-service/models/platform_type.go +++ b/vendor/github.com/openshift/assisted-service/models/platform_type.go @@ -42,8 +42,8 @@ const ( // PlatformTypeNone captures enum value "none" PlatformTypeNone PlatformType = "none" - // PlatformTypeOci captures enum value "oci" - PlatformTypeOci PlatformType = "oci" + // PlatformTypeExternal captures enum value "external" + PlatformTypeExternal PlatformType = "external" ) // for schema @@ -51,7 +51,7 @@ var platformTypeEnum []interface{} func init() { var res []PlatformType - if err := json.Unmarshal([]byte(`["baremetal","nutanix","vsphere","none","oci"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["baremetal","nutanix","vsphere","none","external"]`), &res); err != nil { panic(err) } for _, v := range res { diff --git a/vendor/github.com/openshift/assisted-service/models/release_channel.go b/vendor/github.com/openshift/assisted-service/models/release_channel.go new file mode 100644 index 00000000000..c8dacbfd13d --- /dev/null +++ b/vendor/github.com/openshift/assisted-service/models/release_channel.go @@ -0,0 +1,84 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "encoding/json" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/validate" +) + +// ReleaseChannel Release channel. +// +// swagger:model release-channel +type ReleaseChannel string + +func NewReleaseChannel(value ReleaseChannel) *ReleaseChannel { + return &value +} + +// Pointer returns a pointer to a freshly-allocated ReleaseChannel. +func (m ReleaseChannel) Pointer() *ReleaseChannel { + return &m +} + +const ( + + // ReleaseChannelCandidate captures enum value "candidate" + ReleaseChannelCandidate ReleaseChannel = "candidate" + + // ReleaseChannelFast captures enum value "fast" + ReleaseChannelFast ReleaseChannel = "fast" + + // ReleaseChannelStable captures enum value "stable" + ReleaseChannelStable ReleaseChannel = "stable" + + // ReleaseChannelEus captures enum value "eus" + ReleaseChannelEus ReleaseChannel = "eus" +) + +// for schema +var releaseChannelEnum []interface{} + +func init() { + var res []ReleaseChannel + if err := json.Unmarshal([]byte(`["candidate","fast","stable","eus"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + releaseChannelEnum = append(releaseChannelEnum, v) + } +} + +func (m ReleaseChannel) validateReleaseChannelEnum(path, location string, value ReleaseChannel) error { + if err := validate.EnumCase(path, location, value, releaseChannelEnum, true); err != nil { + return err + } + return nil +} + +// Validate validates this release channel +func (m ReleaseChannel) Validate(formats strfmt.Registry) error { + var res []error + + // value enum + if err := m.validateReleaseChannelEnum("", "body", m); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// ContextValidate validates this release channel based on context it is used +func (m ReleaseChannel) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} diff --git a/vendor/github.com/openshift/assisted-service/models/release_image.go b/vendor/github.com/openshift/assisted-service/models/release_image.go index c1f0861d9ef..9d718f2c50e 100644 --- a/vendor/github.com/openshift/assisted-service/models/release_image.go +++ b/vendor/github.com/openshift/assisted-service/models/release_image.go @@ -13,6 +13,7 @@ import ( "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" + "github.com/lib/pq" ) // ReleaseImage release image @@ -26,7 +27,7 @@ type ReleaseImage struct { CPUArchitecture *string `json:"cpu_architecture" gorm:"default:'x86_64'"` // List of CPU architectures provided by the image. - CPUArchitectures []string `json:"cpu_architectures"` + CPUArchitectures pq.StringArray `json:"cpu_architectures" gorm:"type:text[]"` // Indication that the version is the recommended one. Default bool `json:"default,omitempty"` @@ -36,12 +37,12 @@ type ReleaseImage struct { OpenshiftVersion *string `json:"openshift_version"` // Level of support of the version. - // Enum: [beta production maintenance] + // Enum: [beta production maintenance end-of-life] SupportLevel string `json:"support_level,omitempty"` // The installation image of the OpenShift cluster. // Required: true - URL *string `json:"url"` + URL *string `json:"url" gorm:"primarykey"` // OCP version from the release metadata. // Required: true @@ -146,7 +147,7 @@ var releaseImageTypeSupportLevelPropEnum []interface{} func init() { var res []string - if err := json.Unmarshal([]byte(`["beta","production","maintenance"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["beta","production","maintenance","end-of-life"]`), &res); err != nil { panic(err) } for _, v := range res { @@ -164,6 +165,9 @@ const ( // ReleaseImageSupportLevelMaintenance captures enum value "maintenance" ReleaseImageSupportLevelMaintenance string = "maintenance" + + // ReleaseImageSupportLevelEndOfLife captures enum value "end-of-life" + ReleaseImageSupportLevelEndOfLife string = "end-of-life" ) // prop value enum diff --git a/vendor/github.com/openshift/assisted-service/models/release_source.go b/vendor/github.com/openshift/assisted-service/models/release_source.go new file mode 100644 index 00000000000..70c018876b9 --- /dev/null +++ b/vendor/github.com/openshift/assisted-service/models/release_source.go @@ -0,0 +1,183 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "encoding/json" + "strconv" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// ReleaseSource release source +// +// swagger:model release-source +type ReleaseSource struct { + + // multi cpu architectures + // Required: true + MultiCPUArchitectures []string `json:"multi_cpu_architectures"` + + // Version of the OpenShift cluster. + // Example: 4.14 + // Required: true + OpenshiftVersion *string `json:"openshift_version"` + + // upgrade channels + // Required: true + UpgradeChannels []*UpgradeChannel `json:"upgrade_channels"` +} + +// Validate validates this release source +func (m *ReleaseSource) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateMultiCPUArchitectures(formats); err != nil { + res = append(res, err) + } + + if err := m.validateOpenshiftVersion(formats); err != nil { + res = append(res, err) + } + + if err := m.validateUpgradeChannels(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +var releaseSourceMultiCPUArchitecturesItemsEnum []interface{} + +func init() { + var res []string + if err := json.Unmarshal([]byte(`["x86_64","aarch64","arm64","ppc64le","s390x"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + releaseSourceMultiCPUArchitecturesItemsEnum = append(releaseSourceMultiCPUArchitecturesItemsEnum, v) + } +} + +func (m *ReleaseSource) validateMultiCPUArchitecturesItemsEnum(path, location string, value string) error { + if err := validate.EnumCase(path, location, value, releaseSourceMultiCPUArchitecturesItemsEnum, true); err != nil { + return err + } + return nil +} + +func (m *ReleaseSource) validateMultiCPUArchitectures(formats strfmt.Registry) error { + + if err := validate.Required("multi_cpu_architectures", "body", m.MultiCPUArchitectures); err != nil { + return err + } + + for i := 0; i < len(m.MultiCPUArchitectures); i++ { + + // value enum + if err := m.validateMultiCPUArchitecturesItemsEnum("multi_cpu_architectures"+"."+strconv.Itoa(i), "body", m.MultiCPUArchitectures[i]); err != nil { + return err + } + + } + + return nil +} + +func (m *ReleaseSource) validateOpenshiftVersion(formats strfmt.Registry) error { + + if err := validate.Required("openshift_version", "body", m.OpenshiftVersion); err != nil { + return err + } + + return nil +} + +func (m *ReleaseSource) validateUpgradeChannels(formats strfmt.Registry) error { + + if err := validate.Required("upgrade_channels", "body", m.UpgradeChannels); err != nil { + return err + } + + for i := 0; i < len(m.UpgradeChannels); i++ { + if swag.IsZero(m.UpgradeChannels[i]) { // not required + continue + } + + if m.UpgradeChannels[i] != nil { + if err := m.UpgradeChannels[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("upgrade_channels" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("upgrade_channels" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// ContextValidate validate this release source based on the context it is used +func (m *ReleaseSource) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateUpgradeChannels(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *ReleaseSource) contextValidateUpgradeChannels(ctx context.Context, formats strfmt.Registry) error { + + for i := 0; i < len(m.UpgradeChannels); i++ { + + if m.UpgradeChannels[i] != nil { + if err := m.UpgradeChannels[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("upgrade_channels" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("upgrade_channels" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// MarshalBinary interface implementation +func (m *ReleaseSource) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *ReleaseSource) UnmarshalBinary(b []byte) error { + var res ReleaseSource + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/vendor/github.com/openshift/assisted-service/models/release_sources.go b/vendor/github.com/openshift/assisted-service/models/release_sources.go new file mode 100644 index 00000000000..c2e51cd789a --- /dev/null +++ b/vendor/github.com/openshift/assisted-service/models/release_sources.go @@ -0,0 +1,73 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "strconv" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// ReleaseSources release sources +// +// swagger:model release-sources +type ReleaseSources []*ReleaseSource + +// Validate validates this release sources +func (m ReleaseSources) Validate(formats strfmt.Registry) error { + var res []error + + for i := 0; i < len(m); i++ { + if swag.IsZero(m[i]) { // not required + continue + } + + if m[i] != nil { + if err := m[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName(strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName(strconv.Itoa(i)) + } + return err + } + } + + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// ContextValidate validate this release sources based on the context it is used +func (m ReleaseSources) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + for i := 0; i < len(m); i++ { + + if m[i] != nil { + if err := m[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName(strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName(strconv.Itoa(i)) + } + return err + } + } + + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/vendor/github.com/openshift/assisted-service/models/update_manifest_params.go b/vendor/github.com/openshift/assisted-service/models/update_manifest_params.go index 523e11e54be..9606db2aa74 100644 --- a/vendor/github.com/openshift/assisted-service/models/update_manifest_params.go +++ b/vendor/github.com/openshift/assisted-service/models/update_manifest_params.go @@ -22,7 +22,7 @@ type UpdateManifestParams struct { // The file name for the manifest to modify. // Required: true - // Pattern: ^[^/]*\.(yaml|yml|json)$ + // Pattern: ^[^\/]*\.(json|ya?ml(\.patch_?[a-zA-Z0-9_]*)?)$ FileName string `json:"file_name"` // The folder for the manifest to modify. @@ -34,7 +34,7 @@ type UpdateManifestParams struct { UpdatedContent *string `json:"updated_content,omitempty"` // The new file name for the manifest. - // Pattern: ^[^/]*\.(yaml|yml|json)$ + // Pattern: ^[^\/]*\.(json|ya?ml(\.patch_?[a-zA-Z0-9_]*)?)$ UpdatedFileName *string `json:"updated_file_name,omitempty"` // The new folder for the manifest. Manifests can be placed in 'manifests' or 'openshift' directories. @@ -74,7 +74,7 @@ func (m *UpdateManifestParams) validateFileName(formats strfmt.Registry) error { return err } - if err := validate.Pattern("file_name", "body", m.FileName, `^[^/]*\.(yaml|yml|json)$`); err != nil { + if err := validate.Pattern("file_name", "body", m.FileName, `^[^\/]*\.(json|ya?ml(\.patch_?[a-zA-Z0-9_]*)?)$`); err != nil { return err } @@ -129,7 +129,7 @@ func (m *UpdateManifestParams) validateUpdatedFileName(formats strfmt.Registry) return nil } - if err := validate.Pattern("updated_file_name", "body", *m.UpdatedFileName, `^[^/]*\.(yaml|yml|json)$`); err != nil { + if err := validate.Pattern("updated_file_name", "body", *m.UpdatedFileName, `^[^\/]*\.(json|ya?ml(\.patch_?[a-zA-Z0-9_]*)?)$`); err != nil { return err } diff --git a/vendor/github.com/openshift/assisted-service/models/upgrade_channel.go b/vendor/github.com/openshift/assisted-service/models/upgrade_channel.go new file mode 100644 index 00000000000..a413c3d5b1c --- /dev/null +++ b/vendor/github.com/openshift/assisted-service/models/upgrade_channel.go @@ -0,0 +1,177 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "encoding/json" + "strconv" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// UpgradeChannel upgrade channel +// +// swagger:model upgrade-channel +type UpgradeChannel struct { + + // channels + // Required: true + Channels []ReleaseChannel `json:"channels"` + + // The CPU architecture of the image. + // Required: true + // Enum: [x86_64 aarch64 arm64 ppc64le s390x multi] + CPUArchitecture *string `json:"cpu_architecture"` +} + +// Validate validates this upgrade channel +func (m *UpgradeChannel) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateChannels(formats); err != nil { + res = append(res, err) + } + + if err := m.validateCPUArchitecture(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *UpgradeChannel) validateChannels(formats strfmt.Registry) error { + + if err := validate.Required("channels", "body", m.Channels); err != nil { + return err + } + + for i := 0; i < len(m.Channels); i++ { + + if err := m.Channels[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("channels" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("channels" + "." + strconv.Itoa(i)) + } + return err + } + + } + + return nil +} + +var upgradeChannelTypeCPUArchitecturePropEnum []interface{} + +func init() { + var res []string + if err := json.Unmarshal([]byte(`["x86_64","aarch64","arm64","ppc64le","s390x","multi"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + upgradeChannelTypeCPUArchitecturePropEnum = append(upgradeChannelTypeCPUArchitecturePropEnum, v) + } +} + +const ( + + // UpgradeChannelCPUArchitectureX8664 captures enum value "x86_64" + UpgradeChannelCPUArchitectureX8664 string = "x86_64" + + // UpgradeChannelCPUArchitectureAarch64 captures enum value "aarch64" + UpgradeChannelCPUArchitectureAarch64 string = "aarch64" + + // UpgradeChannelCPUArchitectureArm64 captures enum value "arm64" + UpgradeChannelCPUArchitectureArm64 string = "arm64" + + // UpgradeChannelCPUArchitecturePpc64le captures enum value "ppc64le" + UpgradeChannelCPUArchitecturePpc64le string = "ppc64le" + + // UpgradeChannelCPUArchitectureS390x captures enum value "s390x" + UpgradeChannelCPUArchitectureS390x string = "s390x" + + // UpgradeChannelCPUArchitectureMulti captures enum value "multi" + UpgradeChannelCPUArchitectureMulti string = "multi" +) + +// prop value enum +func (m *UpgradeChannel) validateCPUArchitectureEnum(path, location string, value string) error { + if err := validate.EnumCase(path, location, value, upgradeChannelTypeCPUArchitecturePropEnum, true); err != nil { + return err + } + return nil +} + +func (m *UpgradeChannel) validateCPUArchitecture(formats strfmt.Registry) error { + + if err := validate.Required("cpu_architecture", "body", m.CPUArchitecture); err != nil { + return err + } + + // value enum + if err := m.validateCPUArchitectureEnum("cpu_architecture", "body", *m.CPUArchitecture); err != nil { + return err + } + + return nil +} + +// ContextValidate validate this upgrade channel based on the context it is used +func (m *UpgradeChannel) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateChannels(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *UpgradeChannel) contextValidateChannels(ctx context.Context, formats strfmt.Registry) error { + + for i := 0; i < len(m.Channels); i++ { + + if err := m.Channels[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("channels" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("channels" + "." + strconv.Itoa(i)) + } + return err + } + + } + + return nil +} + +// MarshalBinary interface implementation +func (m *UpgradeChannel) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *UpgradeChannel) UnmarshalBinary(b []byte) error { + var res UpgradeChannel + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/vendor/github.com/openshift/assisted-service/models/v2_cluster_update_params.go b/vendor/github.com/openshift/assisted-service/models/v2_cluster_update_params.go index 27bc0598506..cf3edc1efcf 100644 --- a/vendor/github.com/openshift/assisted-service/models/v2_cluster_update_params.go +++ b/vendor/github.com/openshift/assisted-service/models/v2_cluster_update_params.go @@ -24,10 +24,6 @@ type V2ClusterUpdateParams struct { // A comma-separated list of NTP sources (name or IP) going to be added to all the hosts. AdditionalNtpSource *string `json:"additional_ntp_source,omitempty"` - // (DEPRECATED) The virtual IP used to reach the OpenShift cluster's API. - // Pattern: ^(?:(?:(?:[0-9]{1,3}\.){3}[0-9]{1,3})|(?:(?:[0-9a-fA-F]*:[0-9a-fA-F]*){2,}))?$ - APIVip *string `json:"api_vip,omitempty"` - // The domain name used to reach the OpenShift cluster API. APIVipDNSName *string `json:"api_vip_dns_name,omitempty"` @@ -69,10 +65,6 @@ type V2ClusterUpdateParams struct { // Explicit ignition endpoint overrides the default ignition endpoint. IgnitionEndpoint *IgnitionEndpoint `json:"ignition_endpoint,omitempty" gorm:"embedded;embeddedPrefix:ignition_endpoint_"` - // (DEPRECATED) The virtual IP used for cluster ingress traffic. - // Pattern: ^(?:(?:(?:[0-9]{1,3}\.){3}[0-9]{1,3})|(?:(?:[0-9a-fA-F]*:[0-9a-fA-F]*){2,}))?$ - IngressVip *string `json:"ingress_vip,omitempty"` - // The virtual IPs used for cluster ingress traffic. Enter one IP address for single-stack clusters, or up to two for dual-stack clusters (at most one IP address per IP stack used). The order of stacks should be the same as order of subnets in Cluster Networks, Service Networks, and Machine Networks. IngressVips []*IngressVip `json:"ingress_vips"` @@ -131,10 +123,6 @@ type V2ClusterUpdateParams struct { func (m *V2ClusterUpdateParams) Validate(formats strfmt.Registry) error { var res []error - if err := m.validateAPIVip(formats); err != nil { - res = append(res, err) - } - if err := m.validateAPIVips(formats); err != nil { res = append(res, err) } @@ -163,10 +151,6 @@ func (m *V2ClusterUpdateParams) Validate(formats strfmt.Registry) error { res = append(res, err) } - if err := m.validateIngressVip(formats); err != nil { - res = append(res, err) - } - if err := m.validateIngressVips(formats); err != nil { res = append(res, err) } @@ -209,18 +193,6 @@ func (m *V2ClusterUpdateParams) Validate(formats strfmt.Registry) error { return nil } -func (m *V2ClusterUpdateParams) validateAPIVip(formats strfmt.Registry) error { - if swag.IsZero(m.APIVip) { // not required - return nil - } - - if err := validate.Pattern("api_vip", "body", *m.APIVip, `^(?:(?:(?:[0-9]{1,3}\.){3}[0-9]{1,3})|(?:(?:[0-9a-fA-F]*:[0-9a-fA-F]*){2,}))?$`); err != nil { - return err - } - - return nil -} - func (m *V2ClusterUpdateParams) validateAPIVips(formats strfmt.Registry) error { if swag.IsZero(m.APIVips) { // not required return nil @@ -387,18 +359,6 @@ func (m *V2ClusterUpdateParams) validateIgnitionEndpoint(formats strfmt.Registry return nil } -func (m *V2ClusterUpdateParams) validateIngressVip(formats strfmt.Registry) error { - if swag.IsZero(m.IngressVip) { // not required - return nil - } - - if err := validate.Pattern("ingress_vip", "body", *m.IngressVip, `^(?:(?:(?:[0-9]{1,3}\.){3}[0-9]{1,3})|(?:(?:[0-9a-fA-F]*:[0-9a-fA-F]*){2,}))?$`); err != nil { - return err - } - - return nil -} - func (m *V2ClusterUpdateParams) validateIngressVips(formats strfmt.Registry) error { if swag.IsZero(m.IngressVips) { // not required return nil diff --git a/vendor/github.com/openshift/assisted-service/pkg/validations/validations.go b/vendor/github.com/openshift/assisted-service/pkg/validations/validations.go new file mode 100644 index 00000000000..5b5612352fd --- /dev/null +++ b/vendor/github.com/openshift/assisted-service/pkg/validations/validations.go @@ -0,0 +1,192 @@ +package validations + +import ( + "crypto/x509" + "encoding/base64" + "fmt" + "net" + "net/http" + "net/url" + "regexp" + "strings" + + "github.com/asaskevich/govalidator" + "github.com/pkg/errors" + "github.com/thoas/go-funk" +) + +const ( + baseDomainRegex = `^[a-z\d][\-]*[a-z\d]+$` + dnsNameRegex = `^([a-z\d]([\-]*[a-z\d]+)*\.)+[a-z\d]+[\-]*[a-z\d]+$` + hostnameRegex = `^[a-z0-9][a-z0-9\-\.]{0,61}[a-z0-9]$` + installerArgsValuesRegex = `^[A-Za-z0-9@!#$%*()_+-=//.,";':{}\[\]]+$` +) + +var allowedFlags = []string{"--append-karg", "--delete-karg", "-n", "--copy-network", "--network-dir", "--save-partlabel", "--save-partindex", "--image-url", "--image-file"} + +func ValidateInstallerArgs(args []string) error { + argsRe := regexp.MustCompile("^-+.*") + valuesRe := regexp.MustCompile(installerArgsValuesRegex) + + for _, arg := range args { + if argsRe.MatchString(arg) { + if !funk.ContainsString(allowedFlags, arg) { + return fmt.Errorf("found unexpected flag %s for installer - allowed flags are %v", arg, allowedFlags) + } + continue + } + + if !valuesRe.MatchString(arg) { + return fmt.Errorf("found unexpected chars in value %s for installer", arg) + } + } + + return nil +} + +func ValidateDomainNameFormat(dnsDomainName string) (int32, error) { + matched, err := regexp.MatchString(baseDomainRegex, dnsDomainName) + if err != nil { + return http.StatusInternalServerError, errors.Wrapf(err, "Single DNS base domain validation for %s", dnsDomainName) + } + if matched && len(dnsDomainName) > 1 { + return 0, nil + } + matched, err = regexp.MatchString(dnsNameRegex, dnsDomainName) + if err != nil { + return http.StatusInternalServerError, errors.Wrapf(err, "DNS name validation for %s", dnsDomainName) + } + if !matched { + return http.StatusBadRequest, errors.Errorf("DNS format mismatch: %s domain name is not valid", dnsDomainName) + } + return 0, nil +} + +func ValidateHostname(name string) error { + matched, err := regexp.MatchString(hostnameRegex, name) + if err != nil { + return errors.Wrapf(err, "Hostname validation for %s", name) + } + if !matched { + return errors.Errorf(`Hostname format mismatch: %s name is not valid. + Hostname must have a maximum length of 64 characters, + start and end with a lowercase alphanumerical character, + and can only contain lowercase alphanumerical characters, dashes, and periods.`, name) + } + return nil +} + +func AllStrings(vs []string, f func(string) bool) bool { + for _, v := range vs { + if !f(v) { + return false + } + } + return true +} + +func ValidateAdditionalNTPSource(commaSeparatedNTPSources string) bool { + return AllStrings(strings.Split(commaSeparatedNTPSources, ","), ValidateNTPSource) +} + +func ValidateNTPSource(ntpSource string) bool { + if addr := net.ParseIP(ntpSource); addr != nil { + return true + } + + if err := ValidateHostname(ntpSource); err == nil { + return true + } + + return false +} + +// ValidateHTTPFormat validates the HTTP and HTTPS format +func ValidateHTTPFormat(theurl string) error { + u, err := url.Parse(theurl) + if err != nil { + return fmt.Errorf("URL '%s' format is not valid: %w", theurl, err) + } + if !(u.Scheme == "http" || u.Scheme == "https") { + return errors.Errorf("The URL scheme must be http(s) and specified in the URL: '%s'", theurl) + } + return nil +} + +// ValidateHTTPProxyFormat validates the HTTP Proxy and HTTPS Proxy format +func ValidateHTTPProxyFormat(proxyURL string) error { + if !govalidator.IsURL(proxyURL) { + return errors.Errorf("Proxy URL format is not valid: '%s'", proxyURL) + } + u, err := url.Parse(proxyURL) + if err != nil { + return errors.Errorf("Proxy URL format is not valid: '%s'", proxyURL) + } + if u.Scheme == "https" { + return errors.Errorf("The URL scheme must be http; https is currently not supported: '%s'", proxyURL) + } + if u.Scheme != "http" { + return errors.Errorf("The URL scheme must be http and specified in the URL: '%s'", proxyURL) + } + return nil +} + +// ValidateNoProxyFormat validates the no-proxy format which should be a comma-separated list +// of destination domain names, domains, IP addresses or other network CIDRs. A domain can be +// prefaced with '.' to include all subdomains of that domain. +func ValidateNoProxyFormat(noProxy string) error { + if noProxy == "*" { + return nil + } + domains := strings.Split(noProxy, ",") + for _, s := range domains { + s = strings.TrimPrefix(s, ".") + if govalidator.IsIP(s) { + continue + } + + if govalidator.IsCIDR(s) { + continue + } + + if govalidator.IsDNSName(s) { + continue + } + return errors.Errorf("NO Proxy format is not valid: '%s'. "+ + "NO Proxy is a comma-separated list of destination domain names, domains, IP addresses or other network CIDRs. "+ + "A domain can be prefaced with '.' to include all subdomains of that domain. Use '*' to bypass proxy for all destinations with OpenShift 4.8 or later.", noProxy) + } + return nil +} + +func ValidateTags(tags string) error { + if tags == "" { + return nil + } + if !AllStrings(strings.Split(tags, ","), IsValidTag) { + errMsg := "Invalid format for Tags: %s. Tags should be a comma-separated list (e.g. tag1,tag2,tag3). " + + "Each tag can consist of the following characters: Alphanumeric (aA-zZ, 0-9), underscore (_) and white-spaces." + return errors.Errorf(errMsg, tags) + } + return nil +} + +func IsValidTag(tag string) bool { + tagRegex := `^\w+( \w+)*$` // word characters and whitespace + return regexp.MustCompile(tagRegex).MatchString(tag) +} + +// ValidateCaCertificate ensures the specified base64 CA certificate +// is valid by trying to decode and parse it. +func ValidateCaCertificate(certificate string) error { + decodedCaCert, err := base64.StdEncoding.DecodeString(certificate) + if err != nil { + return errors.Wrap(err, "failed to decode certificate") + } + caCertPool := x509.NewCertPool() + if ok := caCertPool.AppendCertsFromPEM(decodedCaCert); !ok { + return errors.Errorf("unable to parse certificate") + } + + return nil +} diff --git a/vendor/github.com/openshift/custom-resource-status/conditions/v1/conditions.go b/vendor/github.com/openshift/custom-resource-status/conditions/v1/conditions.go index bbeee804a2b..7f98c60a063 100644 --- a/vendor/github.com/openshift/custom-resource-status/conditions/v1/conditions.go +++ b/vendor/github.com/openshift/custom-resource-status/conditions/v1/conditions.go @@ -8,7 +8,8 @@ import ( ) // SetStatusCondition sets the corresponding condition in conditions to newCondition. -func SetStatusCondition(conditions *[]Condition, newCondition Condition) { +// The return value indicates if this resulted in any changes *other than* LastHeartbeatTime. +func SetStatusCondition(conditions *[]Condition, newCondition Condition) bool { if conditions == nil { conditions = &[]Condition{} } @@ -17,22 +18,18 @@ func SetStatusCondition(conditions *[]Condition, newCondition Condition) { newCondition.LastTransitionTime = metav1.NewTime(time.Now()) newCondition.LastHeartbeatTime = metav1.NewTime(time.Now()) *conditions = append(*conditions, newCondition) - return - } - - if existingCondition.Status != newCondition.Status { - existingCondition.Status = newCondition.Status - existingCondition.LastTransitionTime = metav1.NewTime(time.Now()) + return true } - existingCondition.Reason = newCondition.Reason - existingCondition.Message = newCondition.Message + changed := updateCondition(existingCondition, newCondition) existingCondition.LastHeartbeatTime = metav1.NewTime(time.Now()) + return changed } // SetStatusConditionNoHearbeat sets the corresponding condition in conditions to newCondition // without setting lastHeartbeatTime. -func SetStatusConditionNoHeartbeat(conditions *[]Condition, newCondition Condition) { +// The return value indicates if this resulted in any changes. +func SetStatusConditionNoHeartbeat(conditions *[]Condition, newCondition Condition) bool { if conditions == nil { conditions = &[]Condition{} } @@ -40,16 +37,10 @@ func SetStatusConditionNoHeartbeat(conditions *[]Condition, newCondition Conditi if existingCondition == nil { newCondition.LastTransitionTime = metav1.NewTime(time.Now()) *conditions = append(*conditions, newCondition) - return + return true } - if existingCondition.Status != newCondition.Status { - existingCondition.Status = newCondition.Status - existingCondition.LastTransitionTime = metav1.NewTime(time.Now()) - } - - existingCondition.Reason = newCondition.Reason - existingCondition.Message = newCondition.Message + return updateCondition(existingCondition, newCondition) } // RemoveStatusCondition removes the corresponding conditionType from conditions. @@ -67,6 +58,25 @@ func RemoveStatusCondition(conditions *[]Condition, conditionType ConditionType) *conditions = newConditions } +func updateCondition(existingCondition *Condition, newCondition Condition) bool { + changed := false + if existingCondition.Status != newCondition.Status { + changed = true + existingCondition.Status = newCondition.Status + existingCondition.LastTransitionTime = metav1.NewTime(time.Now()) + } + + if existingCondition.Reason != newCondition.Reason { + changed = true + existingCondition.Reason = newCondition.Reason + } + if existingCondition.Message != newCondition.Message { + changed = true + existingCondition.Message = newCondition.Message + } + return changed +} + // FindStatusCondition finds the conditionType in conditions. func FindStatusCondition(conditions []Condition, conditionType ConditionType) *Condition { for i := range conditions { diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/alibabacloud/doc.go b/vendor/github.com/openshift/hive/apis/hive/v1/alibabacloud/doc.go new file mode 100644 index 00000000000..4ec0d37d61e --- /dev/null +++ b/vendor/github.com/openshift/hive/apis/hive/v1/alibabacloud/doc.go @@ -0,0 +1,4 @@ +// Package alibabacloud contains API Schema definitions for Alibaba Cloud cluster. +// +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=github.com/openshift/hive/apis/hive +package alibabacloud diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/alibabacloud/machinepool.go b/vendor/github.com/openshift/hive/apis/hive/v1/alibabacloud/machinepool.go new file mode 100644 index 00000000000..bdc9d89f4e7 --- /dev/null +++ b/vendor/github.com/openshift/hive/apis/hive/v1/alibabacloud/machinepool.go @@ -0,0 +1,41 @@ +package alibabacloud + +// DiskCategory is the category of the ECS disk. Supported disk category: +// cloud_essd(ESSD disk), cloud_efficiency(ultra disk). +// +// +kubebuilder:validation:Enum="";cloud_efficiency;cloud_essd +type DiskCategory string + +// MachinePool stores the configuration for a machine pool installed +// on Alibaba Cloud. +type MachinePool struct { + // Zones is list of availability zones that can be used. + // eg. ["cn-hangzhou-i", "cn-hangzhou-h", "cn-hangzhou-j"] + // + // +optional + Zones []string `json:"zones,omitempty"` + + // InstanceType defines the ECS instance type. + // eg. ecs.g6.large + // + // +optional + InstanceType string `json:"instanceType,omitempty"` + + // SystemDiskCategory defines the category of the system disk. + // + // +optional + SystemDiskCategory DiskCategory `json:"systemDiskCategory,omitempty"` + + // SystemDiskSize defines the size of the system disk in gibibytes (GiB). + // + // +kubebuilder:validation:Type=integer + // +kubebuilder:validation:Minimum=120 + // +optional + SystemDiskSize int `json:"systemDiskSize,omitempty"` + + // ImageID is the Image ID that should be used to create ECS instance. + // If set, the ImageID should belong to the same region as the cluster. + // + // +optional + ImageID string `json:"imageID,omitempty"` +} diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/alibabacloud/platform.go b/vendor/github.com/openshift/hive/apis/hive/v1/alibabacloud/platform.go new file mode 100644 index 00000000000..b38a7982209 --- /dev/null +++ b/vendor/github.com/openshift/hive/apis/hive/v1/alibabacloud/platform.go @@ -0,0 +1,16 @@ +package alibabacloud + +import ( + corev1 "k8s.io/api/core/v1" +) + +// Platform stores all the global configuration that all machinesets use. +type Platform struct { + // CredentialsSecretRef refers to a secret that contains Alibaba Cloud account access + // credentials. + CredentialsSecretRef corev1.LocalObjectReference `json:"credentialsSecretRef"` + + // Region specifies the Alibaba Cloud region where the cluster will be + // created. + Region string `json:"region"` +} diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/alibabacloud/zz_generated.deepcopy.go b/vendor/github.com/openshift/hive/apis/hive/v1/alibabacloud/zz_generated.deepcopy.go new file mode 100644 index 00000000000..d86041ab236 --- /dev/null +++ b/vendor/github.com/openshift/hive/apis/hive/v1/alibabacloud/zz_generated.deepcopy.go @@ -0,0 +1,44 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package alibabacloud + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MachinePool) DeepCopyInto(out *MachinePool) { + *out = *in + if in.Zones != nil { + in, out := &in.Zones, &out.Zones + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachinePool. +func (in *MachinePool) DeepCopy() *MachinePool { + if in == nil { + return nil + } + out := new(MachinePool) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Platform) DeepCopyInto(out *Platform) { + *out = *in + out.CredentialsSecretRef = in.CredentialsSecretRef + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Platform. +func (in *Platform) DeepCopy() *Platform { + if in == nil { + return nil + } + out := new(Platform) + in.DeepCopyInto(out) + return out +} diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/aws/machinepool.go b/vendor/github.com/openshift/hive/apis/hive/v1/aws/machinepool.go index 603aaeca535..5ae5025745f 100644 --- a/vendor/github.com/openshift/hive/apis/hive/v1/aws/machinepool.go +++ b/vendor/github.com/openshift/hive/apis/hive/v1/aws/machinepool.go @@ -6,9 +6,14 @@ type MachinePoolPlatform struct { // Zones is list of availability zones that can be used. Zones []string `json:"zones,omitempty"` - // Subnets is the list of subnets to which to attach the machines. - // There must be exactly one private subnet for each availability zone used. - // If public subnets are specified, there must be exactly one private and one public subnet specified for each availability zone. + // Subnets is the list of IDs of subnets to which to attach the machines. + // There must be exactly one subnet for each availability zone used. + // These subnets may be public or private. + // As a special case, for consistency with install-config, you may specify exactly one + // private and one public subnet for each availability zone. In this case, the public + // subnets will be filtered out and only the private subnets will be used. + // If empty/omitted, we will look for subnets in each availability zone tagged with + // Name=-private-. Subnets []string `json:"subnets,omitempty"` // InstanceType defines the ec2 instance type. @@ -21,6 +26,16 @@ type MachinePoolPlatform struct { // SpotMarketOptions allows users to configure instances to be run using AWS Spot instances. // +optional SpotMarketOptions *SpotMarketOptions `json:"spotMarketOptions,omitempty"` + + // EC2MetadataOptions defines metadata service interaction options for EC2 instances in the machine pool. + // +optional + EC2Metadata *EC2Metadata `json:"metadataService,omitempty"` + + // AdditionalSecurityGroupIDs contains IDs of additional security groups for machines, where each ID + // is presented in the format sg-xxxx. + // + // +optional + AdditionalSecurityGroupIDs []string `json:"additionalSecurityGroupIDs,omitempty"` } // SpotMarketOptions defines the options available to a user when configuring @@ -36,7 +51,8 @@ type SpotMarketOptions struct { // EC2RootVolume defines the storage for an ec2 instance. type EC2RootVolume struct { // IOPS defines the iops for the storage. - IOPS int `json:"iops"` + // +optional + IOPS int `json:"iops,omitempty"` // Size defines the size of the storage. Size int `json:"size"` // Type defines the type of the storage. @@ -47,3 +63,16 @@ type EC2RootVolume struct { // +optional KMSKeyARN string `json:"kmsKeyARN,omitempty"` } + +// EC2Metadata defines the metadata service interaction options for an ec2 instance. +// https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html +type EC2Metadata struct { + // Authentication determines whether or not the host requires the use of authentication when interacting with the metadata service. + // When using authentication, this enforces v2 interaction method (IMDSv2) with the metadata service. + // When omitted, this means the user has no opinion and the value is left to the platform to choose a good + // default, which is subject to change over time. The current default is optional. + // At this point this field represents `HttpTokens` parameter from `InstanceMetadataOptionsRequest` structure in AWS EC2 API + // https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_InstanceMetadataOptionsRequest.html + // +optional + Authentication string `json:"authentication,omitempty"` +} diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/aws/metadata.go b/vendor/github.com/openshift/hive/apis/hive/v1/aws/metadata.go new file mode 100644 index 00000000000..efbb50e5d63 --- /dev/null +++ b/vendor/github.com/openshift/hive/apis/hive/v1/aws/metadata.go @@ -0,0 +1,8 @@ +package aws + +// Metadata contains AWS metadata (e.g. for uninstalling the cluster). +type Metadata struct { + // HostedZoneRole is the role to assume when performing operations + // on a hosted zone owned by another account. + HostedZoneRole *string `json:"hostedZoneRole,omitempty"` +} diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/aws/platform.go b/vendor/github.com/openshift/hive/apis/hive/v1/aws/platform.go index 399e82145bf..9b71df8cefd 100644 --- a/vendor/github.com/openshift/hive/apis/hive/v1/aws/platform.go +++ b/vendor/github.com/openshift/hive/apis/hive/v1/aws/platform.go @@ -39,6 +39,13 @@ type PlatformStatus struct { // PrivateLinkAccess configures access to the cluster API using AWS PrivateLink type PrivateLinkAccess struct { Enabled bool `json:"enabled"` + + // AdditionalAllowedPrincipals is a list of additional allowed principal ARNs to be configured + // for the Private Link cluster's VPC Endpoint Service. + // ARNs provided as AdditionalAllowedPrincipals will be configured for the cluster's VPC Endpoint + // Service in addition to the IAM entity used by Hive. + // +optional + AdditionalAllowedPrincipals *[]string `json:"additionalAllowedPrincipals,omitempty"` } // PrivateLinkAccessStatus contains the observed state for PrivateLinkAccess resources. @@ -54,6 +61,15 @@ type PrivateLinkAccessStatus struct { type VPCEndpointService struct { Name string `json:"name,omitempty"` ID string `json:"id,omitempty"` + // DefaultAllowedPrincipal is the ARN of the IAM entity used by Hive as configured for the Private + // Link cluster's VPC Endpoint Service. + // +optional + DefaultAllowedPrincipal *string `json:"defaultAllowedPrincipal,omitempty"` + // AdditionalAllowedPrincipals is a list of additional allowed principal ARNs that have been configured + // for the Private Link cluster's VPC Endpoint Service. This list in Status is used to determine if a sync + // of Allowed Principals is needed outside of the regular reconcile period of 2hrs. + // +optional + AdditionalAllowedPrincipals *[]string `json:"additionalAllowedPrincipals,omitempty"` } // AssumeRole stores information for the IAM role that needs to be assumed diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/aws/zz_generated.deepcopy.go b/vendor/github.com/openshift/hive/apis/hive/v1/aws/zz_generated.deepcopy.go index bccf75c9daf..7d5f07fe93b 100644 --- a/vendor/github.com/openshift/hive/apis/hive/v1/aws/zz_generated.deepcopy.go +++ b/vendor/github.com/openshift/hive/apis/hive/v1/aws/zz_generated.deepcopy.go @@ -21,6 +21,22 @@ func (in *AssumeRole) DeepCopy() *AssumeRole { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EC2Metadata) DeepCopyInto(out *EC2Metadata) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EC2Metadata. +func (in *EC2Metadata) DeepCopy() *EC2Metadata { + if in == nil { + return nil + } + out := new(EC2Metadata) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *EC2RootVolume) DeepCopyInto(out *EC2RootVolume) { *out = *in @@ -56,6 +72,16 @@ func (in *MachinePoolPlatform) DeepCopyInto(out *MachinePoolPlatform) { *out = new(SpotMarketOptions) (*in).DeepCopyInto(*out) } + if in.EC2Metadata != nil { + in, out := &in.EC2Metadata, &out.EC2Metadata + *out = new(EC2Metadata) + **out = **in + } + if in.AdditionalSecurityGroupIDs != nil { + in, out := &in.AdditionalSecurityGroupIDs, &out.AdditionalSecurityGroupIDs + *out = make([]string, len(*in)) + copy(*out, *in) + } return } @@ -69,6 +95,27 @@ func (in *MachinePoolPlatform) DeepCopy() *MachinePoolPlatform { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Metadata) DeepCopyInto(out *Metadata) { + *out = *in + if in.HostedZoneRole != nil { + in, out := &in.HostedZoneRole, &out.HostedZoneRole + *out = new(string) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Metadata. +func (in *Metadata) DeepCopy() *Metadata { + if in == nil { + return nil + } + out := new(Metadata) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Platform) DeepCopyInto(out *Platform) { *out = *in @@ -88,7 +135,7 @@ func (in *Platform) DeepCopyInto(out *Platform) { if in.PrivateLink != nil { in, out := &in.PrivateLink, &out.PrivateLink *out = new(PrivateLinkAccess) - **out = **in + (*in).DeepCopyInto(*out) } return } @@ -109,7 +156,7 @@ func (in *PlatformStatus) DeepCopyInto(out *PlatformStatus) { if in.PrivateLink != nil { in, out := &in.PrivateLink, &out.PrivateLink *out = new(PrivateLinkAccessStatus) - **out = **in + (*in).DeepCopyInto(*out) } return } @@ -127,6 +174,15 @@ func (in *PlatformStatus) DeepCopy() *PlatformStatus { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PrivateLinkAccess) DeepCopyInto(out *PrivateLinkAccess) { *out = *in + if in.AdditionalAllowedPrincipals != nil { + in, out := &in.AdditionalAllowedPrincipals, &out.AdditionalAllowedPrincipals + *out = new([]string) + if **in != nil { + in, out := *in, *out + *out = make([]string, len(*in)) + copy(*out, *in) + } + } return } @@ -143,7 +199,7 @@ func (in *PrivateLinkAccess) DeepCopy() *PrivateLinkAccess { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PrivateLinkAccessStatus) DeepCopyInto(out *PrivateLinkAccessStatus) { *out = *in - out.VPCEndpointService = in.VPCEndpointService + in.VPCEndpointService.DeepCopyInto(&out.VPCEndpointService) return } @@ -181,6 +237,20 @@ func (in *SpotMarketOptions) DeepCopy() *SpotMarketOptions { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VPCEndpointService) DeepCopyInto(out *VPCEndpointService) { *out = *in + if in.DefaultAllowedPrincipal != nil { + in, out := &in.DefaultAllowedPrincipal, &out.DefaultAllowedPrincipal + *out = new(string) + **out = **in + } + if in.AdditionalAllowedPrincipals != nil { + in, out := &in.AdditionalAllowedPrincipals, &out.AdditionalAllowedPrincipals + *out = new([]string) + if **in != nil { + in, out := *in, *out + *out = make([]string, len(*in)) + copy(*out, *in) + } + } return } diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/azure/disk.go b/vendor/github.com/openshift/hive/apis/hive/v1/azure/disk.go new file mode 100644 index 00000000000..2463235e768 --- /dev/null +++ b/vendor/github.com/openshift/hive/apis/hive/v1/azure/disk.go @@ -0,0 +1,48 @@ +package azure + +import "fmt" + +// ToID creates an Azure resource ID for the disk encryption set. +// It is possible to return a non-valid ID when SubscriptionID is empty. This +// should never happen since if SubscriptionID is empty, it is set to the +// current subscription. Also, should it somehow be empty and this returns an +// invalid ID, the validation code will produce an error when checked against +// the validation.RxDiskEncryptionSetID regular expression. +func (d *DiskEncryptionSet) ToID() string { + return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/diskEncryptionSets/%s", + d.SubscriptionID, d.ResourceGroup, d.Name) +} + +// OSDisk defines the disk for machines on Azure. +type OSDisk struct { + // DiskSizeGB defines the size of disk in GB. + // + // +kubebuilder:validation:Minimum=0 + DiskSizeGB int32 `json:"diskSizeGB"` + // DiskType defines the type of disk. + // For control plane nodes, the valid values are Premium_LRS and StandardSSD_LRS. + // Default is Premium_LRS. + // +optional + // +kubebuilder:validation:Enum=Standard_LRS;Premium_LRS;StandardSSD_LRS + DiskType string `json:"diskType,omitempty"` + + // DiskEncryptionSet defines a disk encryption set. + // + // +optional + *DiskEncryptionSet `json:"diskEncryptionSet,omitempty"` +} + +// DiskEncryptionSet defines the configuration for a disk encryption set. +type DiskEncryptionSet struct { + // SubscriptionID defines the Azure subscription the disk encryption + // set is in. + SubscriptionID string `json:"subscriptionId,omitempty"` + // ResourceGroup defines the Azure resource group used by the disk + // encryption set. + ResourceGroup string `json:"resourceGroup"` + // Name is the name of the disk encryption set. + Name string `json:"name"` +} + +// DefaultDiskType holds the default Azure disk type used by the VMs. +const DefaultDiskType string = "Premium_LRS" diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/azure/machinepool.go b/vendor/github.com/openshift/hive/apis/hive/v1/azure/machinepool.go index 9bb9b7f9695..1fb9f51ccc8 100644 --- a/vendor/github.com/openshift/hive/apis/hive/v1/azure/machinepool.go +++ b/vendor/github.com/openshift/hive/apis/hive/v1/azure/machinepool.go @@ -13,29 +13,20 @@ type MachinePool struct { // OSDisk defines the storage for instance. OSDisk `json:"osDisk"` -} -// OSDisk defines the disk for machines on Azure. -type OSDisk struct { - // DiskSizeGB defines the size of disk in GB. - DiskSizeGB int32 `json:"diskSizeGB"` + // OSImage defines the image to use for the OS. + // +optional + OSImage *OSImage `json:"osImage,omitempty"` } -// Set sets the values from `required` to `a`. -func (a *MachinePool) Set(required *MachinePool) { - if required == nil || a == nil { - return - } - - if len(required.Zones) > 0 { - a.Zones = required.Zones - } - - if required.InstanceType != "" { - a.InstanceType = required.InstanceType - } - - if required.OSDisk.DiskSizeGB != 0 { - a.OSDisk.DiskSizeGB = required.OSDisk.DiskSizeGB - } +// OSImage is the image to use for the OS of a machine. +type OSImage struct { + // Publisher is the publisher of the image. + Publisher string `json:"publisher"` + // Offer is the offer of the image. + Offer string `json:"offer"` + // SKU is the SKU of the image. + SKU string `json:"sku"` + // Version is the version of the image. + Version string `json:"version"` } diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/azure/metadata.go b/vendor/github.com/openshift/hive/apis/hive/v1/azure/metadata.go index 472c01c4592..65552358ddb 100644 --- a/vendor/github.com/openshift/hive/apis/hive/v1/azure/metadata.go +++ b/vendor/github.com/openshift/hive/apis/hive/v1/azure/metadata.go @@ -2,5 +2,6 @@ package azure // Metadata contains Azure metadata (e.g. for uninstalling the cluster). type Metadata struct { - Region string `json:"region"` + // ResourceGroupName is the name of the resource group in which the cluster resources were created. + ResourceGroupName *string `json:"resourceGroupName"` } diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/azure/platform.go b/vendor/github.com/openshift/hive/apis/hive/v1/azure/platform.go index 37bb188d33b..d5201a93426 100644 --- a/vendor/github.com/openshift/hive/apis/hive/v1/azure/platform.go +++ b/vendor/github.com/openshift/hive/apis/hive/v1/azure/platform.go @@ -50,7 +50,7 @@ func (e CloudEnvironment) Name() string { return string(e) } -//SetBaseDomain parses the baseDomainID and sets the related fields on azure.Platform +// SetBaseDomain parses the baseDomainID and sets the related fields on azure.Platform func (p *Platform) SetBaseDomain(baseDomainID string) error { parts := strings.Split(baseDomainID, "/") p.BaseDomainResourceGroupName = parts[4] diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/azure/zz_generated.deepcopy.go b/vendor/github.com/openshift/hive/apis/hive/v1/azure/zz_generated.deepcopy.go index 81cad96d5ad..adc8cd56842 100644 --- a/vendor/github.com/openshift/hive/apis/hive/v1/azure/zz_generated.deepcopy.go +++ b/vendor/github.com/openshift/hive/apis/hive/v1/azure/zz_generated.deepcopy.go @@ -5,6 +5,22 @@ package azure +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DiskEncryptionSet) DeepCopyInto(out *DiskEncryptionSet) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DiskEncryptionSet. +func (in *DiskEncryptionSet) DeepCopy() *DiskEncryptionSet { + if in == nil { + return nil + } + out := new(DiskEncryptionSet) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MachinePool) DeepCopyInto(out *MachinePool) { *out = *in @@ -13,7 +29,12 @@ func (in *MachinePool) DeepCopyInto(out *MachinePool) { *out = make([]string, len(*in)) copy(*out, *in) } - out.OSDisk = in.OSDisk + in.OSDisk.DeepCopyInto(&out.OSDisk) + if in.OSImage != nil { + in, out := &in.OSImage, &out.OSImage + *out = new(OSImage) + **out = **in + } return } @@ -30,6 +51,11 @@ func (in *MachinePool) DeepCopy() *MachinePool { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Metadata) DeepCopyInto(out *Metadata) { *out = *in + if in.ResourceGroupName != nil { + in, out := &in.ResourceGroupName, &out.ResourceGroupName + *out = new(string) + **out = **in + } return } @@ -46,6 +72,11 @@ func (in *Metadata) DeepCopy() *Metadata { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OSDisk) DeepCopyInto(out *OSDisk) { *out = *in + if in.DiskEncryptionSet != nil { + in, out := &in.DiskEncryptionSet, &out.DiskEncryptionSet + *out = new(DiskEncryptionSet) + **out = **in + } return } @@ -59,6 +90,22 @@ func (in *OSDisk) DeepCopy() *OSDisk { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OSImage) DeepCopyInto(out *OSImage) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OSImage. +func (in *OSImage) DeepCopy() *OSImage { + if in == nil { + return nil + } + out := new(OSImage) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Platform) DeepCopyInto(out *Platform) { *out = *in diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/clusterclaim_types.go b/vendor/github.com/openshift/hive/apis/hive/v1/clusterclaim_types.go index 7bb772d0143..30694959773 100644 --- a/vendor/github.com/openshift/hive/apis/hive/v1/clusterclaim_types.go +++ b/vendor/github.com/openshift/hive/apis/hive/v1/clusterclaim_types.go @@ -69,6 +69,16 @@ type ClusterClaimCondition struct { // ClusterClaimConditionType is a valid value for ClusterClaimCondition.Type. type ClusterClaimConditionType string +// ConditionType satisfies the conditions.Condition interface +func (c ClusterClaimCondition) ConditionType() ConditionType { + return c.Type +} + +// String satisfies the conditions.ConditionType interface +func (t ClusterClaimConditionType) String() string { + return string(t) +} + const ( // ClusterClaimPendingCondition is set when a cluster has not yet been assigned and made ready to the claim. ClusterClaimPendingCondition ClusterClaimConditionType = "Pending" diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/clusterdeployment_types.go b/vendor/github.com/openshift/hive/apis/hive/v1/clusterdeployment_types.go index 3fe46701981..a42959aca09 100644 --- a/vendor/github.com/openshift/hive/apis/hive/v1/clusterdeployment_types.go +++ b/vendor/github.com/openshift/hive/apis/hive/v1/clusterdeployment_types.go @@ -1,15 +1,18 @@ package v1 import ( + configv1 "github.com/openshift/api/config/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/openshift/hive/apis/hive/v1/agent" + "github.com/openshift/hive/apis/hive/v1/alibabacloud" "github.com/openshift/hive/apis/hive/v1/aws" "github.com/openshift/hive/apis/hive/v1/azure" "github.com/openshift/hive/apis/hive/v1/baremetal" "github.com/openshift/hive/apis/hive/v1/gcp" "github.com/openshift/hive/apis/hive/v1/ibmcloud" + "github.com/openshift/hive/apis/hive/v1/none" "github.com/openshift/hive/apis/hive/v1/openstack" "github.com/openshift/hive/apis/hive/v1/ovirt" "github.com/openshift/hive/apis/hive/v1/vsphere" @@ -28,10 +31,6 @@ const ( // alert on cluster types differently. HiveClusterTypeLabel = "hive.openshift.io/cluster-type" - // DefaultClusterType will be used when the above HiveClusterTypeLabel is unset. This - // value will not be added as a label, only used for metrics vectors. - DefaultClusterType = "unspecified" - // HiveInstallLogLabel is used on ConfigMaps uploaded by the install manager which contain an install log. HiveInstallLogLabel = "hive.openshift.io/install-log" @@ -98,6 +97,9 @@ const ( // ClusterPowerStateWaitingForClusterOperators is used when waiting for ClusterOperators to // get to a good state. (Available=True, Processing=False, Degraded=False) ClusterPowerStateWaitingForClusterOperators ClusterPowerState = "WaitingForClusterOperators" + + // ClusterPowerStateUnknown indicates that we can't/won't discover the state of the cluster's cloud machines. + ClusterPowerStateUnknown = "Unknown" ) // ClusterDeploymentSpec defines the desired state of ClusterDeployment @@ -230,10 +232,16 @@ type Provisioning struct { // that will take precedence over the one from the ClusterImageSet. ImageSetRef *ClusterImageSetReference `json:"imageSetRef,omitempty"` - // ManifestsConfigMapRef is a reference to user-provided manifests to - // add to or replace manifests that are generated by the installer. + // ManifestsConfigMapRef is a reference to user-provided manifests to add to or replace manifests + // that are generated by the installer. It serves the same purpose as, and is mutually exclusive + // with, ManifestsSecretRef. ManifestsConfigMapRef *corev1.LocalObjectReference `json:"manifestsConfigMapRef,omitempty"` + // ManifestsSecretRef is a reference to user-provided manifests to add to or replace manifests + // that are generated by the installer. It serves the same purpose as, and is mutually exclusive + // with, ManifestsConfigMapRef. + ManifestsSecretRef *corev1.LocalObjectReference `json:"manifestsSecretRef,omitempty"` + // SSHPrivateKeySecretRef is the reference to the secret that contains the private SSH key to use // for access to compute instances. This private key should correspond to the public key included // in the InstallConfig. The private key is used by Hive to gather logs on the target cluster if @@ -271,6 +279,10 @@ type ClusterPoolReference struct { // ClaimedTimestamp is the time this cluster was assigned to a ClusterClaim. This is only used for // ClusterDeployments belonging to ClusterPools. ClaimedTimestamp *metav1.Time `json:"claimedTimestamp,omitempty"` + // CustomizationRef is the ClusterPool Inventory claimed customization for this ClusterDeployment. + // The Customization exists in the ClusterPool namespace. + // +optional + CustomizationRef *corev1.LocalObjectReference `json:"clusterDeploymentCustomization,omitempty"` } // ClusterMetadata contains metadata information about the installed cluster. @@ -288,6 +300,24 @@ type ClusterMetadata struct { // AdminPasswordSecretRef references the secret containing the admin username/password which can be used to login to this cluster. // +optional AdminPasswordSecretRef *corev1.LocalObjectReference `json:"adminPasswordSecretRef,omitempty"` + + // Platform holds platform-specific cluster metadata + // +optional + Platform *ClusterPlatformMetadata `json:"platform,omitempty"` +} + +type ClusterPlatformMetadata struct { + // AWS holds AWS-specific cluster metadata + // +optional + AWS *aws.Metadata `json:"aws,omitempty"` + + // Azure holds azure-specific cluster metadata + // +optional + Azure *azure.Metadata `json:"azure,omitempty"` + + // GCP holds GCP-specific cluster metadata + // +optional + GCP *gcp.Metadata `json:"gcp,omitempty"` } // ClusterDeploymentStatus defines the observed state of ClusterDeployment @@ -369,6 +399,16 @@ type ClusterDeploymentCondition struct { // ClusterDeploymentConditionType is a valid value for ClusterDeploymentCondition.Type type ClusterDeploymentConditionType string +// ConditionType satisfies the conditions.Condition interface +func (c ClusterDeploymentCondition) ConditionType() ConditionType { + return c.Type +} + +// String satisfies the conditions.ConditionType interface +func (t ClusterDeploymentConditionType) String() string { + return string(t) +} + const ( // InstallerImageResolutionFailedCondition is a condition that indicates whether the job // to determine the installer image based on a release image was successful. @@ -450,6 +490,10 @@ const ( ClusterInstallCompletedClusterDeploymentCondition ClusterDeploymentConditionType = "ClusterInstallCompleted" ClusterInstallStoppedClusterDeploymentCondition ClusterDeploymentConditionType = "ClusterInstallStopped" ClusterInstallRequirementsMetClusterDeploymentCondition ClusterDeploymentConditionType = "ClusterInstallRequirementsMet" + + // ClusterImageSetNotFoundCondition is a legacy condition type that is not intended to be used + // in production. This type is never used by hive. + ClusterImageSetNotFoundCondition ClusterDeploymentConditionType = "ClusterImageSetNotFound" ) // PositivePolarityClusterDeploymentConditions is a slice containing all condition types with positive polarity @@ -480,8 +524,7 @@ const ( // Hibernating state. HibernatingReasonHibernating = string(ClusterPowerStateHibernating) // HibernatingReasonUnsupported is used as the reason when the cluster spec - // specifies that the cluster be moved to a Hibernating state, but either the cluster - // version is not compatible with hibernation (< 4.4.8) or the cloud provider of + // specifies that the cluster be moved to a Hibernating state, but the cloud provider of // the cluster is not supported. HibernatingReasonUnsupported = "Unsupported" // HibernatingReasonFailedToStop is used when there was an error stopping machines @@ -494,6 +537,12 @@ const ( // (It does not necessarily mean they are currently copacetic -- check ClusterSync status // for that.) HibernatingReasonSyncSetsApplied = "SyncSetsApplied" + // HibernatingReasonPowerStatePaused indicates that we can't/won't discover the state of the + // cluster's cloud machines because the powerstate-paused annotation is set. + HibernatingReasonPowerStatePaused = "PowerStatePaused" + // HibernatingReasonClusterDeploymentDeleted indicates that a Cluster Deployment has been deleted + // and that the cluster is deprovisioning unless preserveOnDelete is set to true. + HibernatingReasonClusterDeploymentDeleted = "ClusterDeploymentDeleted" // ReadyReasonStoppingOrHibernating is used as the reason for the Ready condition when the cluster // is stopping or hibernating. Precise details are available in the Hibernating condition. @@ -514,6 +563,12 @@ const ( ReadyReasonWaitingForClusterOperators = string(ClusterPowerStateWaitingForClusterOperators) // ReadyReasonRunning is used on the Ready condition as the reason when the cluster is running and ready ReadyReasonRunning = string(ClusterPowerStateRunning) + // ReadyReasonPowerStatePaused indicates that we can't/won't discover the state of the + // cluster's cloud machines because the powerstate-paused annotation is set. + ReadyReasonPowerStatePaused = "PowerStatePaused" + // ReadyReasonClusterDeploymentDeleted indicates that a Cluster Deployment has been deleted + // and that the cluster is deprovisioning unless preserveOnDelete is set to true. + ReadyReasonClusterDeploymentDeleted = "ClusterDeploymentDeleted" ) // Provisioned status condition reasons @@ -545,7 +600,7 @@ const InitializedConditionReason = "Initialized" // +kubebuilder:printcolumn:name="InfraID",type="string",JSONPath=".spec.clusterMetadata.infraID" // +kubebuilder:printcolumn:name="Platform",type="string",JSONPath=".metadata.labels.hive\\.openshift\\.io/cluster-platform" // +kubebuilder:printcolumn:name="Region",type="string",JSONPath=".metadata.labels.hive\\.openshift\\.io/cluster-region" -// +kubebuilder:printcolumn:name="Version",type="string",JSONPath=".metadata.labels.hive\\.openshift\\.io/version-major-minor-patch" +// +kubebuilder:printcolumn:name="Version",type="string",JSONPath=".metadata.labels.hive\\.openshift\\.io/version" // +kubebuilder:printcolumn:name="ClusterType",type="string",JSONPath=".metadata.labels.hive\\.openshift\\.io/cluster-type" // +kubebuilder:printcolumn:name="ProvisionStatus",type="string",JSONPath=".status.conditions[?(@.type=='Provisioned')].reason" // +kubebuilder:printcolumn:name="PowerState",type="string",JSONPath=".status.powerState" @@ -571,6 +626,9 @@ type ClusterDeploymentList struct { // Platform is the configuration for the specific platform upon which to perform // the installation. Only one of the platform configuration should be set. type Platform struct { + // AlibabaCloud is the configuration used when installing on Alibaba Cloud + AlibabaCloud *alibabacloud.Platform `json:"alibabacloud,omitempty"` + // AWS is the configuration used when installing on AWS. AWS *aws.Platform `json:"aws,omitempty"` @@ -600,6 +658,10 @@ type Platform struct { // IBMCloud is the configuration used when installing on IBM Cloud IBMCloud *ibmcloud.Platform `json:"ibmcloud,omitempty"` + + // None indicates platform-agnostic install. + // https://docs.openshift.com/container-platform/4.7/installing/installing_platform_agnostic/installing-platform-agnostic.html + None *none.Platform `json:"none,omitempty"` } // PlatformStatus contains the observed state for the specific platform upon which to @@ -634,6 +696,10 @@ type ClusterIngress struct { // should be used for this Ingress // +optional ServingCertificate string `json:"servingCertificate,omitempty"` + + // HttpErrorCodePages allows configuring custom HTTP error pages using the IngressController object + // +optional + HttpErrorCodePages *configv1.ConfigMapNameReference `json:"httpErrorCodePages,omitempty"` } // ControlPlaneConfigSpec contains additional configuration settings for a target @@ -650,6 +716,13 @@ type ControlPlaneConfigSpec struct { // active, Hive will use the override URL for further communications with the API server of the remote cluster. // +optional APIURLOverride string `json:"apiURLOverride,omitempty"` + + // APIServerIPOverride is the optional override of the API server IP address. + // Hive will use this IP address for creating TCP connections. + // Port from the original API server URL will be used. + // This field can be used when repointing the APIServer's DNS is not viable option. + // +optional + APIServerIPOverride string `json:"apiServerIPOverride,omitempty"` } // ControlPlaneServingCertificateSpec specifies serving certificate settings for diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/clusterdeploymentcustomization_types.go b/vendor/github.com/openshift/hive/apis/hive/v1/clusterdeploymentcustomization_types.go new file mode 100644 index 00000000000..8917e756c51 --- /dev/null +++ b/vendor/github.com/openshift/hive/apis/hive/v1/clusterdeploymentcustomization_types.go @@ -0,0 +1,99 @@ +package v1 + +import ( + conditionsv1 "github.com/openshift/custom-resource-status/conditions/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + // CustomizationApplyReasonSucceeded indicates that the customization + // worked properly on the last applied cluster deployment. + CustomizationApplyReasonSucceeded = "Succeeded" + // CustomizationApplyReasonBrokenSyntax indicates that Hive failed to apply + // customization patches on install-config. More details would be found in + // ApplySucceded condition message. + CustomizationApplyReasonBrokenSyntax = "BrokenBySyntax" + // CustomizationApplyReasonBrokenCloud indicates that cluster deployment provision has failed + // when using this customization. More details would be found in the ApplySucceeded condition message. + CustomizationApplyReasonBrokenCloud = "BrokenByCloud" + // CustomizationApplyReasonInstallationPending indicates that the customization patches have + // been successfully applied but provisioning is not completed yet. + CustomizationApplyReasonInstallationPending = "InstallationPending" +) + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ClusterDeploymentCustomization is the Schema for clusterdeploymentcustomizations API. +// +kubebuilder:subresource:status +// +k8s:openapi-gen=true +// +kubebuilder:resource:scope=Namespaced +type ClusterDeploymentCustomization struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ClusterDeploymentCustomizationSpec `json:"spec"` + Status ClusterDeploymentCustomizationStatus `json:"status,omitempty"` +} + +// ClusterDeploymentCustomizationSpec defines the desired state of ClusterDeploymentCustomization. +type ClusterDeploymentCustomizationSpec struct { + // InstallConfigPatches is a list of patches to be applied to the install-config. + InstallConfigPatches []PatchEntity `json:"installConfigPatches,omitempty"` +} + +// PatchEntity represent a json patch (RFC 6902) to be applied to the install-config +type PatchEntity struct { + // Op is the operation to perform: add, remove, replace, move, copy, test + // +required + Op string `json:"op"` + // Path is the json path to the value to be modified + // +required + Path string `json:"path"` + // From is the json path to copy or move the value from + // +optional + From string `json:"from,omitempty"` + // Value is the value to be used in the operation + // +required + Value string `json:"value"` +} + +// ClusterDeploymentCustomizationStatus defines the observed state of ClusterDeploymentCustomization. +type ClusterDeploymentCustomizationStatus struct { + // ClusterDeploymentRef is a reference to the cluster deployment that this customization is applied on. + // +optional + ClusterDeploymentRef *corev1.LocalObjectReference `json:"clusterDeploymentRef,omitempty"` + + // ClusterPoolRef is the name of the current cluster pool the CDC used at. + // +optional + ClusterPoolRef *corev1.LocalObjectReference `json:"clusterPoolRef,omitempty"` + + // LastAppliedConfiguration contains the last applied patches to the install-config. + // The information will retain for reference in case the customization is updated. + // +optional + LastAppliedConfiguration string `json:"lastAppliedConfiguration,omitempty"` + + // Conditions describes the state of the operator's reconciliation functionality. + // +patchMergeKey=type + // +patchStrategy=merge + // +optional + Conditions []conditionsv1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` +} + +const ( + ApplySucceededCondition conditionsv1.ConditionType = "ApplySucceeded" +) + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ClusterDeploymentCustomizationList contains a list of ClusterDeploymentCustomizations. +type ClusterDeploymentCustomizationList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ClusterDeploymentCustomization `json:"items"` +} + +func init() { + SchemeBuilder.Register(&ClusterDeploymentCustomization{}, &ClusterDeploymentCustomizationList{}) +} diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/clusterdeprovision_types.go b/vendor/github.com/openshift/hive/apis/hive/v1/clusterdeprovision_types.go index 28342137231..2b71367578c 100644 --- a/vendor/github.com/openshift/hive/apis/hive/v1/clusterdeprovision_types.go +++ b/vendor/github.com/openshift/hive/apis/hive/v1/clusterdeprovision_types.go @@ -15,6 +15,14 @@ type ClusterDeprovisionSpec struct { // ClusterID is a globally unique identifier for the cluster to deprovision. It will be used if specified. ClusterID string `json:"clusterID,omitempty"` + // ClusterName is the friendly name of the cluster. It is used for subdomains, + // some resource tagging, and other instances where a friendly name for the + // cluster is useful. + ClusterName string `json:"clusterName,omitempty"` + + // BaseDomain is the DNS base domain. + BaseDomain string `json:"baseDomain,omitempty"` + // Platform contains platform-specific configuration for a ClusterDeprovision Platform ClusterDeprovisionPlatform `json:"platform,omitempty"` } @@ -32,6 +40,8 @@ type ClusterDeprovisionStatus struct { // ClusterDeprovisionPlatform contains platform-specific configuration for the // deprovision type ClusterDeprovisionPlatform struct { + // AlibabaCloud contains Alibaba Cloud specific deprovision settings + AlibabaCloud *AlibabaCloudClusterDeprovision `json:"alibabacloud,omitempty"` // AWS contains AWS-specific deprovision settings AWS *AWSClusterDeprovision `json:"aws,omitempty"` // Azure contains Azure-specific deprovision settings @@ -48,6 +58,17 @@ type ClusterDeprovisionPlatform struct { IBMCloud *IBMClusterDeprovision `json:"ibmcloud,omitempty"` } +// AlibabaCloudClusterDeprovision contains AlibabaCloud-specific configuration for a ClusterDeprovision +type AlibabaCloudClusterDeprovision struct { + // Region is the Alibaba region for this deprovision + Region string `json:"region"` + // BaseDomain is the DNS base domain. + // TODO: Use the non-platform-specific BaseDomain field. + BaseDomain string `json:"baseDomain"` + // CredentialsSecretRef is the Alibaba account credentials to use for deprovisioning the cluster + CredentialsSecretRef corev1.LocalObjectReference `json:"credentialsSecretRef"` +} + // AWSClusterDeprovision contains AWS-specific configuration for a ClusterDeprovision type AWSClusterDeprovision struct { // Region is the AWS region for this deprovisioning @@ -61,6 +82,11 @@ type AWSClusterDeprovision struct { // AWS account access for deprovisioning the cluster. // +optional CredentialsAssumeRole *aws.AssumeRole `json:"credentialsAssumeRole,omitempty"` + + // HostedZoneRole is the role to assume when performing operations + // on a hosted zone owned by another account. + // +optional + HostedZoneRole *string `json:"hostedZoneRole,omitempty"` } // AzureClusterDeprovision contains Azure-specific configuration for a ClusterDeprovision @@ -72,6 +98,10 @@ type AzureClusterDeprovision struct { // If empty, the value is equal to "AzurePublicCloud". // +optional CloudName *azure.CloudEnvironment `json:"cloudName,omitempty"` + // ResourceGroupName is the name of the resource group where the cluster was installed. + // Required for new deprovisions (schema notwithstanding). + // +optional + ResourceGroupName *string `json:"resourceGroupName,omitempty"` } // GCPClusterDeprovision contains GCP-specific configuration for a ClusterDeprovision @@ -80,6 +110,10 @@ type GCPClusterDeprovision struct { Region string `json:"region"` // CredentialsSecretRef is the GCP account credentials to use for deprovisioning the cluster CredentialsSecretRef *corev1.LocalObjectReference `json:"credentialsSecretRef,omitempty"` + + // NetworkProjectID is used for shared VPC setups + // +optional + NetworkProjectID *string `json:"networkProjectID,omitempty"` } // OpenStackClusterDeprovision contains OpenStack-specific configuration for a ClusterDeprovision @@ -122,13 +156,10 @@ type OvirtClusterDeprovision struct { type IBMClusterDeprovision struct { // CredentialsSecretRef is the IBM Cloud credentials to use for deprovisioning the cluster CredentialsSecretRef corev1.LocalObjectReference `json:"credentialsSecretRef"` - // AccountID is the IBM Cloud Account ID - AccountID string `json:"accountID"` - // CISInstanceCRN is the IBM Cloud Internet Services Instance CRN - CISInstanceCRN string `json:"cisInstanceCRN"` // Region specifies the IBM Cloud region Region string `json:"region"` - // BaseDomain is the DNS base domain + // BaseDomain is the DNS base domain. + // TODO: Use the non-platform-specific BaseDomain field. BaseDomain string `json:"baseDomain"` } @@ -174,6 +205,16 @@ type ClusterDeprovisionCondition struct { // ClusterDeprovisionConditionType is a valid value for ClusterDeprovisionCondition.Type type ClusterDeprovisionConditionType string +// ConditionType satisfies the conditions.Condition interface +func (c ClusterDeprovisionCondition) ConditionType() ConditionType { + return c.Type +} + +// String satisfies the conditions.ConditionType interface +func (t ClusterDeprovisionConditionType) String() string { + return string(t) +} + const ( // AuthenticationFailureClusterDeprovisionCondition is true when credentials cannot be used because of authentication failure AuthenticationFailureClusterDeprovisionCondition ClusterDeprovisionConditionType = "AuthenticationFailure" diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/clusterinstall_conditions.go b/vendor/github.com/openshift/hive/apis/hive/v1/clusterinstall_conditions.go index 8ec30212027..088d586d548 100644 --- a/vendor/github.com/openshift/hive/apis/hive/v1/clusterinstall_conditions.go +++ b/vendor/github.com/openshift/hive/apis/hive/v1/clusterinstall_conditions.go @@ -10,7 +10,7 @@ import ( // ClusterInstallCondition contains details for the current condition of a cluster install. type ClusterInstallCondition struct { // Type is the type of the condition. - Type string `json:"type"` + Type ClusterInstallConditionType `json:"type"` // Status is the status of the condition. Status corev1.ConditionStatus `json:"status"` // LastProbeTime is the last time we probed the condition. @@ -27,20 +27,32 @@ type ClusterInstallCondition struct { Message string `json:"message,omitempty"` } +type ClusterInstallConditionType string + +// ConditionType satisfies the conditions.Condition interface +func (c ClusterInstallCondition) ConditionType() ConditionType { + return c.Type +} + +// String satisfies the conditions.ConditionType interface +func (t ClusterInstallConditionType) String() string { + return string(t) +} + const ( // ClusterInstallRequirementsMet is True when all pre-install requirements have been met. - ClusterInstallRequirementsMet = "RequirementsMet" + ClusterInstallRequirementsMet ClusterInstallConditionType = "RequirementsMet" // ClusterInstallCompleted is True when the requested install has been completed successfully. - ClusterInstallCompleted = "Completed" + ClusterInstallCompleted ClusterInstallConditionType = "Completed" // ClusterInstallFailed is True when an attempt to install the cluster has failed. // The ClusterInstall controllers may still be retrying if supported, and this condition will // go back to False if a later attempt succeeds. - ClusterInstallFailed = "Failed" + ClusterInstallFailed ClusterInstallConditionType = "Failed" // ClusterInstallStopped is True the controllers are no longer working on this // ClusterInstall. Combine with Completed or Failed to know if the overall request was // successful or not. - ClusterInstallStopped = "Stopped" + ClusterInstallStopped ClusterInstallConditionType = "Stopped" ) diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/clusterpool_types.go b/vendor/github.com/openshift/hive/apis/hive/v1/clusterpool_types.go index 06bde7e10f8..577a0086163 100644 --- a/vendor/github.com/openshift/hive/apis/hive/v1/clusterpool_types.go +++ b/vendor/github.com/openshift/hive/apis/hive/v1/clusterpool_types.go @@ -92,6 +92,11 @@ type ClusterPoolSpec struct { // HibernationConfig configures the hibernation/resume behavior of ClusterDeployments owned by the ClusterPool. // +optional HibernationConfig *HibernationConfig `json:"hibernationConfig"` + + // Inventory maintains a list of entries consumed by the ClusterPool + // to customize the default ClusterDeployment. + // +optional + Inventory []InventoryEntry `json:"inventory,omitempty"` } type HibernationConfig struct { @@ -110,6 +115,22 @@ type HibernationConfig struct { ResumeTimeout metav1.Duration `json:"resumeTimeout"` } +// InventoryEntryKind is the Kind of the inventory entry. +// +kubebuilder:validation:Enum="";ClusterDeploymentCustomization +type InventoryEntryKind string + +const ClusterDeploymentCustomizationInventoryEntry InventoryEntryKind = "ClusterDeploymentCustomization" + +// InventoryEntry maintains a reference to a custom resource consumed by a clusterpool to customize the cluster deployment. +type InventoryEntry struct { + // Kind denotes the kind of the referenced resource. The default is ClusterDeploymentCustomization, which is also currently the only supported value. + // +kubebuilder:default=ClusterDeploymentCustomization + Kind InventoryEntryKind `json:"kind,omitempty"` + // Name is the name of the referenced resource. + // +required + Name string `json:"name,omitempty"` +} + // ClusterPoolClaimLifetime defines the lifetimes for claims for the cluster pool. type ClusterPoolClaimLifetime struct { // Default is the default lifetime of the claim when no lifetime is set on the claim itself. @@ -177,6 +198,16 @@ type ClusterPoolCondition struct { // ClusterPoolConditionType is a valid value for ClusterPoolCondition.Type type ClusterPoolConditionType string +// ConditionType satisfies the conditions.Condition interface +func (c ClusterPoolCondition) ConditionType() ConditionType { + return c.Type +} + +// String satisfies the conditions.ConditionType interface +func (t ClusterPoolConditionType) String() string { + return string(t) +} + const ( // ClusterPoolMissingDependenciesCondition is set when a cluster pool is missing dependencies required to create a // cluster. Dependencies include resources such as the ClusterImageSet and the credentials Secret. @@ -187,6 +218,20 @@ const ( // ClusterPoolAllClustersCurrentCondition indicates whether all unassigned (installing or ready) // ClusterDeployments in the pool match the current configuration of the ClusterPool. ClusterPoolAllClustersCurrentCondition ClusterPoolConditionType = "AllClustersCurrent" + // ClusterPoolInventoryValidCondition is set to provide information on whether the cluster pool inventory is valid. + ClusterPoolInventoryValidCondition ClusterPoolConditionType = "InventoryValid" + // ClusterPoolDeletionPossibleCondition gives information about a deleted ClusterPool which is pending cleanup. + // Note that it is normal for this condition to remain Initialized/Unknown until the ClusterPool is deleted. + ClusterPoolDeletionPossibleCondition ClusterPoolConditionType = "DeletionPossible" +) + +const ( + // InventoryReasonValid is used when all ClusterDeploymentCustomization are + // available and when used the ClusterDeployments are successfully installed. + InventoryReasonValid = "Valid" + // InventoryReasonInvalid is used when there is something wrong with ClusterDeploymentCustomization, for example + // patching issue, provisioning failure, missing, etc. + InventoryReasonInvalid = "Invalid" ) // +genclient diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/clusterprovision_types.go b/vendor/github.com/openshift/hive/apis/hive/v1/clusterprovision_types.go index b6274443862..2382c833bfd 100644 --- a/vendor/github.com/openshift/hive/apis/hive/v1/clusterprovision_types.go +++ b/vendor/github.com/openshift/hive/apis/hive/v1/clusterprovision_types.go @@ -31,8 +31,16 @@ type ClusterProvisionSpec struct { InstallLog *string `json:"installLog,omitempty"` // Metadata is the metadata.json generated by the installer, providing metadata information about the cluster created. + // NOTE: This is not used because it didn't work (it was always empty). We think because the thing it's storing + // (ClusterMetadata from installer) is not a runtime.Object, so can't be put in a RawExtension. Metadata *runtime.RawExtension `json:"metadata,omitempty"` + // MetadataJSON is a JSON representation of the ClusterMetadata produced by the installer. We don't use a + // runtime.RawExtension because ClusterMetadata isn't a runtime.Object. We don't use ClusterMetadata itself + // because we don't want our API consumers to need to pull in the installer code and its dependencies. + // +optional + MetadataJSON []byte `json:"metadataJSON,omitempty"` + // AdminKubeconfigSecretRef references the secret containing the admin kubeconfig for this cluster. AdminKubeconfigSecretRef *corev1.LocalObjectReference `json:"adminKubeconfigSecretRef,omitempty"` @@ -96,6 +104,16 @@ type ClusterProvisionCondition struct { // ClusterProvisionConditionType is a valid value for ClusterProvisionCondition.Type type ClusterProvisionConditionType string +// ConditionType satisfies the conditions.Condition interface +func (c ClusterProvisionCondition) ConditionType() ConditionType { + return c.Type +} + +// String satisfies the conditions.ConditionType interface +func (t ClusterProvisionConditionType) String() string { + return string(t) +} + const ( // ClusterProvisionInitializedCondition is set when a cluster provision has finished initialization. ClusterProvisionInitializedCondition ClusterProvisionConditionType = "ClusterProvisionInitialized" diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/conditions.go b/vendor/github.com/openshift/hive/apis/hive/v1/conditions.go new file mode 100644 index 00000000000..0a8bc956d85 --- /dev/null +++ b/vendor/github.com/openshift/hive/apis/hive/v1/conditions.go @@ -0,0 +1,11 @@ +package v1 + +import "fmt" + +type Condition interface { + ConditionType() ConditionType +} + +type ConditionType interface { + fmt.Stringer +} diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/dnszone_types.go b/vendor/github.com/openshift/hive/apis/hive/v1/dnszone_types.go index a2315138a23..9ebd599b38a 100644 --- a/vendor/github.com/openshift/hive/apis/hive/v1/dnszone_types.go +++ b/vendor/github.com/openshift/hive/apis/hive/v1/dnszone_types.go @@ -179,6 +179,16 @@ type DNSZoneCondition struct { // DNSZoneConditionType is a valid value for DNSZoneCondition.Type type DNSZoneConditionType string +// ConditionType satisfies the conditions.Condition interface +func (c DNSZoneCondition) ConditionType() ConditionType { + return c.Type +} + +// String satisfies the conditions.ConditionType interface +func (t DNSZoneConditionType) String() string { + return string(t) +} + const ( // ZoneAvailableDNSZoneCondition is true if the DNSZone is responding to DNS queries ZoneAvailableDNSZoneCondition DNSZoneConditionType = "ZoneAvailable" diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/gcp/machinepools.go b/vendor/github.com/openshift/hive/apis/hive/v1/gcp/machinepools.go index 283099f2fb9..c8a595b295f 100644 --- a/vendor/github.com/openshift/hive/apis/hive/v1/gcp/machinepools.go +++ b/vendor/github.com/openshift/hive/apis/hive/v1/gcp/machinepools.go @@ -13,6 +13,18 @@ type MachinePool struct { // // +optional OSDisk OSDisk `json:"osDisk"` + + // NetworkProjectID specifies which project the network and subnets exist in when + // they are not in the main ProjectID. + // +optional + NetworkProjectID string `json:"networkProjectID,omitempty"` + + // SecureBoot Defines whether the instance should have secure boot enabled. + // Verifies the digital signature of all boot components, and halts the boot process if signature verification fails. + // If omitted, the platform chooses a default, which is subject to change over time. Currently that default is "Disabled". + // +kubebuilder:validation:Enum=Enabled;Disabled + // +optional + SecureBoot string `json:"secureBoot,omitempty"` } // OSDisk defines the disk for machines on GCP. diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/gcp/metadata.go b/vendor/github.com/openshift/hive/apis/hive/v1/gcp/metadata.go index fcdc59e6a2d..556783130db 100644 --- a/vendor/github.com/openshift/hive/apis/hive/v1/gcp/metadata.go +++ b/vendor/github.com/openshift/hive/apis/hive/v1/gcp/metadata.go @@ -2,6 +2,7 @@ package gcp // Metadata contains GCP metadata (e.g. for uninstalling the cluster). type Metadata struct { - Region string `json:"region"` - ProjectID string `json:"projectID"` + // NetworkProjectID is used for shared VPC setups + // +optional + NetworkProjectID *string `json:"networkProjectID,omitempty"` } diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/gcp/zz_generated.deepcopy.go b/vendor/github.com/openshift/hive/apis/hive/v1/gcp/zz_generated.deepcopy.go index ae6087f2afb..30b4a9df6c2 100644 --- a/vendor/github.com/openshift/hive/apis/hive/v1/gcp/zz_generated.deepcopy.go +++ b/vendor/github.com/openshift/hive/apis/hive/v1/gcp/zz_generated.deepcopy.go @@ -67,6 +67,11 @@ func (in *MachinePool) DeepCopy() *MachinePool { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Metadata) DeepCopyInto(out *Metadata) { *out = *in + if in.NetworkProjectID != nil { + in, out := &in.NetworkProjectID, &out.NetworkProjectID + *out = new(string) + **out = **in + } return } diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/hiveconfig_types.go b/vendor/github.com/openshift/hive/apis/hive/v1/hiveconfig_types.go index a0dbb70e475..034d267d756 100644 --- a/vendor/github.com/openshift/hive/apis/hive/v1/hiveconfig_types.go +++ b/vendor/github.com/openshift/hive/apis/hive/v1/hiveconfig_types.go @@ -5,6 +5,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/openshift/hive/apis/hive/v1/azure" + "github.com/openshift/hive/apis/hive/v1/metricsconfig" ) // HiveConfigSpec defines the desired state of Hive @@ -89,6 +90,10 @@ type HiveConfigSpec struct { // +optional ControllersConfig *ControllersConfig `json:"controllersConfig,omitempty"` + // DeploymentConfig is used to configure (pods/containers of) the Deployments generated by hive-operator. + // +optional + DeploymentConfig *[]DeploymentConfig `json:"deploymentConfig,omitempty"` + // AWSPrivateLink defines the configuration for the aws-private-link controller. // It provides 3 major pieces of information required by the controller, // 1. The Credentials that should be used to create AWS PrivateLink resources other than @@ -138,12 +143,19 @@ type HiveConfigSpec struct { FeatureGates *FeatureGateSelection `json:"featureGates,omitempty"` - // ExportMetrics specifies whether the operator should enable metrics for hive controllers - // to be extracted for prometheus. - // When set to true, the operator deploys ServiceMonitors so that the prometheus instances that - // extract metrics. The operator also sets up RBAC in the TargetNamespace so that openshift - // prometheus in the cluster can list/access objects required to pull metrics. + // ExportMetrics has been disabled and has no effect. If upgrading from a version where it was + // active, please be aware of the following in your HiveConfig.Spec.TargetNamespace (default + // `hive` if unset): + // 1) ServiceMonitors named hive-controllers and hive-clustersync; + // 2) Role and RoleBinding named prometheus-k8s; + // 3) The `openshift.io/cluster-monitoring` metadata.label on the Namespace itself. + // You may wish to delete these resources. Or you may wish to continue using them to enable + // monitoring in your environment; but be aware that hive will no longer reconcile them. ExportMetrics bool `json:"exportMetrics,omitempty"` + + // MetricsConfig encapsulates metrics specific configurations, like opting in for certain metrics. + // +optional + MetricsConfig *metricsconfig.MetricsConfig `json:"metricsConfig,omitempty"` } // ReleaseImageVerificationConfigMapReference is a reference to the ConfigMap that @@ -594,6 +606,27 @@ type ControllersConfig struct { Controllers []SpecificControllerConfig `json:"controllers,omitempty"` } +type DeploymentName string + +const ( + DeploymentNameControllers DeploymentName = "hive-controllers" + DeploymentNameClustersync DeploymentName = "hive-clustersync" + DeploymentNameAdmission DeploymentName = "hiveadmission" +) + +type DeploymentConfig struct { + // DeploymentName is the name of one of the Deployments/StatefulSets managed by hive-operator. + // NOTE: At this time each deployment has only one container. In the future, we may provide a + // way to specify which container this DeploymentConfig will be applied to. + // +kubebuilder:validation:Enum=hive-controllers;hive-clustersync;hiveadmission + DeploymentName DeploymentName `json:"deploymentName"` + + // Resources allows customization of the resource (memory, CPU, etc.) limits and requests used + // by containers in the Deployment/StatefulSet named by DeploymentName. + // +optional + Resources *corev1.ResourceRequirements `json:"resources"` +} + // +genclient:nonNamespaced // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/ibmcloud/platform.go b/vendor/github.com/openshift/hive/apis/hive/v1/ibmcloud/platform.go index 1a5645708b5..1de24acb84d 100644 --- a/vendor/github.com/openshift/hive/apis/hive/v1/ibmcloud/platform.go +++ b/vendor/github.com/openshift/hive/apis/hive/v1/ibmcloud/platform.go @@ -9,13 +9,16 @@ type Platform struct { // CredentialsSecretRef refers to a secret that contains IBM Cloud account access // credentials. CredentialsSecretRef corev1.LocalObjectReference `json:"credentialsSecretRef"` - - // AccountID is the IBM Cloud Account ID - AccountID string `json:"accountID"` - + // AccountID is the IBM Cloud Account ID. + // AccountID is DEPRECATED and is gathered via the IBM Cloud API for the provided + // credentials. This field will be ignored. + // +optional + AccountID string `json:"accountID,omitempty"` // CISInstanceCRN is the IBM Cloud Internet Services Instance CRN - CISInstanceCRN string `json:"cisInstanceCRN"` - + // CISInstanceCRN is DEPRECATED and gathered via the IBM Cloud API for the provided + // credentials and cluster deployment base domain. This field will be ignored. + // +optional + CISInstanceCRN string `json:"cisInstanceCRN,omitempty"` // Region specifies the IBM Cloud region where the cluster will be // created. Region string `json:"region"` diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/machinepool_types.go b/vendor/github.com/openshift/hive/apis/hive/v1/machinepool_types.go index 3dc0b06170f..7210abfa293 100644 --- a/vendor/github.com/openshift/hive/apis/hive/v1/machinepool_types.go +++ b/vendor/github.com/openshift/hive/apis/hive/v1/machinepool_types.go @@ -1,6 +1,7 @@ package v1 import ( + "github.com/openshift/hive/apis/hive/v1/alibabacloud" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -15,7 +16,7 @@ import ( const ( // MachinePoolImageIDOverrideAnnotation can be applied to MachinePools to control the precise image ID to be used - // for the MachineSets we reconcile for this pool. This feature is presently only implemented for AWS, and + // for the MachineSets we reconcile for this pool. This feature is presently only implemented for AWS and GCP, and // is intended for very limited use cases we do not recommend pursuing regularly. As such it is not currently // part of our official API. MachinePoolImageIDOverrideAnnotation = "hive.openshift.io/image-id-override" @@ -53,6 +54,9 @@ type MachinePoolSpec struct { // List of taints that will be applied to the created MachineSet's MachineSpec. // This list will overwrite any modifications made to Node taints on an ongoing basis. + // In case of duplicate entries, first encountered taint Value will be preserved, + // and the rest collapsed on the corresponding MachineSets. + // Note that taints are uniquely identified based on key+effect, not just key. // +optional Taints []corev1.Taint `json:"taints,omitempty"` } @@ -69,6 +73,8 @@ type MachinePoolAutoscaling struct { // MachinePoolPlatform is the platform-specific configuration for a machine // pool. Only one of the platforms should be set. type MachinePoolPlatform struct { + // AlibabaCloud is the configuration used when installing on Alibaba Cloud. + AlibabaCloud *alibabacloud.MachinePool `json:"alibabacloud,omitempty"` // AWS is the configuration used when installing on AWS. AWS *aws.MachinePoolPlatform `json:"aws,omitempty"` // Azure is the configuration used when installing on Azure. @@ -97,6 +103,26 @@ type MachinePoolStatus struct { // Conditions includes more detailed status for the cluster deployment // +optional Conditions []MachinePoolCondition `json:"conditions,omitempty"` + + // OwnedLabels lists the keys of labels this MachinePool created on the remote MachineSet. + // Used to identify labels to remove from the remote MachineSet when they are absent from + // the MachinePool's spec.labels. + // +optional + OwnedLabels []string `json:"ownedLabels,omitempty"` + // OwnedTaints lists identifiers of taints this MachinePool created on the remote MachineSet. + // Used to identify taints to remove from the remote MachineSet when they are absent from + // the MachinePool's spec.taints. + // +optional + OwnedTaints []TaintIdentifier `json:"ownedTaints,omitempty"` +} + +// TaintIdentifier uniquely identifies a Taint. (It turns out taints are mutually exclusive by +// key+effect, not simply by key.) +type TaintIdentifier struct { + // Key matches corev1.Taint.Key. + Key string `json:"key,omitempty"` + // Effect matches corev1.Taint.Effect. + Effect corev1.TaintEffect `json:"effect,omitempty"` } // MachineSetStatus is the status of a machineset in the remote cluster. @@ -153,6 +179,16 @@ type MachinePoolCondition struct { // MachinePoolConditionType is a valid value for MachinePoolCondition.Type type MachinePoolConditionType string +// ConditionType satisfies the conditions.Condition interface +func (c MachinePoolCondition) ConditionType() ConditionType { + return c.Type +} + +// String satisfies the conditions.ConditionType interface +func (t MachinePoolConditionType) String() string { + return string(t) +} + const ( // NotEnoughReplicasMachinePoolCondition is true when the minReplicas field // is set too low for the number of machinesets for the machine pool. diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/metricsconfig/doc.go b/vendor/github.com/openshift/hive/apis/hive/v1/metricsconfig/doc.go new file mode 100644 index 00000000000..422f98a2f04 --- /dev/null +++ b/vendor/github.com/openshift/hive/apis/hive/v1/metricsconfig/doc.go @@ -0,0 +1,4 @@ +// Package metricsconfig contains API Schema definitions for configurations specific to metrics controller. +// +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=github.com/openshift/hive/apis/hive +package metricsconfig diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/metricsconfig/duration_metrics.go b/vendor/github.com/openshift/hive/apis/hive/v1/metricsconfig/duration_metrics.go new file mode 100644 index 00000000000..e81f3f94b41 --- /dev/null +++ b/vendor/github.com/openshift/hive/apis/hive/v1/metricsconfig/duration_metrics.go @@ -0,0 +1,41 @@ +package metricsconfig + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +// MetricsWithDuration represents metrics that report time as values,like transition seconds. +// The purpose of these metrics should be to track outliers - ensure their duration is not set too low. +type MetricsWithDuration struct { + // Name of the metric. It will correspond to an optional relevant metric in hive + // +kubebuilder:validation:Enum=currentStopping;currentResuming;currentWaitingForCO;currentClusterSyncFailing;cumulativeHibernated;cumulativeResumed + Name DurationMetricType `json:"name"` + // Duration is the minimum time taken - the relevant metric will be logged only if the value reported by that metric + // is more than the time mentioned here. For example, if a user opts-in for current clusters stopping and mentions + // 1 hour here, only the clusters stopping for more than an hour will be reported. + // This is a Duration value; see https://pkg.go.dev/time#ParseDuration for accepted formats. + // +kubebuilder:validation:Type=string + // +kubebuilder:validation:Pattern="^([0-9]+(\\.[0-9]+)?(ns|us|µs|ms|s|m|h))+$" + Duration *metav1.Duration `json:"duration"` +} + +// DurationMetricType is a valid value for MetricsWithDuration.Name +type DurationMetricType string + +const ( + // Metrics logged per cluster + + // CurrentStopping corresponds to hive_cluster_deployments_stopping_seconds + CurrentStopping DurationMetricType = "currentStopping" + // CurrentResuming corresponds to hive_cluster_deployments_resuming_seconds + CurrentResuming DurationMetricType = "currentResuming" + // CurrentWaitingForCO corresponds to hive_cluster_deployments_waiting_for_cluster_operators_seconds + CurrentWaitingForCO DurationMetricType = "currentWaitingForCO" + // CurrentClusterSyncFailing corresponds to hive_clustersync_failing_seconds + CurrentClusterSyncFailing DurationMetricType = "currentClusterSyncFailing" + + // These metrics will not be cleared and can potentially blow up the cardinality + + // CumulativeHibernated corresponds to hive_cluster_deployment_hibernation_transition_seconds + CumulativeHibernated DurationMetricType = "cumulativeHibernated" + // CumulativeResumed corresponds to hive_cluster_deployment_running_transition_seconds + CumulativeResumed DurationMetricType = "cumulativeResumed" +) diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/metricsconfig/metrics_config.go b/vendor/github.com/openshift/hive/apis/hive/v1/metricsconfig/metrics_config.go new file mode 100644 index 00000000000..7a85e3e00d5 --- /dev/null +++ b/vendor/github.com/openshift/hive/apis/hive/v1/metricsconfig/metrics_config.go @@ -0,0 +1,20 @@ +package metricsconfig + +type MetricsConfig struct { + // Optional metrics and their configurations + // +optional + MetricsWithDuration []MetricsWithDuration `json:"metricsWithDuration,omitempty"` + // AdditionalClusterDeploymentLabels allows configuration of additional labels to be applied to certain metrics. + // The keys can be any string value suitable for a metric label (see https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels). + // The values can be any ClusterDeployment label key (from metadata.labels). When observing an affected metric, + // hive will label it with the specified metric key, and copy the value from the specified ClusterDeployment label. + // For example, including {"ocp_major_version": "hive.openshift.io/version-major"} will cause affected metrics to + // include a label key ocp_major_version with the value from the hive.openshift.io/version-major ClusterDeployment + // label -- e.g. "4". + // NOTE: Avoid ClusterDeployment labels whose values are unbounded, such as those representing cluster names or IDs, + // as these will cause your prometheus database to grow indefinitely. + // Affected metrics are those whose type implements the metricsWithDynamicLabels interface found in + // pkg/controller/metrics/metrics_with_dynamic_labels.go + // +optional + AdditionalClusterDeploymentLabels *map[string]string `json:"additionalClusterDeploymentLabels,omitempty"` +} diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/metricsconfig/zz_generated.deepcopy.go b/vendor/github.com/openshift/hive/apis/hive/v1/metricsconfig/zz_generated.deepcopy.go new file mode 100644 index 00000000000..2f657023dcb --- /dev/null +++ b/vendor/github.com/openshift/hive/apis/hive/v1/metricsconfig/zz_generated.deepcopy.go @@ -0,0 +1,65 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package metricsconfig + +import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MetricsConfig) DeepCopyInto(out *MetricsConfig) { + *out = *in + if in.MetricsWithDuration != nil { + in, out := &in.MetricsWithDuration, &out.MetricsWithDuration + *out = make([]MetricsWithDuration, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.AdditionalClusterDeploymentLabels != nil { + in, out := &in.AdditionalClusterDeploymentLabels, &out.AdditionalClusterDeploymentLabels + *out = new(map[string]string) + if **in != nil { + in, out := *in, *out + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricsConfig. +func (in *MetricsConfig) DeepCopy() *MetricsConfig { + if in == nil { + return nil + } + out := new(MetricsConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MetricsWithDuration) DeepCopyInto(out *MetricsWithDuration) { + *out = *in + if in.Duration != nil { + in, out := &in.Duration, &out.Duration + *out = new(v1.Duration) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricsWithDuration. +func (in *MetricsWithDuration) DeepCopy() *MetricsWithDuration { + if in == nil { + return nil + } + out := new(MetricsWithDuration) + in.DeepCopyInto(out) + return out +} diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/none/doc.go b/vendor/github.com/openshift/hive/apis/hive/v1/none/doc.go new file mode 100644 index 00000000000..f2bb8992e36 --- /dev/null +++ b/vendor/github.com/openshift/hive/apis/hive/v1/none/doc.go @@ -0,0 +1,4 @@ +// Package none contains API Schema definitions for platform-agnostic installations. +// +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=github.com/openshift/hive/apis/hive +package none diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/none/platform.go b/vendor/github.com/openshift/hive/apis/hive/v1/none/platform.go new file mode 100644 index 00000000000..e80ef0f0ed0 --- /dev/null +++ b/vendor/github.com/openshift/hive/apis/hive/v1/none/platform.go @@ -0,0 +1,6 @@ +package none + +// Platform defines agent based install configuration for platform-agnostic clusters. +// Can only be used with spec.installStrategy.agent. +type Platform struct { +} diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/none/zz_generated.deepcopy.go b/vendor/github.com/openshift/hive/apis/hive/v1/none/zz_generated.deepcopy.go new file mode 100644 index 00000000000..e474004e81a --- /dev/null +++ b/vendor/github.com/openshift/hive/apis/hive/v1/none/zz_generated.deepcopy.go @@ -0,0 +1,22 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package none + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Platform) DeepCopyInto(out *Platform) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Platform. +func (in *Platform) DeepCopy() *Platform { + if in == nil { + return nil + } + out := new(Platform) + in.DeepCopyInto(out) + return out +} diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/syncset_types.go b/vendor/github.com/openshift/hive/apis/hive/v1/syncset_types.go index cabc7fbb679..fafdc9d935a 100644 --- a/vendor/github.com/openshift/hive/apis/hive/v1/syncset_types.go +++ b/vendor/github.com/openshift/hive/apis/hive/v1/syncset_types.go @@ -106,6 +106,16 @@ type SecretMapping struct { // SyncConditionType is a valid value for SyncCondition.Type type SyncConditionType string +// ConditionType satisfies the conditions.Condition interface +func (c SyncCondition) ConditionType() ConditionType { + return c.Type +} + +// String satisfies the conditions.ConditionType interface +func (t SyncConditionType) String() string { + return string(t) +} + const ( // ApplySuccessSyncCondition indicates whether the resource or patch has been applied. ApplySuccessSyncCondition SyncConditionType = "ApplySuccess" diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/vsphere/machinepools.go b/vendor/github.com/openshift/hive/apis/hive/v1/vsphere/machinepools.go index 95ca854eb20..71e67294ba8 100644 --- a/vendor/github.com/openshift/hive/apis/hive/v1/vsphere/machinepools.go +++ b/vendor/github.com/openshift/hive/apis/hive/v1/vsphere/machinepools.go @@ -3,6 +3,11 @@ package vsphere // MachinePool stores the configuration for a machine pool installed // on vSphere. type MachinePool struct { + // ResourcePool is the name of the resource pool that will be used for virtual machines. + // If it is not present, a default value will be used. + // +optional + ResourcePool string `json:"resourcePool,omitempty"` + // NumCPUs is the total number of virtual processor cores to assign a vm. NumCPUs int32 `json:"cpus"` diff --git a/vendor/github.com/openshift/hive/apis/hive/v1/zz_generated.deepcopy.go b/vendor/github.com/openshift/hive/apis/hive/v1/zz_generated.deepcopy.go index dfdd9fbed1b..4e4554200bd 100644 --- a/vendor/github.com/openshift/hive/apis/hive/v1/zz_generated.deepcopy.go +++ b/vendor/github.com/openshift/hive/apis/hive/v1/zz_generated.deepcopy.go @@ -7,12 +7,16 @@ package v1 import ( configv1 "github.com/openshift/api/config/v1" + conditionsv1 "github.com/openshift/custom-resource-status/conditions/v1" agent "github.com/openshift/hive/apis/hive/v1/agent" + alibabacloud "github.com/openshift/hive/apis/hive/v1/alibabacloud" aws "github.com/openshift/hive/apis/hive/v1/aws" azure "github.com/openshift/hive/apis/hive/v1/azure" baremetal "github.com/openshift/hive/apis/hive/v1/baremetal" gcp "github.com/openshift/hive/apis/hive/v1/gcp" ibmcloud "github.com/openshift/hive/apis/hive/v1/ibmcloud" + metricsconfig "github.com/openshift/hive/apis/hive/v1/metricsconfig" + none "github.com/openshift/hive/apis/hive/v1/none" openstack "github.com/openshift/hive/apis/hive/v1/openstack" ovirt "github.com/openshift/hive/apis/hive/v1/ovirt" vsphere "github.com/openshift/hive/apis/hive/v1/vsphere" @@ -57,6 +61,11 @@ func (in *AWSClusterDeprovision) DeepCopyInto(out *AWSClusterDeprovision) { *out = new(aws.AssumeRole) **out = **in } + if in.HostedZoneRole != nil { + in, out := &in.HostedZoneRole, &out.HostedZoneRole + *out = new(string) + **out = **in + } return } @@ -236,6 +245,23 @@ func (in *AWSServiceProviderCredentials) DeepCopy() *AWSServiceProviderCredentia return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AlibabaCloudClusterDeprovision) DeepCopyInto(out *AlibabaCloudClusterDeprovision) { + *out = *in + out.CredentialsSecretRef = in.CredentialsSecretRef + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AlibabaCloudClusterDeprovision. +func (in *AlibabaCloudClusterDeprovision) DeepCopy() *AlibabaCloudClusterDeprovision { + if in == nil { + return nil + } + out := new(AlibabaCloudClusterDeprovision) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ArgoCDConfig) DeepCopyInto(out *ArgoCDConfig) { *out = *in @@ -265,6 +291,11 @@ func (in *AzureClusterDeprovision) DeepCopyInto(out *AzureClusterDeprovision) { *out = new(azure.CloudEnvironment) **out = **in } + if in.ResourceGroupName != nil { + in, out := &in.ResourceGroupName, &out.ResourceGroupName + *out = new(string) + **out = **in + } return } @@ -656,6 +687,121 @@ func (in *ClusterDeploymentCondition) DeepCopy() *ClusterDeploymentCondition { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterDeploymentCustomization) DeepCopyInto(out *ClusterDeploymentCustomization) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterDeploymentCustomization. +func (in *ClusterDeploymentCustomization) DeepCopy() *ClusterDeploymentCustomization { + if in == nil { + return nil + } + out := new(ClusterDeploymentCustomization) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterDeploymentCustomization) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterDeploymentCustomizationList) DeepCopyInto(out *ClusterDeploymentCustomizationList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ClusterDeploymentCustomization, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterDeploymentCustomizationList. +func (in *ClusterDeploymentCustomizationList) DeepCopy() *ClusterDeploymentCustomizationList { + if in == nil { + return nil + } + out := new(ClusterDeploymentCustomizationList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterDeploymentCustomizationList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterDeploymentCustomizationSpec) DeepCopyInto(out *ClusterDeploymentCustomizationSpec) { + *out = *in + if in.InstallConfigPatches != nil { + in, out := &in.InstallConfigPatches, &out.InstallConfigPatches + *out = make([]PatchEntity, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterDeploymentCustomizationSpec. +func (in *ClusterDeploymentCustomizationSpec) DeepCopy() *ClusterDeploymentCustomizationSpec { + if in == nil { + return nil + } + out := new(ClusterDeploymentCustomizationSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterDeploymentCustomizationStatus) DeepCopyInto(out *ClusterDeploymentCustomizationStatus) { + *out = *in + if in.ClusterDeploymentRef != nil { + in, out := &in.ClusterDeploymentRef, &out.ClusterDeploymentRef + *out = new(corev1.LocalObjectReference) + **out = **in + } + if in.ClusterPoolRef != nil { + in, out := &in.ClusterPoolRef, &out.ClusterPoolRef + *out = new(corev1.LocalObjectReference) + **out = **in + } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]conditionsv1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterDeploymentCustomizationStatus. +func (in *ClusterDeploymentCustomizationStatus) DeepCopy() *ClusterDeploymentCustomizationStatus { + if in == nil { + return nil + } + out := new(ClusterDeploymentCustomizationStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterDeploymentList) DeepCopyInto(out *ClusterDeploymentList) { *out = *in @@ -902,6 +1048,11 @@ func (in *ClusterDeprovisionList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterDeprovisionPlatform) DeepCopyInto(out *ClusterDeprovisionPlatform) { *out = *in + if in.AlibabaCloud != nil { + in, out := &in.AlibabaCloud, &out.AlibabaCloud + *out = new(AlibabaCloudClusterDeprovision) + **out = **in + } if in.AWS != nil { in, out := &in.AWS, &out.AWS *out = new(AWSClusterDeprovision) @@ -1112,6 +1263,11 @@ func (in *ClusterIngress) DeepCopyInto(out *ClusterIngress) { *out = new(metav1.LabelSelector) (*in).DeepCopyInto(*out) } + if in.HttpErrorCodePages != nil { + in, out := &in.HttpErrorCodePages, &out.HttpErrorCodePages + *out = new(configv1.ConfigMapNameReference) + **out = **in + } return } @@ -1168,6 +1324,11 @@ func (in *ClusterMetadata) DeepCopyInto(out *ClusterMetadata) { *out = new(corev1.LocalObjectReference) **out = **in } + if in.Platform != nil { + in, out := &in.Platform, &out.Platform + *out = new(ClusterPlatformMetadata) + (*in).DeepCopyInto(*out) + } return } @@ -1204,6 +1365,37 @@ func (in *ClusterOperatorState) DeepCopy() *ClusterOperatorState { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterPlatformMetadata) DeepCopyInto(out *ClusterPlatformMetadata) { + *out = *in + if in.AWS != nil { + in, out := &in.AWS, &out.AWS + *out = new(aws.Metadata) + (*in).DeepCopyInto(*out) + } + if in.Azure != nil { + in, out := &in.Azure, &out.Azure + *out = new(azure.Metadata) + (*in).DeepCopyInto(*out) + } + if in.GCP != nil { + in, out := &in.GCP, &out.GCP + *out = new(gcp.Metadata) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterPlatformMetadata. +func (in *ClusterPlatformMetadata) DeepCopy() *ClusterPlatformMetadata { + if in == nil { + return nil + } + out := new(ClusterPlatformMetadata) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterPool) DeepCopyInto(out *ClusterPool) { *out = *in @@ -1316,6 +1508,11 @@ func (in *ClusterPoolReference) DeepCopyInto(out *ClusterPoolReference) { in, out := &in.ClaimedTimestamp, &out.ClaimedTimestamp *out = (*in).DeepCopy() } + if in.CustomizationRef != nil { + in, out := &in.CustomizationRef, &out.CustomizationRef + *out = new(corev1.LocalObjectReference) + **out = **in + } return } @@ -1388,6 +1585,11 @@ func (in *ClusterPoolSpec) DeepCopyInto(out *ClusterPoolSpec) { *out = new(HibernationConfig) **out = **in } + if in.Inventory != nil { + in, out := &in.Inventory, &out.Inventory + *out = make([]InventoryEntry, len(*in)) + copy(*out, *in) + } return } @@ -1528,6 +1730,11 @@ func (in *ClusterProvisionSpec) DeepCopyInto(out *ClusterProvisionSpec) { *out = new(runtime.RawExtension) (*in).DeepCopyInto(*out) } + if in.MetadataJSON != nil { + in, out := &in.MetadataJSON, &out.MetadataJSON + *out = make([]byte, len(*in)) + copy(*out, *in) + } if in.AdminKubeconfigSecretRef != nil { in, out := &in.AdminKubeconfigSecretRef, &out.AdminKubeconfigSecretRef *out = new(corev1.LocalObjectReference) @@ -2098,6 +2305,27 @@ func (in *DNSZoneStatus) DeepCopy() *DNSZoneStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeploymentConfig) DeepCopyInto(out *DeploymentConfig) { + *out = *in + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = new(corev1.ResourceRequirements) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentConfig. +func (in *DeploymentConfig) DeepCopy() *DeploymentConfig { + if in == nil { + return nil + } + out := new(DeploymentConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *FailedProvisionAWSConfig) DeepCopyInto(out *FailedProvisionAWSConfig) { *out = *in @@ -2195,6 +2423,11 @@ func (in *GCPClusterDeprovision) DeepCopyInto(out *GCPClusterDeprovision) { *out = new(corev1.LocalObjectReference) **out = **in } + if in.NetworkProjectID != nil { + in, out := &in.NetworkProjectID, &out.NetworkProjectID + *out = new(string) + **out = **in + } return } @@ -2385,6 +2618,17 @@ func (in *HiveConfigSpec) DeepCopyInto(out *HiveConfigSpec) { *out = new(ControllersConfig) (*in).DeepCopyInto(*out) } + if in.DeploymentConfig != nil { + in, out := &in.DeploymentConfig, &out.DeploymentConfig + *out = new([]DeploymentConfig) + if **in != nil { + in, out := *in, *out + *out = make([]DeploymentConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + } if in.AWSPrivateLink != nil { in, out := &in.AWSPrivateLink, &out.AWSPrivateLink *out = new(AWSPrivateLinkConfig) @@ -2401,6 +2645,11 @@ func (in *HiveConfigSpec) DeepCopyInto(out *HiveConfigSpec) { *out = new(FeatureGateSelection) (*in).DeepCopyInto(*out) } + if in.MetricsConfig != nil { + in, out := &in.MetricsConfig, &out.MetricsConfig + *out = new(metricsconfig.MetricsConfig) + (*in).DeepCopyInto(*out) + } return } @@ -2470,6 +2719,22 @@ func (in *IdentityProviderStatus) DeepCopy() *IdentityProviderStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InventoryEntry) DeepCopyInto(out *InventoryEntry) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InventoryEntry. +func (in *InventoryEntry) DeepCopy() *InventoryEntry { + if in == nil { + return nil + } + out := new(InventoryEntry) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *KubeconfigSecretReference) DeepCopyInto(out *KubeconfigSecretReference) { *out = *in @@ -2677,6 +2942,11 @@ func (in *MachinePoolNameLeaseStatus) DeepCopy() *MachinePoolNameLeaseStatus { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MachinePoolPlatform) DeepCopyInto(out *MachinePoolPlatform) { *out = *in + if in.AlibabaCloud != nil { + in, out := &in.AlibabaCloud, &out.AlibabaCloud + *out = new(alibabacloud.MachinePool) + (*in).DeepCopyInto(*out) + } if in.AWS != nil { in, out := &in.AWS, &out.AWS *out = new(aws.MachinePoolPlatform) @@ -2784,6 +3054,16 @@ func (in *MachinePoolStatus) DeepCopyInto(out *MachinePoolStatus) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.OwnedLabels != nil { + in, out := &in.OwnedLabels, &out.OwnedLabels + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.OwnedTaints != nil { + in, out := &in.OwnedTaints, &out.OwnedTaints + *out = make([]TaintIdentifier, len(*in)) + copy(*out, *in) + } return } @@ -2954,9 +3234,30 @@ func (in *OvirtClusterDeprovision) DeepCopy() *OvirtClusterDeprovision { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PatchEntity) DeepCopyInto(out *PatchEntity) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PatchEntity. +func (in *PatchEntity) DeepCopy() *PatchEntity { + if in == nil { + return nil + } + out := new(PatchEntity) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Platform) DeepCopyInto(out *Platform) { *out = *in + if in.AlibabaCloud != nil { + in, out := &in.AlibabaCloud, &out.AlibabaCloud + *out = new(alibabacloud.Platform) + **out = **in + } if in.AWS != nil { in, out := &in.AWS, &out.AWS *out = new(aws.Platform) @@ -3002,6 +3303,11 @@ func (in *Platform) DeepCopyInto(out *Platform) { *out = new(ibmcloud.Platform) **out = **in } + if in.None != nil { + in, out := &in.None, &out.None + *out = new(none.Platform) + **out = **in + } return } @@ -3054,6 +3360,11 @@ func (in *Provisioning) DeepCopyInto(out *Provisioning) { *out = new(corev1.LocalObjectReference) **out = **in } + if in.ManifestsSecretRef != nil { + in, out := &in.ManifestsSecretRef, &out.ManifestsSecretRef + *out = new(corev1.LocalObjectReference) + **out = **in + } if in.SSHPrivateKeySecretRef != nil { in, out := &in.SSHPrivateKeySecretRef, &out.SSHPrivateKeySecretRef *out = new(corev1.LocalObjectReference) @@ -3685,6 +3996,22 @@ func (in *SyncStatus) DeepCopy() *SyncStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaintIdentifier) DeepCopyInto(out *TaintIdentifier) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaintIdentifier. +func (in *TaintIdentifier) DeepCopy() *TaintIdentifier { + if in == nil { + return nil + } + out := new(TaintIdentifier) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VSphereClusterDeprovision) DeepCopyInto(out *VSphereClusterDeprovision) { *out = *in diff --git a/vendor/github.com/openshift/hive/apis/scheme/scheme.go b/vendor/github.com/openshift/hive/apis/scheme/scheme.go index 0afe1edf572..622ccaffecd 100644 --- a/vendor/github.com/openshift/hive/apis/scheme/scheme.go +++ b/vendor/github.com/openshift/hive/apis/scheme/scheme.go @@ -24,37 +24,36 @@ limitations under the License. // Each API group should define a utility function // called AddToScheme for adding its types to a Scheme: // -// // in package myapigroupv1... -// var ( -// SchemeGroupVersion = schema.GroupVersion{Group: "my.api.group", Version: "v1"} -// SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} -// AddToScheme = SchemeBuilder.AddToScheme -// ) +// // in package myapigroupv1... +// var ( +// SchemeGroupVersion = schema.GroupVersion{Group: "my.api.group", Version: "v1"} +// SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} +// AddToScheme = SchemeBuilder.AddToScheme +// ) // -// func init() { -// SchemeBuilder.Register(&MyType{}, &MyTypeList) -// } -// var ( -// scheme *runtime.Scheme = runtime.NewScheme() -// ) +// func init() { +// SchemeBuilder.Register(&MyType{}, &MyTypeList) +// } +// var ( +// scheme *runtime.Scheme = runtime.NewScheme() +// ) // // This also true of the built-in Kubernetes types. Then, in the entrypoint for // your manager, assemble the scheme containing exactly the types you need, // panicing if scheme registration failed. For instance, if our controller needs // types from the core/v1 API group (e.g. Pod), plus types from my.api.group/v1: // -// func init() { -// utilruntime.Must(myapigroupv1.AddToScheme(scheme)) -// utilruntime.Must(kubernetesscheme.AddToScheme(scheme)) -// } -// -// func main() { -// mgr := controllers.NewManager(context.Background(), controllers.GetConfigOrDie(), manager.Options{ -// Scheme: scheme, -// }) -// // ... -// } +// func init() { +// utilruntime.Must(myapigroupv1.AddToScheme(scheme)) +// utilruntime.Must(kubernetesscheme.AddToScheme(scheme)) +// } // +// func main() { +// mgr := controllers.NewManager(context.Background(), controllers.GetConfigOrDie(), manager.Options{ +// Scheme: scheme, +// }) +// // ... +// } package scheme import ( diff --git a/vendor/github.com/thoas/go-funk/.gitignore b/vendor/github.com/thoas/go-funk/.gitignore new file mode 100644 index 00000000000..7b4c598b0b9 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/.gitignore @@ -0,0 +1,27 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof + +#GoLand +.idea \ No newline at end of file diff --git a/vendor/github.com/thoas/go-funk/.travis.yml b/vendor/github.com/thoas/go-funk/.travis.yml new file mode 100644 index 00000000000..f8aa08ad61e --- /dev/null +++ b/vendor/github.com/thoas/go-funk/.travis.yml @@ -0,0 +1,7 @@ +language: go +before_install: + - go get golang.org/x/tools/cmd/cover + - go get github.com/stretchr/testify +go: + - "1.16" +script: make test diff --git a/vendor/github.com/thoas/go-funk/CHANGELOG.md b/vendor/github.com/thoas/go-funk/CHANGELOG.md new file mode 100644 index 00000000000..000a371bf42 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/CHANGELOG.md @@ -0,0 +1,29 @@ +go-funk changelog +================= + +0.1 (2017-01-18) +---------------- + +Changes can be seen [here](https://github.com/thoas/go-funk/compare/73b8ae1f6443c9d4acbdc612bbb2ca804bb39b1d...master) + +* Better test suite +* Better documentation +* Add typesafe implementations: + + * ``Contains`` + * ``Sum`` + * ``Reverse`` + * ``IndexOf`` + * ``Uniq`` + * ``Shuffle`` +* Add benchmarks + + * ``Contains`` + * ``Uniq`` + * ``Sum`` +* Fix ``redirectValue`` when using a circular reference +* Add ``Sum`` generic implementation which computes the sum of values in an array +* Add ``Tail`` generic implementation to retrieve all but the first element of array +* Add ``Initial`` generic implementation to retrieve all but the last element of array +* Add ``Last`` generic implementation to retrieve the last element of an array +* Add ``Head`` generic implementation to retrieve the first element of an array diff --git a/vendor/github.com/thoas/go-funk/LICENSE b/vendor/github.com/thoas/go-funk/LICENSE new file mode 100644 index 00000000000..2430978a067 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 Florent Messa + +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/thoas/go-funk/Makefile b/vendor/github.com/thoas/go-funk/Makefile new file mode 100644 index 00000000000..59ae860ebc3 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/Makefile @@ -0,0 +1,11 @@ +build: + go build -v ./... + +test: + go test -v ./... + +lint: + golangci-lint run + +bench: + go test -benchmem -bench . diff --git a/vendor/github.com/thoas/go-funk/README.rst b/vendor/github.com/thoas/go-funk/README.rst new file mode 100644 index 00000000000..d1a9f856581 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/README.rst @@ -0,0 +1,895 @@ +go-funk +======= + +.. image:: https://secure.travis-ci.org/thoas/go-funk.svg?branch=master + :alt: Build Status + :target: http://travis-ci.org/thoas/go-funk + +.. image:: https://godoc.org/github.com/thoas/go-funk?status.svg + :alt: GoDoc + :target: https://pkg.go.dev/github.com/thoas/go-funk + +.. image:: https://goreportcard.com/badge/github.com/thoas/go-funk + :alt: Go report + :target: https://goreportcard.com/report/github.com/thoas/go-funk + +``go-funk`` is a modern Go library based on reflect_. + +Generic helpers rely on reflect_, be careful this code runs exclusively on runtime so you must have a good test suite. + +These helpers have started as an experiment to learn reflect_. It may look like lodash_ in some aspects but +it will have its own roadmap. lodash_ is an awesome library with a lot of work behind it, all features included in +``go-funk`` come from internal use cases. + +You can also find typesafe implementation in the godoc_. + +Why this name? +-------------- + +Long story, short answer because ``func`` is a reserved word in Go, I wanted something similar. + +Initially this project was named ``fn`` I don't need to explain why that was a bad idea for french speakers :) + +Let's ``funk``! + +.. image:: https://media.giphy.com/media/3oEjHQKtDXpeGN9rW0/giphy.gif + +<3 + +Installation +------------ + +.. code-block:: bash + + go get github.com/thoas/go-funk + +Usage +----- + +.. code-block:: go + + import "github.com/thoas/go-funk" + +These examples will be based on the following data model: + +.. code-block:: go + + type Foo struct { + ID int + FirstName string `tag_name:"tag 1"` + LastName string `tag_name:"tag 2"` + Age int `tag_name:"tag 3"` + } + + func (f Foo) TableName() string { + return "foo" + } + +With fixtures: + +.. code-block:: go + + f := &Foo{ + ID: 1, + FirstName: "Foo", + LastName: "Bar", + Age: 30, + } + +You can import ``go-funk`` using a basic statement: + +.. code-block:: go + + import "github.com/thoas/go-funk" + +funk.Contains +............. + +Returns true if an element is present in a iteratee (slice, map, string). + +One frustrating thing in Go is to implement ``contains`` methods for each type, for example: + +.. code-block:: go + + func ContainsInt(s []int, e int) bool { + for _, a := range s { + if a == e { + return true + } + } + return false + } + +this can be replaced by ``funk.Contains``: + +.. code-block:: go + + // slice of string + funk.Contains([]string{"foo", "bar"}, "bar") // true + + // slice of Foo ptr + funk.Contains([]*Foo{f}, f) // true + funk.Contains([]*Foo{f}, func (foo *Foo) bool { + return foo.ID == f.ID + }) // true + funk.Contains([]*Foo{f}, nil) // false + + b := &Foo{ + ID: 2, + FirstName: "Florent", + LastName: "Messa", + Age: 28, + } + + funk.Contains([]*Foo{f}, b) // false + + // string + funk.Contains("florent", "rent") // true + funk.Contains("florent", "foo") // false + + // even map + funk.Contains(map[int]string{1: "Florent"}, 1) // true + funk.Contains(map[int]string{1: "Florent"}, func(key int, name string) bool { + return key == 1 // or `name == "Florent"` for the value type + }) // true + +see also, typesafe implementations: ContainsInt_, ContainsInt64_, ContainsFloat32_, ContainsFloat64_, ContainsString_ + +.. _ContainsFloat32: https://godoc.org/github.com/thoas/go-funk#ContainsFloat32 +.. _ContainsFloat64: https://godoc.org/github.com/thoas/go-funk#ContainsFloat64 +.. _ContainsInt: https://godoc.org/github.com/thoas/go-funk#ContainsInt +.. _ContainsInt64: https://godoc.org/github.com/thoas/go-funk#ContainsInt64 +.. _ContainsString: https://godoc.org/github.com/thoas/go-funk#ContainsString + +funk.Intersect +.............. + +Returns the intersection between two collections. + +.. code-block:: go + + funk.Intersect([]int{1, 2, 3, 4}, []int{2, 4, 6}) // []int{2, 4} + funk.Intersect([]string{"foo", "bar", "hello", "bar"}, []string{"foo", "bar"}) // []string{"foo", "bar"} + +see also, typesafe implementations: IntersectString + +.. IntersectString: https://godoc.org/github.com/thoas/go-funk#IntersectString + + +funk.Difference +.............. + +Returns the difference between two collections. + +.. code-block:: go + + funk.Difference([]int{1, 2, 3, 4}, []int{2, 4, 6}) // []int{1, 3}, []int{6} + funk.Difference([]string{"foo", "bar", "hello", "bar"}, []string{"foo", "bar"}) // []string{"hello"}, []string{} + +see also, typesafe implementations: DifferenceString + +.. DifferenceString: https://godoc.org/github.com/thoas/go-funk#DifferenceString + + +funk.IndexOf +............ + +Gets the index at which the first occurrence of a value is found in an array or return -1 +if the value cannot be found. + +.. code-block:: go + + // slice of string + funk.IndexOf([]string{"foo", "bar"}, "bar") // 1 + funk.IndexOf([]string{"foo", "bar"}, func(value string) bool { + return value == "bar" + }) // 1 + funk.IndexOf([]string{"foo", "bar"}, "gilles") // -1 + +see also, typesafe implementations: IndexOfInt_, IndexOfInt64_, IndexOfFloat32_, IndexOfFloat64_, IndexOfString_ + +.. _IndexOfFloat32: https://godoc.org/github.com/thoas/go-funk#IndexOfFloat32 +.. _IndexOfFloat64: https://godoc.org/github.com/thoas/go-funk#IndexOfFloat64 +.. _IndexOfInt: https://godoc.org/github.com/thoas/go-funk#IndexOfInt +.. _IndexOfInt64: https://godoc.org/github.com/thoas/go-funk#IndexOfInt64 +.. _IndexOfString: https://godoc.org/github.com/thoas/go-funk#IndexOfString + +funk.LastIndexOf +................ + +Gets the index at which the last occurrence of a value is found in an array or return -1 +if the value cannot be found. + +.. code-block:: go + + // slice of string + funk.LastIndexOf([]string{"foo", "bar", "bar"}, "bar") // 2 + funk.LastIndexOf([]string{"foo", "bar"}, func(value string) bool { + return value == "bar" + }) // 2 + funk.LastIndexOf([]string{"foo", "bar"}, "gilles") // -1 + +see also, typesafe implementations: LastIndexOfInt_, LastIndexOfInt64_, LastIndexOfFloat32_, LastIndexOfFloat64_, LastIndexOfString_ + +.. _LastIndexOfFloat32: https://godoc.org/github.com/thoas/go-funk#LastIndexOfFloat32 +.. _LastIndexOfFloat64: https://godoc.org/github.com/thoas/go-funk#LastIndexOfFloat64 +.. _LastIndexOfInt: https://godoc.org/github.com/thoas/go-funk#LastIndexOfInt +.. _LastIndexOfInt64: https://godoc.org/github.com/thoas/go-funk#LastIndexOfInt64 +.. _LastIndexOfString: https://godoc.org/github.com/thoas/go-funk#LastIndexOfString + +funk.ToMap +.......... + +Transforms a slice or an array of structs to a map based on a ``pivot`` field. + +.. code-block:: go + + f := &Foo{ + ID: 1, + FirstName: "Gilles", + LastName: "Fabio", + Age: 70, + } + + b := &Foo{ + ID: 2, + FirstName: "Florent", + LastName: "Messa", + Age: 80, + } + + results := []*Foo{f, b} + + mapping := funk.ToMap(results, "ID") // map[int]*Foo{1: f, 2: b} + +funk.ToSet +.......... + +Transforms an array or a slice to a set (a map with zero-size values). + +.. code-block:: go + + f := Foo{ + ID: 1, + FirstName: "Gilles", + LastName: "Fabio", + Age: 70, + } + + b := Foo{ + ID: 2, + FirstName: "Florent", + LastName: "Messa", + Age: 80, + } + + mapping := funk.ToSet([]Foo{f, b}) // map[Foo]stuct{}{f: struct{}{}, b: struct{}{}} + + mapping := funk.ToSet([4]int{1, 1, 2, 2}) // map[int]struct{}{1: struct{}{}, 2: struct{}{}} + + + +funk.Filter +........... + +Filters a slice based on a predicate. + +.. code-block:: go + + r := funk.Filter([]int{1, 2, 3, 4}, func(x int) bool { + return x%2 == 0 + }) // []int{2, 4} + +see also, typesafe implementations: FilterInt_, FilterInt64_, FilterFloat32_, FilterFloat64_, FilterString_ + +.. _FilterFloat32: https://godoc.org/github.com/thoas/go-funk#FilterFloat32 +.. _FilterFloat64: https://godoc.org/github.com/thoas/go-funk#FilterFloat64 +.. _FilterInt: https://godoc.org/github.com/thoas/go-funk#FilterInt +.. _FilterInt64: https://godoc.org/github.com/thoas/go-funk#FilterInt64 +.. _FilterString: https://godoc.org/github.com/thoas/go-funk#FilterString + +funk.Reduce +........... + +Reduces an iteratee based on an accumulator function or operation rune for numbers. + +.. code-block:: go + + // Using operation runes. '+' and '*' only supported. + r := funk.Reduce([]int{1, 2, 3, 4}, '+', float64(0)) // 10 + r := funk.Reduce([]int{1, 2, 3, 4}, '*', 1) // 24 + + // Using accumulator function + r := funk.Reduce([]int{1, 2, 3, 4}, func(acc float64, num int) float64 { + return acc + float64(num) + }, float64(0)) // 10 + + r := funk.Reduce([]int{1, 2, 3, 4}, func(acc string, num int) string { + return acc + fmt.Sprint(num) + }, "") // "1234" + +funk.Find +......... + +Finds an element in a slice based on a predicate. + +.. code-block:: go + + r := funk.Find([]int{1, 2, 3, 4}, func(x int) bool { + return x%2 == 0 + }) // 2 + +see also, typesafe implementations: FindInt_, FindInt64_, FindFloat32_, FindFloat64_, FindString_ + +.. _FindFloat32: https://godoc.org/github.com/thoas/go-funk#FindFloat32 +.. _FindFloat64: https://godoc.org/github.com/thoas/go-funk#FindFloat64 +.. _FindInt: https://godoc.org/github.com/thoas/go-funk#FindInt +.. _FindInt64: https://godoc.org/github.com/thoas/go-funk#FindInt64 +.. _FindString: https://godoc.org/github.com/thoas/go-funk#FindString + +funk.Map +........ + +Manipulates an iteratee (map, slice) and transforms it to another type: + +* map -> slice +* map -> map +* slice -> map +* slice -> slice + +.. code-block:: go + + r := funk.Map([]int{1, 2, 3, 4}, func(x int) int { + return x * 2 + }) // []int{2, 4, 6, 8} + + r := funk.Map([]int{1, 2, 3, 4}, func(x int) string { + return "Hello" + }) // []string{"Hello", "Hello", "Hello", "Hello"} + + r = funk.Map([]int{1, 2, 3, 4}, func(x int) (int, int) { + return x, x + }) // map[int]int{1: 1, 2: 2, 3: 3, 4: 4} + + mapping := map[int]string{ + 1: "Florent", + 2: "Gilles", + } + + r = funk.Map(mapping, func(k int, v string) int { + return k + }) // []int{1, 2} + + r = funk.Map(mapping, func(k int, v string) (string, string) { + return fmt.Sprintf("%d", k), v + }) // map[string]string{"1": "Florent", "2": "Gilles"} + +funk.FlatMap +............ + +Manipulates an iteratee (map, slice) and transforms it to to a flattened collection of another type: + +* map -> slice +* slice -> slice + +.. code-block:: go + + r := funk.FlatMap([][]int{{1, 2}, {3, 4}}, func(x []int) []int { + return append(x, 0) + }) // []int{1, 2, 0, 3, 4, 0} + + mapping := map[string][]int{ + "Florent": {1, 2}, + "Gilles": {3, 4}, + } + + r = funk.FlatMap(mapping, func(k string, v []int) []int { + return v + }) // []int{1, 2, 3, 4} + +funk.Get +........ + +Retrieves the value at path of struct(s) or map(s). + +.. code-block:: go + + var bar *Bar = &Bar{ + Name: "Test", + Bars: []*Bar{ + &Bar{ + Name: "Level1-1", + Bar: &Bar{ + Name: "Level2-1", + }, + }, + &Bar{ + Name: "Level1-2", + Bar: &Bar{ + Name: "Level2-2", + }, + }, + }, + } + + var foo *Foo = &Foo{ + ID: 1, + FirstName: "Dark", + LastName: "Vador", + Age: 30, + Bar: bar, + Bars: []*Bar{ + bar, + bar, + }, + } + + funk.Get([]*Foo{foo}, "Bar.Bars.Bar.Name") // []string{"Level2-1", "Level2-2"} + funk.Get(foo, "Bar.Bars.Bar.Name") // []string{"Level2-1", "Level2-2"} + funk.Get(foo, "Bar.Name") // Test + +``funk.Get`` also support ``map`` values: + +.. code-block:: go + + bar := map[string]interface{}{ + "Name": "Test", + } + + foo1 := map[string]interface{}{ + "ID": 1, + "FirstName": "Dark", + "LastName": "Vador", + "Age": 30, + "Bar": bar, + } + + foo2 := &map[string]interface{}{ + "ID": 1, + "FirstName": "Dark", + "LastName": "Vador", + "Age": 30, + "Labels": map[string]interface{} { + "example.com/hello": "world", + }, + } // foo2.Bar is nil + + funk.Get(bar, "Name") // "Test" + funk.Get([]map[string]interface{}{foo1, foo2}, "Bar.Name") // []string{"Test"} + funk.Get(foo2, "Bar.Name") // nil + funk.Get(foo2, `Labels."example.com/hello"`) // world + + +``funk.Get`` also handles ``nil`` values: + +.. code-block:: go + + bar := &Bar{ + Name: "Test", + } + + foo1 := &Foo{ + ID: 1, + FirstName: "Dark", + LastName: "Vador", + Age: 30, + Bar: bar, + } + + foo2 := &Foo{ + ID: 1, + FirstName: "Dark", + LastName: "Vador", + Age: 30, + } // foo2.Bar is nil + + funk.Get([]*Foo{foo1, foo2}, "Bar.Name") // []string{"Test"} + funk.Get(foo2, "Bar.Name") // nil + + + +funk.GetOrElse +.............. + +Retrieves the value of the pointer or default. + +.. code-block:: go + + str := "hello world" + GetOrElse(&str, "foobar") // string{"hello world"} + GetOrElse(str, "foobar") // string{"hello world"} + GetOrElse(nil, "foobar") // string{"foobar"} + +funk.Set +........ +Set value at a path of a struct + +.. code-block:: go + + var bar Bar = Bar{ + Name: "level-0", + Bar: &Bar{ + Name: "level-1", + Bars: []*Bar{ + {Name: "level2-1"}, + {Name: "level2-2"}, + }, + }, + } + + _ = Set(&bar, "level-0-new", "Name") + fmt.Println(bar.Name) // "level-0-new" + + MustSet(&bar, "level-1-new", "Bar.Name") + fmt.Println(bar.Bar.Name) // "level-1-new" + + Set(&bar, "level-2-new", "Bar.Bars.Name") + fmt.Println(bar.Bar.Bars[0].Name) // "level-2-new" + fmt.Println(bar.Bar.Bars[1].Name) // "level-2-new" + +funk.MustSet +............ +Short hand for funk.Set if struct does not contain interface{} field type to discard errors. + +funk.Prune +.......... +Copy a struct with only selected fields. Slice is handled by pruning all elements. + +.. code-block:: go + + bar := &Bar{ + Name: "Test", + } + + foo1 := &Foo{ + ID: 1, + FirstName: "Dark", + LastName: "Vador", + Bar: bar, + } + + pruned, _ := Prune(foo1, []string{"FirstName", "Bar.Name"}) + // *Foo{ + // ID: 0, + // FirstName: "Dark", + // LastName: "", + // Bar: &Bar{Name: "Test}, + // } + +funk.PruneByTag +.......... +Same functionality as funk.Prune, but uses struct tags instead of struct field names. + +funk.Keys +......... + +Creates an array of the own enumerable map keys or struct field names. + +.. code-block:: go + + funk.Keys(map[string]int{"one": 1, "two": 2}) // []string{"one", "two"} (iteration order is not guaranteed) + + foo := &Foo{ + ID: 1, + FirstName: "Dark", + LastName: "Vador", + Age: 30, + } + + funk.Keys(foo) // []string{"ID", "FirstName", "LastName", "Age"} (iteration order is not guaranteed) + +funk.Values +........... + +Creates an array of the own enumerable map values or struct field values. + +.. code-block:: go + + funk.Values(map[string]int{"one": 1, "two": 2}) // []int{1, 2} (iteration order is not guaranteed) + + foo := &Foo{ + ID: 1, + FirstName: "Dark", + LastName: "Vador", + Age: 30, + } + + funk.Values(foo) // []interface{}{1, "Dark", "Vador", 30} (iteration order is not guaranteed) + +funk.ForEach +............ + +Range over an iteratee (map, slice). + +Or update element in slice(Not map, reflect#Value#MapIndex#CanSet is false). + +.. code-block:: go + + funk.ForEach([]int{1, 2, 3, 4}, func(x int) { + fmt.Println(x) + }) + + foo := []int{1,2,3} + funk.ForEach(foo, func(x *int){ *x = *x * 2}) + fmt.Println(foo) // []int{2, 4, 6} + +funk.ForEachRight +............ + +Range over an iteratee (map, slice) from the right. + +.. code-block:: go + + results := []int{} + + funk.ForEachRight([]int{1, 2, 3, 4}, func(x int) { + results = append(results, x) + }) + + fmt.Println(results) // []int{4, 3, 2, 1} + +funk.Chunk +.......... + +Creates an array of elements split into groups with the length of the size. +If array can't be split evenly, the final chunk will be the remaining element. + +.. code-block:: go + + funk.Chunk([]int{1, 2, 3, 4, 5}, 2) // [][]int{[]int{1, 2}, []int{3, 4}, []int{5}} + +funk.FlattenDeep +................ + +Recursively flattens an array. + +.. code-block:: go + + funk.FlattenDeep([][]int{[]int{1, 2}, []int{3, 4}}) // []int{1, 2, 3, 4} + +funk.Uniq +......... + +Creates an array with unique values. + +.. code-block:: go + + funk.Uniq([]int{0, 1, 1, 2, 3, 0, 0, 12}) // []int{0, 1, 2, 3, 12} + +see also, typesafe implementations: UniqInt_, UniqInt64_, UniqFloat32_, UniqFloat64_, UniqString_ + +.. _UniqFloat32: https://godoc.org/github.com/thoas/go-funk#UniqFloat32 +.. _UniqFloat64: https://godoc.org/github.com/thoas/go-funk#UniqFloat64 +.. _UniqInt: https://godoc.org/github.com/thoas/go-funk#UniqInt +.. _UniqInt64: https://godoc.org/github.com/thoas/go-funk#UniqInt64 +.. _UniqString: https://godoc.org/github.com/thoas/go-funk#UniqString + +funk.UniqBy +......... + +Creates an array with unique values returned by a callback. + +.. code-block:: go + + funk.UniqBy([]int{0, 1, 1, 2, 3, 0, 0, 12}, func(nbr int) int { + return nbr % 3 + }) // []int{0, 1, 2} + + foo1 := Foo{ + ID: 42, + FirstName: "Bob", + } + foo2 := Foo{ + ID: 42, + FirstName: "Bob", + } + funk.UniqBy([]Foo{foo1, foo2}, func(f Foo) int { + return f.ID + }) // []Foo{ Foo{ID: 42, Firstname: "Bob"} } + +funk.Drop +......... + +Creates an array/slice with `n` elements dropped from the beginning. + +.. code-block:: go + + funk.Drop([]int{0, 0, 0, 0}, 3) // []int{0} + +see also, typesafe implementations: DropInt_, DropInt32_, DropInt64_, DropFloat32_, DropFloat64_, DropString_ + +.. _DropInt: https://godoc.org/github.com/thoas/go-funk#DropInt +.. _DropInt32: https://godoc.org/github.com/thoas/go-funk#DropInt64 +.. _DropInt64: https://godoc.org/github.com/thoas/go-funk#DropInt64 +.. _DropFloat32: https://godoc.org/github.com/thoas/go-funk#DropFloat32 +.. _DropFloat64: https://godoc.org/github.com/thoas/go-funk#DropFloat64 +.. _DropString: https://godoc.org/github.com/thoas/go-funk#DropString + +funk.Initial +............ + +Gets all but the last element of array. + +.. code-block:: go + + funk.Initial([]int{0, 1, 2, 3, 4}) // []int{0, 1, 2, 3} + +funk.Tail +......... + +Gets all but the first element of array. + +.. code-block:: go + + funk.Tail([]int{0, 1, 2, 3, 4}) // []int{1, 2, 3, 4} + +funk.Shuffle +............ + +Creates an array of shuffled values. + +.. code-block:: go + + funk.Shuffle([]int{0, 1, 2, 3, 4}) // []int{2, 1, 3, 4, 0} + + +see also, typesafe implementations: ShuffleInt_, ShuffleInt64_, ShuffleFloat32_, ShuffleFloat64_, ShuffleString_ + +.. _ShuffleFloat32: https://godoc.org/github.com/thoas/go-funk#ShuffleFloat32 +.. _ShuffleFloat64: https://godoc.org/github.com/thoas/go-funk#ShuffleFloat64 +.. _ShuffleInt: https://godoc.org/github.com/thoas/go-funk#ShuffleInt +.. _ShuffleInt64: https://godoc.org/github.com/thoas/go-funk#ShuffleInt64 +.. _ShuffleString: https://godoc.org/github.com/thoas/go-funk#ShuffleString + +funk.Subtract +............. + +Returns the subtraction between two collections. It preserve order. + +.. code-block:: go + + funk.Subtract([]int{0, 1, 2, 3, 4}, []int{0, 4}) // []int{1, 2, 3} + funk.Subtract([]int{0, 3, 2, 3, 4}, []int{0, 4}) // []int{3, 2, 3} + + +see also, typesafe implementations: SubtractString_ + +.. SubtractString: https://godoc.org/github.com/thoas/go-funk#SubtractString + +funk.Sum +........ + +Computes the sum of the values in an array. + +.. code-block:: go + + funk.Sum([]int{0, 1, 2, 3, 4}) // 10.0 + funk.Sum([]interface{}{0.5, 1, 2, 3, 4}) // 10.5 + +see also, typesafe implementations: SumInt_, SumInt64_, SumFloat32_, SumFloat64_ + +.. _SumFloat32: https://godoc.org/github.com/thoas/go-funk#SumFloat32 +.. _SumFloat64: https://godoc.org/github.com/thoas/go-funk#SumFloat64 +.. _SumInt: https://godoc.org/github.com/thoas/go-funk#SumInt +.. _SumInt64: https://godoc.org/github.com/thoas/go-funk#SumInt64 + +funk.Reverse +............ + +Transforms an array such that the first element will become the last, the second element +will become the second to last, etc. + +.. code-block:: go + + funk.Reverse([]int{0, 1, 2, 3, 4}) // []int{4, 3, 2, 1, 0} + +see also, typesafe implementations: ReverseInt_, ReverseInt64_, ReverseFloat32_, ReverseFloat64_, ReverseString_, ReverseStrings_ + +.. _ReverseFloat32: https://godoc.org/github.com/thoas/go-funk#ReverseFloat32 +.. _ReverseFloat64: https://godoc.org/github.com/thoas/go-funk#ReverseFloat64 +.. _ReverseInt: https://godoc.org/github.com/thoas/go-funk#ReverseInt +.. _ReverseInt64: https://godoc.org/github.com/thoas/go-funk#ReverseInt64 +.. _ReverseString: https://godoc.org/github.com/thoas/go-funk#ReverseString +.. _ReverseStrings: https://godoc.org/github.com/thoas/go-funk#ReverseStrings + +funk.SliceOf +............ + +Returns a slice based on an element. + +.. code-block:: go + + funk.SliceOf(f) // will return a []*Foo{f} + +funk.RandomInt +.............. + +Generates a random int, based on a min and max values. + +.. code-block:: go + + funk.RandomInt(0, 100) // will be between 0 and 100 + +funk.RandomString +................. + +Generates a random string with a fixed length. + +.. code-block:: go + + funk.RandomString(4) // will be a string of 4 random characters + +funk.Shard +.......... + +Generates a sharded string with a fixed length and depth. + +.. code-block:: go + + funk.Shard("e89d66bdfdd4dd26b682cc77e23a86eb", 1, 2, false) // []string{"e", "8", "e89d66bdfdd4dd26b682cc77e23a86eb"} + + funk.Shard("e89d66bdfdd4dd26b682cc77e23a86eb", 2, 2, false) // []string{"e8", "9d", "e89d66bdfdd4dd26b682cc77e23a86eb"} + + funk.Shard("e89d66bdfdd4dd26b682cc77e23a86eb", 2, 3, true) // []string{"e8", "9d", "66", "bdfdd4dd26b682cc77e23a86eb"} + +funk.Subset +............. + +Returns true if a collection is a subset of another + +.. code-block:: go + + funk.Subset([]int{1, 2, 4}, []int{1, 2, 3, 4, 5}) // true + funk.Subset([]string{"foo", "bar"},[]string{"foo", "bar", "hello", "bar", "hi"}) //true + + +Performance +----------- + +``go-funk`` currently has an open issue about performance_, don't hesitate to participate in the discussion +to enhance the generic helpers implementations. + +Let's stop beating around the bush, a typesafe implementation in pure Go of ``funk.Contains``, let's say for example: + +.. code-block:: go + + func ContainsInt(s []int, e int) bool { + for _, a := range s { + if a == e { + return true + } + } + return false + } + +will always outperform an implementation based on reflect_ in terms of speed and allocs because of +how it's implemented in the language. + +If you want a similarity, gorm_ will always be slower than sqlx_ (which is very low level btw) and will use more allocs. + +You must not think generic helpers of ``go-funk`` as a replacement when you are dealing with performance in your codebase, +you should use typesafe implementations instead. + +Contributing +------------ + +* Ping me on twitter `@thoas `_ (DMs, mentions, whatever :)) +* Fork the `project `_ +* Fix `open issues `_ or request new features + +Don't hesitate ;) + +Authors +------- + +* Florent Messa +* Gilles Fabio +* Alexey Pokhozhaev +* Alexandre Nicolaie + +.. _reflect: https://golang.org/pkg/reflect/ +.. _lodash: https://lodash.com/ +.. _performance: https://github.com/thoas/go-funk/issues/19 +.. _gorm: https://github.com/jinzhu/gorm +.. _sqlx: https://github.com/jmoiron/sqlx +.. _godoc: https://godoc.org/github.com/thoas/go-funk diff --git a/vendor/github.com/thoas/go-funk/assign.go b/vendor/github.com/thoas/go-funk/assign.go new file mode 100644 index 00000000000..a0da834e81e --- /dev/null +++ b/vendor/github.com/thoas/go-funk/assign.go @@ -0,0 +1,129 @@ +package funk + +import ( + "errors" + "fmt" + "reflect" + "strings" +) + +// Set assigns in at path with value val. i.e. in.path = val +// in accepts types of ptr to struct, ptr to variable, slice and ptr to slice. +// Along the path, interface{} is supported and nil ptr is initialized to ptr to zero value +// of the type until the variable to be set is obtained. +// It returns errors when encountering along the path unknown types, uninitialized +// interface{} or interface{} containing struct directly (not ptr to struct). +// +// Slice is resolved the same way in funk.Get(), by traversing each element of the slice, +// so that each element of the slice's corresponding field are going to be set to the same provided val. +// If Set is called on slice with empty path "", it behaves the same as funk.Fill() +// +// If in is well formed, i.e. do not expect above descripted errors to happen, funk.MustSet() +// is a short hand wrapper to discard error return +func Set(in interface{}, val interface{}, path string) error { + if in == nil { + return errors.New("Cannot Set nil") + } + parts := []string{} + if path != "" { + parts = strings.Split(path, ".") + } + return setByParts(in, val, parts) +} + +// we need this layer to handle interface{} type +func setByParts(in interface{}, val interface{}, parts []string) error { + + if in == nil { + // nil interface can happen during traversing the path + return errors.New("Cannot traverse nil/uninitialized interface{}") + } + + inValue := reflect.ValueOf(in) + inKind := inValue.Type().Kind() + + // Note: if interface contains a struct (not ptr to struct) then the content of the struct cannot be set. + // I.e. it is not CanAddr() or CanSet() + // So we require in interface{} to be a ptr, slice or array + if inKind == reflect.Ptr { + inValue = inValue.Elem() // if it is ptr we set its content not ptr its self + } else if inKind != reflect.Array && inKind != reflect.Slice { + return fmt.Errorf("Type %s not supported by Set", inValue.Type().String()) + } + + return set(inValue, reflect.ValueOf(val), parts) +} + +// traverse inValue using path in parts and set the dst to be setValue +func set(inValue reflect.Value, setValue reflect.Value, parts []string) error { + + // traverse the path to get the inValue we need to set + i := 0 + for i < len(parts) { + + kind := inValue.Kind() + + switch kind { + case reflect.Invalid: + // do not expect this case to happen + return errors.New("nil pointer found along the path") + case reflect.Struct: + fValue := inValue.FieldByName(parts[i]) + if !fValue.IsValid() { + return fmt.Errorf("field name %v is not found in struct %v", parts[i], inValue.Type().String()) + } + if !fValue.CanSet() { + return fmt.Errorf("field name %v is not exported in struct %v", parts[i], inValue.Type().String()) + } + inValue = fValue + i++ + case reflect.Slice, reflect.Array: + // set all its elements + length := inValue.Len() + for j := 0; j < length; j++ { + err := set(inValue.Index(j), setValue, parts[i:]) + if err != nil { + return err + } + } + return nil + case reflect.Ptr: + // only traverse down one level + if inValue.IsNil() { + // we initialize nil ptr to ptr to zero value of the type + // and continue traversing + inValue.Set(reflect.New(inValue.Type().Elem())) + } + // traverse the ptr until it is not pointer any more or is nil again + inValue = redirectValue(inValue) + case reflect.Interface: + // Note: if interface contains a struct (not ptr to struct) then the content of the struct cannot be set. + // I.e. it is not CanAddr() or CanSet(). This is why setByParts has a nil ptr check. + // we treat this as a new call to setByParts, and it will do proper check of the types + return setByParts(inValue.Interface(), setValue.Interface(), parts[i:]) + default: + return fmt.Errorf("kind %v in path %v is not supported", kind, parts[i]) + } + + } + // here inValue holds the value we need to set + + // interface{} can be set to any val + // other types we ensure the type matches + if inValue.Kind() != setValue.Kind() && inValue.Kind() != reflect.Interface { + return fmt.Errorf("cannot set target of type %v with type %v", inValue.Kind(), setValue.Kind()) + } + inValue.Set(setValue) + + return nil +} + +// MustSet is functionally the same as Set. +// It panics instead of returning error. +// It is safe to use if the in value is well formed. +func MustSet(in interface{}, val interface{}, path string) { + err := Set(in, val, path) + if err != nil { + panic(err) + } +} diff --git a/vendor/github.com/thoas/go-funk/builder.go b/vendor/github.com/thoas/go-funk/builder.go new file mode 100644 index 00000000000..6dfc814d732 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/builder.go @@ -0,0 +1,110 @@ +package funk + +import ( + "fmt" + "reflect" +) + +// Builder contains all tools which can be chained. +type Builder interface { + Chunk(size int) Builder + Compact() Builder + Drop(n int) Builder + Filter(predicate interface{}) Builder + Flatten() Builder + FlattenDeep() Builder + Initial() Builder + Intersect(y interface{}) Builder + Join(rarr interface{}, fnc JoinFnc) Builder + Map(mapFunc interface{}) Builder + FlatMap(mapFunc interface{}) Builder + Reverse() Builder + Shuffle() Builder + Tail() Builder + Uniq() Builder + Without(values ...interface{}) Builder + + All() bool + Any() bool + Contains(elem interface{}) bool + Every(elements ...interface{}) bool + Find(predicate interface{}) interface{} + ForEach(predicate interface{}) + ForEachRight(predicate interface{}) + Head() interface{} + Keys() interface{} + IndexOf(elem interface{}) int + IsEmpty() bool + Last() interface{} + LastIndexOf(elem interface{}) int + NotEmpty() bool + Product() float64 + Reduce(reduceFunc, acc interface{}) interface{} + Sum() float64 + Type() reflect.Type + Value() interface{} + Values() interface{} +} + +// Chain creates a simple new go-funk.Builder from a collection. Each method +// call generate a new builder containing the previous result. +func Chain(v interface{}) Builder { + isNotNil(v, "Chain") + + valueType := reflect.TypeOf(v) + if isValidBuilderEntry(valueType) || + (valueType.Kind() == reflect.Ptr && isValidBuilderEntry(valueType.Elem())) { + return &chainBuilder{v} + } + + panic(fmt.Sprintf("Type %s is not supported by Chain", valueType.String())) +} + +// LazyChain creates a lazy go-funk.Builder from a collection. Each method call +// generate a new builder containing a method generating the previous value. +// With that, all data are only generated when we call a tailling method like All or Find. +func LazyChain(v interface{}) Builder { + isNotNil(v, "LazyChain") + + valueType := reflect.TypeOf(v) + if isValidBuilderEntry(valueType) || + (valueType.Kind() == reflect.Ptr && isValidBuilderEntry(valueType.Elem())) { + return &lazyBuilder{func() interface{} { return v }} + } + + panic(fmt.Sprintf("Type %s is not supported by LazyChain", valueType.String())) + +} + +// LazyChainWith creates a lazy go-funk.Builder from a generator. Like LazyChain, each +// method call generate a new builder containing a method generating the previous value. +// But, instead of using a collection, it takes a generator which can generate values. +// With LazyChainWith, to can create a generic pipeline of collection transformation and, +// throw the generator, sending different collection. +func LazyChainWith(generator func() interface{}) Builder { + isNotNil(generator, "LazyChainWith") + return &lazyBuilder{func() interface{} { + isNotNil(generator, "LazyChainWith") + + v := generator() + valueType := reflect.TypeOf(v) + if isValidBuilderEntry(valueType) || + (valueType.Kind() == reflect.Ptr && isValidBuilderEntry(valueType.Elem())) { + return v + } + + panic(fmt.Sprintf("Type %s is not supported by LazyChainWith generator", valueType.String())) + }} +} + +func isNotNil(v interface{}, from string) { + if v == nil { + panic(fmt.Sprintf("nil value is not supported by %s", from)) + } +} + +func isValidBuilderEntry(valueType reflect.Type) bool { + return valueType.Kind() == reflect.Slice || valueType.Kind() == reflect.Array || + valueType.Kind() == reflect.Map || + valueType.Kind() == reflect.String +} diff --git a/vendor/github.com/thoas/go-funk/chain_builder.go b/vendor/github.com/thoas/go-funk/chain_builder.go new file mode 100644 index 00000000000..18226ab573c --- /dev/null +++ b/vendor/github.com/thoas/go-funk/chain_builder.go @@ -0,0 +1,142 @@ +package funk + +import ( + "fmt" + "reflect" +) + +type chainBuilder struct { + collection interface{} +} + +func (b *chainBuilder) Chunk(size int) Builder { + return &chainBuilder{Chunk(b.collection, size)} +} +func (b *chainBuilder) Compact() Builder { + return &chainBuilder{Compact(b.collection)} +} +func (b *chainBuilder) Drop(n int) Builder { + return &chainBuilder{Drop(b.collection, n)} +} +func (b *chainBuilder) Filter(predicate interface{}) Builder { + return &chainBuilder{Filter(b.collection, predicate)} +} +func (b *chainBuilder) Flatten() Builder { + return &chainBuilder{Flatten(b.collection)} +} +func (b *chainBuilder) FlattenDeep() Builder { + return &chainBuilder{FlattenDeep(b.collection)} +} +func (b *chainBuilder) Initial() Builder { + return &chainBuilder{Initial(b.collection)} +} +func (b *chainBuilder) Intersect(y interface{}) Builder { + return &chainBuilder{Intersect(b.collection, y)} +} +func (b *chainBuilder) Join(rarr interface{}, fnc JoinFnc) Builder { + return &chainBuilder{Join(b.collection, rarr, fnc)} +} +func (b *chainBuilder) Map(mapFunc interface{}) Builder { + return &chainBuilder{Map(b.collection, mapFunc)} +} +func (b *chainBuilder) FlatMap(mapFunc interface{}) Builder { + return &chainBuilder{FlatMap(b.collection, mapFunc)} +} +func (b *chainBuilder) Reverse() Builder { + return &chainBuilder{Reverse(b.collection)} +} +func (b *chainBuilder) Shuffle() Builder { + return &chainBuilder{Shuffle(b.collection)} +} +func (b *chainBuilder) Tail() Builder { + return &chainBuilder{Tail(b.collection)} +} +func (b *chainBuilder) Uniq() Builder { + return &chainBuilder{Uniq(b.collection)} +} +func (b *chainBuilder) Without(values ...interface{}) Builder { + return &chainBuilder{Without(b.collection, values...)} +} + +func (b *chainBuilder) All() bool { + v := reflect.ValueOf(b.collection) + t := v.Type() + + if t.Kind() != reflect.Array && t.Kind() != reflect.Slice { + panic(fmt.Sprintf("Type %s is not supported by Chain.All", t.String())) + } + + c := make([]interface{}, v.Len()) + for i := 0; i < v.Len(); i++ { + c[i] = v.Index(i).Interface() + } + return All(c...) +} +func (b *chainBuilder) Any() bool { + v := reflect.ValueOf(b.collection) + t := v.Type() + + if t.Kind() != reflect.Array && t.Kind() != reflect.Slice { + panic(fmt.Sprintf("Type %s is not supported by Chain.Any", t.String())) + } + + c := make([]interface{}, v.Len()) + for i := 0; i < v.Len(); i++ { + c[i] = v.Index(i).Interface() + } + return Any(c...) +} +func (b *chainBuilder) Contains(elem interface{}) bool { + return Contains(b.collection, elem) +} +func (b *chainBuilder) Every(elements ...interface{}) bool { + return Every(b.collection, elements...) +} +func (b *chainBuilder) Find(predicate interface{}) interface{} { + return Find(b.collection, predicate) +} +func (b *chainBuilder) ForEach(predicate interface{}) { + ForEach(b.collection, predicate) +} +func (b *chainBuilder) ForEachRight(predicate interface{}) { + ForEachRight(b.collection, predicate) +} +func (b *chainBuilder) Head() interface{} { + return Head(b.collection) +} +func (b *chainBuilder) Keys() interface{} { + return Keys(b.collection) +} +func (b *chainBuilder) IndexOf(elem interface{}) int { + return IndexOf(b.collection, elem) +} +func (b *chainBuilder) IsEmpty() bool { + return IsEmpty(b.collection) +} +func (b *chainBuilder) Last() interface{} { + return Last(b.collection) +} +func (b *chainBuilder) LastIndexOf(elem interface{}) int { + return LastIndexOf(b.collection, elem) +} +func (b *chainBuilder) NotEmpty() bool { + return NotEmpty(b.collection) +} +func (b *chainBuilder) Product() float64 { + return Product(b.collection) +} +func (b *chainBuilder) Reduce(reduceFunc, acc interface{}) interface{} { + return Reduce(b.collection, reduceFunc, acc) +} +func (b *chainBuilder) Sum() float64 { + return Sum(b.collection) +} +func (b *chainBuilder) Type() reflect.Type { + return reflect.TypeOf(b.collection) +} +func (b *chainBuilder) Value() interface{} { + return b.collection +} +func (b *chainBuilder) Values() interface{} { + return Values(b.collection) +} diff --git a/vendor/github.com/thoas/go-funk/compact.go b/vendor/github.com/thoas/go-funk/compact.go new file mode 100644 index 00000000000..ba151aa9c25 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/compact.go @@ -0,0 +1,50 @@ +package funk + +import ( + "reflect" +) + +// Compact creates a slice with all empty/zero values removed. +func Compact(value interface{}) interface{} { + arr := redirectValue(reflect.ValueOf(value)) + + if arr.Kind() != reflect.Slice && arr.Kind() != reflect.Array { + panic("First parameter must be array or slice") + } + + sliceElemType := sliceElem(arr.Type()) + resultSliceType := reflect.SliceOf(sliceElemType) + result := reflect.MakeSlice(resultSliceType, 0, 0) + + for i := 0; i < arr.Len(); i++ { + elemVal := arr.Index(i) + + if elemVal.Kind() == reflect.Interface { + elemVal = elemVal.Elem() + } + + redirectedElemVal := redirectValue(elemVal) + + switch redirectedElemVal.Kind() { + case reflect.Invalid: + continue + case reflect.Func: + if redirectedElemVal.IsNil() { + continue + } + case reflect.Map, reflect.Slice, reflect.Chan: + if redirectedElemVal.Len() == 0 { + continue + } + default: + defaultValue := reflect.Zero(redirectedElemVal.Type()).Interface() + if redirectedElemVal.Interface() == defaultValue { + continue + } + } + + result = reflect.Append(result, elemVal) + } + + return result.Interface() +} diff --git a/vendor/github.com/thoas/go-funk/fill.go b/vendor/github.com/thoas/go-funk/fill.go new file mode 100644 index 00000000000..6af5cfa77fc --- /dev/null +++ b/vendor/github.com/thoas/go-funk/fill.go @@ -0,0 +1,34 @@ +package funk + +import ( + "errors" + "fmt" + "reflect" +) + +// Fill fills elements of array with value +func Fill(in interface{}, fillValue interface{}) (interface{}, error) { + inValue := reflect.ValueOf(in) + inKind := inValue.Type().Kind() + if inKind != reflect.Slice && inKind != reflect.Array { + return nil, errors.New("Can only fill slices and arrays") + } + + inType := reflect.TypeOf(in).Elem() + value := reflect.ValueOf(fillValue) + if inType != value.Type() { + return nil, fmt.Errorf( + "Cannot fill '%s' with '%s'", reflect.TypeOf(in), value.Type(), + ) + } + + length := inValue.Len() + newSlice := reflect.SliceOf(reflect.TypeOf(fillValue)) + in = reflect.MakeSlice(newSlice, length, length).Interface() + inValue = reflect.ValueOf(in) + + for i := 0; i < length; i++ { + inValue.Index(i).Set(value) + } + return in, nil +} diff --git a/vendor/github.com/thoas/go-funk/helpers.go b/vendor/github.com/thoas/go-funk/helpers.go new file mode 100644 index 00000000000..da3f032a5d4 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/helpers.go @@ -0,0 +1,317 @@ +package funk + +import ( + "bytes" + "math/rand" + "reflect" +) + +var numericZeros = []interface{}{ + int(0), + int8(0), + int16(0), + int32(0), + int64(0), + uint(0), + uint8(0), + uint16(0), + uint32(0), + uint64(0), + float32(0), + float64(0), +} + +// ToFloat64 converts any numeric value to float64. +func ToFloat64(x interface{}) (float64, bool) { + var xf float64 + xok := true + + switch xn := x.(type) { + case uint8: + xf = float64(xn) + case uint16: + xf = float64(xn) + case uint32: + xf = float64(xn) + case uint64: + xf = float64(xn) + case int: + xf = float64(xn) + case int8: + xf = float64(xn) + case int16: + xf = float64(xn) + case int32: + xf = float64(xn) + case int64: + xf = float64(xn) + case float32: + xf = float64(xn) + case float64: + xf = float64(xn) + default: + xok = false + } + + return xf, xok +} + +// PtrOf makes a copy of the given interface and returns a pointer. +func PtrOf(itf interface{}) interface{} { + t := reflect.TypeOf(itf) + + cp := reflect.New(t) + cp.Elem().Set(reflect.ValueOf(itf)) + + // Avoid double pointers if itf is a pointer + if t.Kind() == reflect.Ptr { + return cp.Elem().Interface() + } + + return cp.Interface() +} + +// IsFunction returns if the argument is a function. +func IsFunction(in interface{}, num ...int) bool { + funcType := reflect.TypeOf(in) + + result := funcType != nil && funcType.Kind() == reflect.Func + + if len(num) >= 1 { + result = result && funcType.NumIn() == num[0] + } + + if len(num) == 2 { + result = result && funcType.NumOut() == num[1] + } + + return result +} + +// IsPredicate returns if the argument is a predicate function. +func IsPredicate(in interface{}, inTypes ...reflect.Type) bool { + if len(inTypes) == 0 { + inTypes = append(inTypes, nil) + } + + funcType := reflect.TypeOf(in) + + result := funcType != nil && funcType.Kind() == reflect.Func + + result = result && funcType.NumOut() == 1 && funcType.Out(0).Kind() == reflect.Bool + result = result && funcType.NumIn() == len(inTypes) + + for i := 0; result && i < len(inTypes); i++ { + inType := inTypes[i] + result = inType == nil || inType.ConvertibleTo(funcType.In(i)) + } + + return result +} + +// IsEqual returns if the two objects are equal +func IsEqual(expected interface{}, actual interface{}) bool { + if expected == nil || actual == nil { + return expected == actual + } + + if exp, ok := expected.([]byte); ok { + act, ok := actual.([]byte) + if !ok { + return false + } + + if exp == nil || act == nil { + return true + } + + return bytes.Equal(exp, act) + } + + return reflect.DeepEqual(expected, actual) + +} + +// IsType returns if the two objects are in the same type +func IsType(expected interface{}, actual interface{}) bool { + return IsEqual(reflect.TypeOf(expected), reflect.TypeOf(actual)) +} + +// Equal returns if the two objects are equal +func Equal(expected interface{}, actual interface{}) bool { + return IsEqual(expected, actual) +} + +// NotEqual returns if the two objects are not equal +func NotEqual(expected interface{}, actual interface{}) bool { + return !IsEqual(expected, actual) +} + +// IsIteratee returns if the argument is an iteratee. +func IsIteratee(in interface{}) bool { + if in == nil { + return false + } + arrType := reflect.TypeOf(in) + + kind := arrType.Kind() + + return kind == reflect.Array || kind == reflect.Slice || kind == reflect.Map +} + +// IsCollection returns if the argument is a collection. +func IsCollection(in interface{}) bool { + arrType := reflect.TypeOf(in) + + kind := arrType.Kind() + + return kind == reflect.Array || kind == reflect.Slice +} + +// SliceOf returns a slice which contains the element. +func SliceOf(in interface{}) interface{} { + value := reflect.ValueOf(in) + + sliceType := reflect.SliceOf(reflect.TypeOf(in)) + slice := reflect.New(sliceType) + sliceValue := reflect.MakeSlice(sliceType, 0, 0) + sliceValue = reflect.Append(sliceValue, value) + slice.Elem().Set(sliceValue) + + return slice.Elem().Interface() +} + +// Any returns true if any element of the iterable is not empty. If the iterable is empty, return False. +func Any(objs ...interface{}) bool { + if len(objs) == 0 { + return false + } + + for _, obj := range objs { + if !IsEmpty(obj) { + return true + } + } + + return false +} + +// All returns true if all elements of the iterable are not empty (or if the iterable is empty) +func All(objs ...interface{}) bool { + if len(objs) == 0 { + return true + } + + for _, obj := range objs { + if IsEmpty(obj) { + return false + } + } + + return true +} + +// IsEmpty returns if the object is considered as empty or not. +func IsEmpty(obj interface{}) bool { + if obj == nil || obj == "" || obj == false { + return true + } + + for _, v := range numericZeros { + if obj == v { + return true + } + } + + objValue := reflect.ValueOf(obj) + + switch objValue.Kind() { + case reflect.Map: + fallthrough + case reflect.Slice, reflect.Chan: + return objValue.Len() == 0 + case reflect.Struct: + return reflect.DeepEqual(obj, ZeroOf(obj)) + case reflect.Ptr: + if objValue.IsNil() { + return true + } + + obj = redirectValue(objValue).Interface() + + return reflect.DeepEqual(obj, ZeroOf(obj)) + } + + return false +} + +// IsZero returns if the object is considered as zero value +func IsZero(obj interface{}) bool { + if obj == nil || obj == "" || obj == false { + return true + } + + for _, v := range numericZeros { + if obj == v { + return true + } + } + + return reflect.DeepEqual(obj, ZeroOf(obj)) +} + +// NotEmpty returns if the object is considered as non-empty or not. +func NotEmpty(obj interface{}) bool { + return !IsEmpty(obj) +} + +// ZeroOf returns a zero value of an element. +func ZeroOf(in interface{}) interface{} { + if in == nil { + return nil + } + + return reflect.Zero(reflect.TypeOf(in)).Interface() +} + +// RandomInt generates a random int, based on a min and max values +func RandomInt(min, max int) int { + return min + rand.Intn(max-min) +} + +// Shard will shard a string name +func Shard(str string, width int, depth int, restOnly bool) []string { + var results []string + + for i := 0; i < depth; i++ { + results = append(results, str[(width*i):(width*(i+1))]) + } + + if restOnly { + results = append(results, str[(width*depth):]) + } else { + results = append(results, str) + } + + return results +} + +var defaultLetters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") + +// RandomString returns a random string with a fixed length +func RandomString(n int, allowedChars ...[]rune) string { + var letters []rune + + if len(allowedChars) == 0 { + letters = defaultLetters + } else { + letters = allowedChars[0] + } + + b := make([]rune, n) + for i := range b { + b[i] = letters[rand.Intn(len(letters))] + } + + return string(b) +} diff --git a/vendor/github.com/thoas/go-funk/intersection.go b/vendor/github.com/thoas/go-funk/intersection.go new file mode 100644 index 00000000000..6670ad2b697 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/intersection.go @@ -0,0 +1,282 @@ +package funk + +import ( + "reflect" +) + +// Intersect returns the intersection between two collections. +// +// Deprecated: use Join(x, y, InnerJoin) instead of Intersect, InnerJoin +// implements deduplication mechanism, so verify your code behaviour +// before using it +func Intersect(x interface{}, y interface{}) interface{} { + if !IsCollection(x) { + panic("First parameter must be a collection") + } + if !IsCollection(y) { + panic("Second parameter must be a collection") + } + + hash := map[interface{}]struct{}{} + + xValue := reflect.ValueOf(x) + xType := xValue.Type() + + yValue := reflect.ValueOf(y) + yType := yValue.Type() + + if NotEqual(xType, yType) { + panic("Parameters must have the same type") + } + + zType := reflect.SliceOf(xType.Elem()) + zSlice := reflect.MakeSlice(zType, 0, 0) + + for i := 0; i < xValue.Len(); i++ { + v := xValue.Index(i).Interface() + hash[v] = struct{}{} + } + + for i := 0; i < yValue.Len(); i++ { + v := yValue.Index(i).Interface() + _, ok := hash[v] + if ok { + zSlice = reflect.Append(zSlice, yValue.Index(i)) + } + } + + return zSlice.Interface() +} + +// IntersectString returns the intersection between two collections of string. +func IntersectString(x []string, y []string) []string { + if len(x) == 0 || len(y) == 0 { + return []string{} + } + + set := []string{} + hash := map[string]struct{}{} + + for _, v := range x { + hash[v] = struct{}{} + } + + for _, v := range y { + _, ok := hash[v] + if ok { + set = append(set, v) + } + } + + return set +} + +// Difference returns the difference between two collections. +func Difference(x interface{}, y interface{}) (interface{}, interface{}) { + if !IsIteratee(x) { + panic("First parameter must be an iteratee") + } + if !IsIteratee(y) { + panic("Second parameter must be an iteratee") + } + + xValue := reflect.ValueOf(x) + xType := xValue.Type() + + yValue := reflect.ValueOf(y) + yType := yValue.Type() + + if NotEqual(xType, yType) { + panic("Parameters must have the same type") + } + + if xType.Kind() == reflect.Map { + leftType := reflect.MapOf(xType.Key(), xType.Elem()) + rightType := reflect.MapOf(xType.Key(), xType.Elem()) + leftMap := reflect.MakeMap(leftType) + rightMap := reflect.MakeMap(rightType) + + xIter := xValue.MapRange() + for xIter.Next() { + k := xIter.Key() + xv := xIter.Value() + yv := yValue.MapIndex(k) + equalTo := equal(xv.Interface(), true) + if !yv.IsValid() || !equalTo(yv, yv) { + leftMap.SetMapIndex(k, xv) + } + } + + yIter := yValue.MapRange() + for yIter.Next() { + k := yIter.Key() + yv := yIter.Value() + xv := xValue.MapIndex(k) + equalTo := equal(yv.Interface(), true) + if !xv.IsValid() || !equalTo(xv, xv) { + rightMap.SetMapIndex(k, yv) + } + } + return leftMap.Interface(), rightMap.Interface() + } else { + leftType := reflect.SliceOf(xType.Elem()) + rightType := reflect.SliceOf(yType.Elem()) + leftSlice := reflect.MakeSlice(leftType, 0, 0) + rightSlice := reflect.MakeSlice(rightType, 0, 0) + + for i := 0; i < xValue.Len(); i++ { + v := xValue.Index(i).Interface() + if !Contains(y, v) { + leftSlice = reflect.Append(leftSlice, xValue.Index(i)) + } + } + + for i := 0; i < yValue.Len(); i++ { + v := yValue.Index(i).Interface() + if !Contains(x, v) { + rightSlice = reflect.Append(rightSlice, yValue.Index(i)) + } + } + return leftSlice.Interface(), rightSlice.Interface() + } +} + +// DifferenceString returns the difference between two collections of strings. +func DifferenceString(x []string, y []string) ([]string, []string) { + leftSlice := []string{} + rightSlice := []string{} + + for _, v := range x { + if !ContainsString(y, v) { + leftSlice = append(leftSlice, v) + } + } + + for _, v := range y { + if !ContainsString(x, v) { + rightSlice = append(rightSlice, v) + } + } + + return leftSlice, rightSlice +} + +// DifferenceInt64 returns the difference between two collections of int64s. +func DifferenceInt64(x []int64, y []int64) ([]int64, []int64) { + leftSlice := []int64{} + rightSlice := []int64{} + + for _, v := range x { + if !ContainsInt64(y, v) { + leftSlice = append(leftSlice, v) + } + } + + for _, v := range y { + if !ContainsInt64(x, v) { + rightSlice = append(rightSlice, v) + } + } + + return leftSlice, rightSlice +} + +// DifferenceInt32 returns the difference between two collections of ints32. +func DifferenceInt32(x []int32, y []int32) ([]int32, []int32) { + leftSlice := []int32{} + rightSlice := []int32{} + + for _, v := range x { + if !ContainsInt32(y, v) { + leftSlice = append(leftSlice, v) + } + } + + for _, v := range y { + if !ContainsInt32(x, v) { + rightSlice = append(rightSlice, v) + } + } + + return leftSlice, rightSlice +} + +// DifferenceInt returns the difference between two collections of ints. +func DifferenceInt(x []int, y []int) ([]int, []int) { + leftSlice := []int{} + rightSlice := []int{} + + for _, v := range x { + if !ContainsInt(y, v) { + leftSlice = append(leftSlice, v) + } + } + + for _, v := range y { + if !ContainsInt(x, v) { + rightSlice = append(rightSlice, v) + } + } + + return leftSlice, rightSlice +} + +// DifferenceUInt returns the difference between two collections of uints. +func DifferenceUInt(x []uint, y []uint) ([]uint, []uint) { + leftSlice := []uint{} + rightSlice := []uint{} + + for _, v := range x { + if !ContainsUInt(y, v) { + leftSlice = append(leftSlice, v) + } + } + + for _, v := range y { + if !ContainsUInt(x, v) { + rightSlice = append(rightSlice, v) + } + } + + return leftSlice, rightSlice +} + +// DifferenceUInt32 returns the difference between two collections of uints32. +func DifferenceUInt32(x []uint32, y []uint32) ([]uint32, []uint32) { + leftSlice := []uint32{} + rightSlice := []uint32{} + + for _, v := range x { + if !ContainsUInt32(y, v) { + leftSlice = append(leftSlice, v) + } + } + + for _, v := range y { + if !ContainsUInt32(x, v) { + rightSlice = append(rightSlice, v) + } + } + + return leftSlice, rightSlice +} + +// DifferenceUInt64 returns the difference between two collections of uints64. +func DifferenceUInt64(x []uint64, y []uint64) ([]uint64, []uint64) { + leftSlice := []uint64{} + rightSlice := []uint64{} + + for _, v := range x { + if !ContainsUInt64(y, v) { + leftSlice = append(leftSlice, v) + } + } + + for _, v := range y { + if !ContainsUInt64(x, v) { + rightSlice = append(rightSlice, v) + } + } + + return leftSlice, rightSlice +} diff --git a/vendor/github.com/thoas/go-funk/join.go b/vendor/github.com/thoas/go-funk/join.go new file mode 100644 index 00000000000..f5de606571c --- /dev/null +++ b/vendor/github.com/thoas/go-funk/join.go @@ -0,0 +1,111 @@ +package funk + +import ( + "reflect" + "strings" +) + +type JoinFnc func(lx, rx reflect.Value) reflect.Value + +// Join combines two collections using the given join method. +func Join(larr, rarr interface{}, fnc JoinFnc) interface{} { + if !IsCollection(larr) { + panic("First parameter must be a collection") + } + if !IsCollection(rarr) { + panic("Second parameter must be a collection") + } + + lvalue := reflect.ValueOf(larr) + rvalue := reflect.ValueOf(rarr) + if NotEqual(lvalue.Type(), rvalue.Type()) { + panic("Parameters must have the same type") + } + + return fnc(lvalue, rvalue).Interface() +} + +// InnerJoin finds and returns matching data from two collections. +func InnerJoin(lx, rx reflect.Value) reflect.Value { + result := reflect.MakeSlice(reflect.SliceOf(lx.Type().Elem()), 0, lx.Len()+rx.Len()) + rhash := hashSlice(rx) + lhash := make(map[interface{}]struct{}, lx.Len()) + + for i := 0; i < lx.Len(); i++ { + v := lx.Index(i) + _, ok := rhash[v.Interface()] + _, alreadyExists := lhash[v.Interface()] + if ok && !alreadyExists { + lhash[v.Interface()] = struct{}{} + result = reflect.Append(result, v) + } + } + return result +} + +// OuterJoin finds and returns dissimilar data from two collections. +func OuterJoin(lx, rx reflect.Value) reflect.Value { + ljoin := LeftJoin(lx, rx) + rjoin := RightJoin(lx, rx) + + result := reflect.MakeSlice(reflect.SliceOf(lx.Type().Elem()), ljoin.Len()+rjoin.Len(), ljoin.Len()+rjoin.Len()) + for i := 0; i < ljoin.Len(); i++ { + result.Index(i).Set(ljoin.Index(i)) + } + for i := 0; i < rjoin.Len(); i++ { + result.Index(ljoin.Len() + i).Set(rjoin.Index(i)) + } + + return result +} + +// LeftJoin finds and returns dissimilar data from the first collection (left). +func LeftJoin(lx, rx reflect.Value) reflect.Value { + result := reflect.MakeSlice(reflect.SliceOf(lx.Type().Elem()), 0, lx.Len()) + rhash := hashSlice(rx) + + for i := 0; i < lx.Len(); i++ { + v := lx.Index(i) + _, ok := rhash[v.Interface()] + if !ok { + result = reflect.Append(result, v) + } + } + return result +} + +// LeftJoin finds and returns dissimilar data from the second collection (right). +func RightJoin(lx, rx reflect.Value) reflect.Value { return LeftJoin(rx, lx) } + +func hashSlice(arr reflect.Value) map[interface{}]struct{} { + hash := map[interface{}]struct{}{} + for i := 0; i < arr.Len(); i++ { + v := arr.Index(i).Interface() + hash[v] = struct{}{} + } + return hash +} + +// StringerJoin joins an array of elements which implement the `String() string` function. +// Direct copy of strings.Join() with a few tweaks. +func StringerJoin(elems []interface{ String() string }, sep string) string { + switch len(elems) { + case 0: + return "" + case 1: + return elems[0].String() + } + n := len(sep) * (len(elems) - 1) + for i := 0; i < len(elems); i++ { + n += len(elems[i].String()) + } + + var b strings.Builder + b.Grow(n) + b.WriteString(elems[0].String()) + for _, s := range elems[1:] { + b.WriteString(sep) + b.WriteString(s.String()) + } + return b.String() +} diff --git a/vendor/github.com/thoas/go-funk/join_primitives.go b/vendor/github.com/thoas/go-funk/join_primitives.go new file mode 100644 index 00000000000..eefcac12d58 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/join_primitives.go @@ -0,0 +1,373 @@ +package funk + +type JoinIntFnc func(lx, rx []int) []int + +// JoinInt combines two int collections using the given join method. +func JoinInt(larr, rarr []int, fnc JoinIntFnc) []int { + return fnc(larr, rarr) +} + +// InnerJoinInt finds and returns matching data from two int collections. +func InnerJoinInt(lx, rx []int) []int { + result := make([]int, 0, len(lx)+len(rx)) + rhash := hashSliceInt(rx) + lhash := make(map[int]struct{}, len(lx)) + + for _, v := range lx { + _, ok := rhash[v] + _, alreadyExists := lhash[v] + if ok && !alreadyExists { + lhash[v] = struct{}{} + result = append(result, v) + } + } + return result +} + +// OuterJoinInt finds and returns dissimilar data from two int collections. +func OuterJoinInt(lx, rx []int) []int { + ljoin := LeftJoinInt(lx, rx) + rjoin := RightJoinInt(lx, rx) + + result := make([]int, len(ljoin)+len(rjoin)) + copy(result, ljoin) + for i, v := range rjoin { + result[len(ljoin)+i] = v + } + return result +} + +// LeftJoinInt finds and returns dissimilar data from the first int collection (left). +func LeftJoinInt(lx, rx []int) []int { + result := make([]int, 0, len(lx)) + rhash := hashSliceInt(rx) + + for _, v := range lx { + _, ok := rhash[v] + if !ok { + result = append(result, v) + } + } + return result +} + +// LeftJoinInt finds and returns dissimilar data from the second int collection (right). +func RightJoinInt(lx, rx []int) []int { return LeftJoinInt(rx, lx) } + +func hashSliceInt(arr []int) map[int]struct{} { + hash := make(map[int]struct{}, len(arr)) + for _, i := range arr { + hash[i] = struct{}{} + } + return hash +} + +type JoinInt32Fnc func(lx, rx []int32) []int32 + +// JoinInt32 combines two int32 collections using the given join method. +func JoinInt32(larr, rarr []int32, fnc JoinInt32Fnc) []int32 { + return fnc(larr, rarr) +} + +// InnerJoinInt32 finds and returns matching data from two int32 collections. +func InnerJoinInt32(lx, rx []int32) []int32 { + result := make([]int32, 0, len(lx)+len(rx)) + rhash := hashSliceInt32(rx) + lhash := make(map[int32]struct{}, len(lx)) + + for _, v := range lx { + _, ok := rhash[v] + _, alreadyExists := lhash[v] + if ok && !alreadyExists { + lhash[v] = struct{}{} + result = append(result, v) + } + } + return result +} + +// OuterJoinInt32 finds and returns dissimilar data from two int32 collections. +func OuterJoinInt32(lx, rx []int32) []int32 { + ljoin := LeftJoinInt32(lx, rx) + rjoin := RightJoinInt32(lx, rx) + + result := make([]int32, len(ljoin)+len(rjoin)) + copy(result, ljoin) + for i, v := range rjoin { + result[len(ljoin)+i] = v + } + return result +} + +// LeftJoinInt32 finds and returns dissimilar data from the first int32 collection (left). +func LeftJoinInt32(lx, rx []int32) []int32 { + result := make([]int32, 0, len(lx)) + rhash := hashSliceInt32(rx) + + for _, v := range lx { + _, ok := rhash[v] + if !ok { + result = append(result, v) + } + } + return result +} + +// LeftJoinInt32 finds and returns dissimilar data from the second int32 collection (right). +func RightJoinInt32(lx, rx []int32) []int32 { return LeftJoinInt32(rx, lx) } + +func hashSliceInt32(arr []int32) map[int32]struct{} { + hash := make(map[int32]struct{}, len(arr)) + for _, i := range arr { + hash[i] = struct{}{} + } + return hash +} + +type JoinInt64Fnc func(lx, rx []int64) []int64 + +// JoinInt64 combines two int64 collections using the given join method. +func JoinInt64(larr, rarr []int64, fnc JoinInt64Fnc) []int64 { + return fnc(larr, rarr) +} + +// InnerJoinInt64 finds and returns matching data from two int64 collections. +func InnerJoinInt64(lx, rx []int64) []int64 { + result := make([]int64, 0, len(lx)+len(rx)) + rhash := hashSliceInt64(rx) + lhash := make(map[int64]struct{}, len(lx)) + + for _, v := range lx { + _, ok := rhash[v] + _, alreadyExists := lhash[v] + if ok && !alreadyExists { + lhash[v] = struct{}{} + result = append(result, v) + } + } + return result +} + +// OuterJoinInt64 finds and returns dissimilar data from two int64 collections. +func OuterJoinInt64(lx, rx []int64) []int64 { + ljoin := LeftJoinInt64(lx, rx) + rjoin := RightJoinInt64(lx, rx) + + result := make([]int64, len(ljoin)+len(rjoin)) + copy(result, ljoin) + for i, v := range rjoin { + result[len(ljoin)+i] = v + } + return result +} + +// LeftJoinInt64 finds and returns dissimilar data from the first int64 collection (left). +func LeftJoinInt64(lx, rx []int64) []int64 { + result := make([]int64, 0, len(lx)) + rhash := hashSliceInt64(rx) + + for _, v := range lx { + _, ok := rhash[v] + if !ok { + result = append(result, v) + } + } + return result +} + +// LeftJoinInt64 finds and returns dissimilar data from the second int64 collection (right). +func RightJoinInt64(lx, rx []int64) []int64 { return LeftJoinInt64(rx, lx) } + +func hashSliceInt64(arr []int64) map[int64]struct{} { + hash := make(map[int64]struct{}, len(arr)) + for _, i := range arr { + hash[i] = struct{}{} + } + return hash +} + +type JoinStringFnc func(lx, rx []string) []string + +// JoinString combines two string collections using the given join method. +func JoinString(larr, rarr []string, fnc JoinStringFnc) []string { + return fnc(larr, rarr) +} + +// InnerJoinString finds and returns matching data from two string collections. +func InnerJoinString(lx, rx []string) []string { + result := make([]string, 0, len(lx)+len(rx)) + rhash := hashSliceString(rx) + lhash := make(map[string]struct{}, len(lx)) + + for _, v := range lx { + _, ok := rhash[v] + _, alreadyExists := lhash[v] + if ok && !alreadyExists { + lhash[v] = struct{}{} + result = append(result, v) + } + } + return result +} + +// OuterJoinString finds and returns dissimilar data from two string collections. +func OuterJoinString(lx, rx []string) []string { + ljoin := LeftJoinString(lx, rx) + rjoin := RightJoinString(lx, rx) + + result := make([]string, len(ljoin)+len(rjoin)) + copy(result, ljoin) + for i, v := range rjoin { + result[len(ljoin)+i] = v + } + return result +} + +// LeftJoinString finds and returns dissimilar data from the first string collection (left). +func LeftJoinString(lx, rx []string) []string { + result := make([]string, 0, len(lx)) + rhash := hashSliceString(rx) + + for _, v := range lx { + _, ok := rhash[v] + if !ok { + result = append(result, v) + } + } + return result +} + +// LeftJoinString finds and returns dissimilar data from the second string collection (right). +func RightJoinString(lx, rx []string) []string { return LeftJoinString(rx, lx) } + +func hashSliceString(arr []string) map[string]struct{} { + hash := make(map[string]struct{}, len(arr)) + for _, i := range arr { + hash[i] = struct{}{} + } + return hash +} + +type JoinFloat32Fnc func(lx, rx []float32) []float32 + +// JoinFloat32 combines two float32 collections using the given join method. +func JoinFloat32(larr, rarr []float32, fnc JoinFloat32Fnc) []float32 { + return fnc(larr, rarr) +} + +// InnerJoinFloat32 finds and returns matching data from two float32 collections. +func InnerJoinFloat32(lx, rx []float32) []float32 { + result := make([]float32, 0, len(lx)+len(rx)) + rhash := hashSliceFloat32(rx) + lhash := make(map[float32]struct{}, len(lx)) + + for _, v := range lx { + _, ok := rhash[v] + _, alreadyExists := lhash[v] + if ok && !alreadyExists { + lhash[v] = struct{}{} + result = append(result, v) + } + } + return result +} + +// OuterJoinFloat32 finds and returns dissimilar data from two float32 collections. +func OuterJoinFloat32(lx, rx []float32) []float32 { + ljoin := LeftJoinFloat32(lx, rx) + rjoin := RightJoinFloat32(lx, rx) + + result := make([]float32, len(ljoin)+len(rjoin)) + copy(result, ljoin) + for i, v := range rjoin { + result[len(ljoin)+i] = v + } + return result +} + +// LeftJoinFloat32 finds and returns dissimilar data from the first float32 collection (left). +func LeftJoinFloat32(lx, rx []float32) []float32 { + result := make([]float32, 0, len(lx)) + rhash := hashSliceFloat32(rx) + + for _, v := range lx { + _, ok := rhash[v] + if !ok { + result = append(result, v) + } + } + return result +} + +// LeftJoinFloat32 finds and returns dissimilar data from the second float32 collection (right). +func RightJoinFloat32(lx, rx []float32) []float32 { return LeftJoinFloat32(rx, lx) } + +func hashSliceFloat32(arr []float32) map[float32]struct{} { + hash := make(map[float32]struct{}, len(arr)) + for _, i := range arr { + hash[i] = struct{}{} + } + return hash +} + +type JoinFloat64Fnc func(lx, rx []float64) []float64 + +// JoinFloat64 combines two float64 collections using the given join method. +func JoinFloat64(larr, rarr []float64, fnc JoinFloat64Fnc) []float64 { + return fnc(larr, rarr) +} + +// InnerJoinFloat64 finds and returns matching data from two float64 collections. +func InnerJoinFloat64(lx, rx []float64) []float64 { + result := make([]float64, 0, len(lx)+len(rx)) + rhash := hashSliceFloat64(rx) + lhash := make(map[float64]struct{}, len(lx)) + + for _, v := range lx { + _, ok := rhash[v] + _, alreadyExists := lhash[v] + if ok && !alreadyExists { + lhash[v] = struct{}{} + result = append(result, v) + } + } + return result +} + +// OuterJoinFloat64 finds and returns dissimilar data from two float64 collections. +func OuterJoinFloat64(lx, rx []float64) []float64 { + ljoin := LeftJoinFloat64(lx, rx) + rjoin := RightJoinFloat64(lx, rx) + + result := make([]float64, len(ljoin)+len(rjoin)) + copy(result, ljoin) + for i, v := range rjoin { + result[len(ljoin)+i] = v + } + return result +} + +// LeftJoinFloat64 finds and returns dissimilar data from the first float64 collection (left). +func LeftJoinFloat64(lx, rx []float64) []float64 { + result := make([]float64, 0, len(lx)) + rhash := hashSliceFloat64(rx) + + for _, v := range lx { + _, ok := rhash[v] + if !ok { + result = append(result, v) + } + } + return result +} + +// LeftJoinFloat64 finds and returns dissimilar data from the second float64 collection (right). +func RightJoinFloat64(lx, rx []float64) []float64 { return LeftJoinFloat64(rx, lx) } + +func hashSliceFloat64(arr []float64) map[float64]struct{} { + hash := make(map[float64]struct{}, len(arr)) + for _, i := range arr { + hash[i] = struct{}{} + } + return hash +} diff --git a/vendor/github.com/thoas/go-funk/lazy_builder.go b/vendor/github.com/thoas/go-funk/lazy_builder.go new file mode 100644 index 00000000000..9ba1907ea10 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/lazy_builder.go @@ -0,0 +1,117 @@ +package funk + +import "reflect" + +type lazyBuilder struct { + exec func() interface{} +} + +func (b *lazyBuilder) Chunk(size int) Builder { + return &lazyBuilder{func() interface{} { return Chunk(b.exec(), size) }} +} +func (b *lazyBuilder) Compact() Builder { + return &lazyBuilder{func() interface{} { return Compact(b.exec()) }} +} +func (b *lazyBuilder) Drop(n int) Builder { + return &lazyBuilder{func() interface{} { return Drop(b.exec(), n) }} +} +func (b *lazyBuilder) Filter(predicate interface{}) Builder { + return &lazyBuilder{func() interface{} { return Filter(b.exec(), predicate) }} +} +func (b *lazyBuilder) Flatten() Builder { + return &lazyBuilder{func() interface{} { return Flatten(b.exec()) }} +} +func (b *lazyBuilder) FlattenDeep() Builder { + return &lazyBuilder{func() interface{} { return FlattenDeep(b.exec()) }} +} +func (b *lazyBuilder) Initial() Builder { + return &lazyBuilder{func() interface{} { return Initial(b.exec()) }} +} +func (b *lazyBuilder) Intersect(y interface{}) Builder { + return &lazyBuilder{func() interface{} { return Intersect(b.exec(), y) }} +} +func (b *lazyBuilder) Join(rarr interface{}, fnc JoinFnc) Builder { + return &lazyBuilder{func() interface{} { return Join(b.exec(), rarr, fnc) }} +} +func (b *lazyBuilder) Map(mapFunc interface{}) Builder { + return &lazyBuilder{func() interface{} { return Map(b.exec(), mapFunc) }} +} +func (b *lazyBuilder) FlatMap(mapFunc interface{}) Builder { + return &lazyBuilder{func() interface{} { return FlatMap(b.exec(), mapFunc) }} +} +func (b *lazyBuilder) Reverse() Builder { + return &lazyBuilder{func() interface{} { return Reverse(b.exec()) }} +} +func (b *lazyBuilder) Shuffle() Builder { + return &lazyBuilder{func() interface{} { return Shuffle(b.exec()) }} +} +func (b *lazyBuilder) Tail() Builder { + return &lazyBuilder{func() interface{} { return Tail(b.exec()) }} +} +func (b *lazyBuilder) Uniq() Builder { + return &lazyBuilder{func() interface{} { return Uniq(b.exec()) }} +} +func (b *lazyBuilder) Without(values ...interface{}) Builder { + return &lazyBuilder{func() interface{} { return Without(b.exec(), values...) }} +} + +func (b *lazyBuilder) All() bool { + return (&chainBuilder{b.exec()}).All() +} +func (b *lazyBuilder) Any() bool { + return (&chainBuilder{b.exec()}).Any() +} +func (b *lazyBuilder) Contains(elem interface{}) bool { + return Contains(b.exec(), elem) +} +func (b *lazyBuilder) Every(elements ...interface{}) bool { + return Every(b.exec(), elements...) +} +func (b *lazyBuilder) Find(predicate interface{}) interface{} { + return Find(b.exec(), predicate) +} +func (b *lazyBuilder) ForEach(predicate interface{}) { + ForEach(b.exec(), predicate) +} +func (b *lazyBuilder) ForEachRight(predicate interface{}) { + ForEachRight(b.exec(), predicate) +} +func (b *lazyBuilder) Head() interface{} { + return Head(b.exec()) +} +func (b *lazyBuilder) Keys() interface{} { + return Keys(b.exec()) +} +func (b *lazyBuilder) IndexOf(elem interface{}) int { + return IndexOf(b.exec(), elem) +} +func (b *lazyBuilder) IsEmpty() bool { + return IsEmpty(b.exec()) +} +func (b *lazyBuilder) Last() interface{} { + return Last(b.exec()) +} +func (b *lazyBuilder) LastIndexOf(elem interface{}) int { + return LastIndexOf(b.exec(), elem) +} +func (b *lazyBuilder) NotEmpty() bool { + return NotEmpty(b.exec()) +} +func (b *lazyBuilder) Product() float64 { + return Product(b.exec()) +} +func (b *lazyBuilder) Reduce(reduceFunc, acc interface{}) interface{} { + return Reduce(b.exec(), reduceFunc, acc) +} +func (b *lazyBuilder) Sum() float64 { + return Sum(b.exec()) +} +func (b *lazyBuilder) Type() reflect.Type { + return reflect.TypeOf(b.exec()) +} +func (b *lazyBuilder) Value() interface{} { + return b.exec() +} +func (b *lazyBuilder) Values() interface{} { + return Values(b.exec()) +} diff --git a/vendor/github.com/thoas/go-funk/map.go b/vendor/github.com/thoas/go-funk/map.go new file mode 100644 index 00000000000..4dfb4f90478 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/map.go @@ -0,0 +1,74 @@ +package funk + +import ( + "fmt" + "reflect" +) + +// Keys creates an array of the own enumerable map keys or struct field names. +func Keys(out interface{}) interface{} { + value := redirectValue(reflect.ValueOf(out)) + valueType := value.Type() + + if value.Kind() == reflect.Map { + keys := value.MapKeys() + + length := len(keys) + + resultSlice := reflect.MakeSlice(reflect.SliceOf(valueType.Key()), length, length) + + for i, key := range keys { + resultSlice.Index(i).Set(key) + } + + return resultSlice.Interface() + } + + if value.Kind() == reflect.Struct { + length := value.NumField() + + resultSlice := make([]string, length) + + for i := 0; i < length; i++ { + resultSlice[i] = valueType.Field(i).Name + } + + return resultSlice + } + + panic(fmt.Sprintf("Type %s is not supported by Keys", valueType.String())) +} + +// Values creates an array of the own enumerable map values or struct field values. +func Values(out interface{}) interface{} { + value := redirectValue(reflect.ValueOf(out)) + valueType := value.Type() + + if value.Kind() == reflect.Map { + keys := value.MapKeys() + + length := len(keys) + + resultSlice := reflect.MakeSlice(reflect.SliceOf(valueType.Elem()), length, length) + + for i, key := range keys { + resultSlice.Index(i).Set(value.MapIndex(key)) + } + + return resultSlice.Interface() + } + + if value.Kind() == reflect.Struct { + length := value.NumField() + + resultSlice := make([]interface{}, length) + + for i := 0; i < length; i++ { + resultSlice[i] = value.Field(i).Interface() + } + + return resultSlice + } + + panic(fmt.Sprintf("Type %s is not supported by Keys", valueType.String())) +} diff --git a/vendor/github.com/thoas/go-funk/max.go b/vendor/github.com/thoas/go-funk/max.go new file mode 100644 index 00000000000..0d63dc6e781 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/max.go @@ -0,0 +1,178 @@ +package funk + +import "strings" + +// MaxInt validates the input, compares the elements and returns the maximum element in an array/slice. +// It accepts []int +// It returns int +func MaxInt(i []int) int { + if len(i) == 0 { + panic("arg is an empty array/slice") + } + var max int + for idx := 0; idx < len(i); idx++ { + item := i[idx] + if idx == 0 { + max = item + continue + } + if item > max { + max = item + } + } + return max +} + +// MaxInt8 validates the input, compares the elements and returns the maximum element in an array/slice. +// It accepts []int8 +// It returns int8 +func MaxInt8(i []int8) int8 { + if len(i) == 0 { + panic("arg is an empty array/slice") + } + var max int8 + for idx := 0; idx < len(i); idx++ { + item := i[idx] + if idx == 0 { + max = item + continue + } + if item > max { + max = item + } + } + return max +} + +// MaxInt16 validates the input, compares the elements and returns the maximum element in an array/slice. +// It accepts []int16 +// It returns int16 +func MaxInt16(i []int16) int16 { + if len(i) == 0 { + panic("arg is an empty array/slice") + } + var max int16 + for idx := 0; idx < len(i); idx++ { + item := i[idx] + if idx == 0 { + max = item + continue + } + if item > max { + max = item + } + } + return max +} + +// MaxInt32 validates the input, compares the elements and returns the maximum element in an array/slice. +// It accepts []int32 +// It returns int32 +func MaxInt32(i []int32) int32 { + if len(i) == 0 { + panic("arg is an empty array/slice") + } + var max int32 + for idx := 0; idx < len(i); idx++ { + item := i[idx] + if idx == 0 { + max = item + continue + } + if item > max { + max = item + } + } + return max +} + +// MaxInt64 validates the input, compares the elements and returns the maximum element in an array/slice. +// It accepts []int64 +// It returns int64 +func MaxInt64(i []int64) int64 { + if len(i) == 0 { + panic("arg is an empty array/slice") + } + var max int64 + for idx := 0; idx < len(i); idx++ { + item := i[idx] + if idx == 0 { + max = item + continue + } + if item > max { + max = item + } + } + return max +} + +// MaxFloat32 validates the input, compares the elements and returns the maximum element in an array/slice. +// It accepts []float32 +// It returns float32 +func MaxFloat32(i []float32) float32 { + if len(i) == 0 { + panic("arg is an empty array/slice") + } + var max float32 + for idx := 0; idx < len(i); idx++ { + item := i[idx] + if idx == 0 { + max = item + continue + } + if item > max { + max = item + } + } + return max +} + +// MaxFloat64 validates the input, compares the elements and returns the maximum element in an array/slice. +// It accepts []float64 +// It returns float64 +func MaxFloat64(i []float64) float64 { + if len(i) == 0 { + panic("arg is an empty array/slice") + } + var max float64 + for idx := 0; idx < len(i); idx++ { + item := i[idx] + if idx == 0 { + max = item + continue + } + if item > max { + max = item + } + } + return max +} + +// MaxString validates the input, compares the elements and returns the maximum element in an array/slice. +// It accepts []string +// It returns string +func MaxString(i []string) string { + if len(i) == 0 { + panic("arg is an empty array/slice") + } + var max string + for idx := 0; idx < len(i); idx++ { + item := i[idx] + if idx == 0 { + max = item + continue + } + max = compareStringsMax(max, item) + } + return max +} + +// compareStrings uses the strings.Compare method to compare two strings, and returns the greater one. +func compareStringsMax(max, current string) string { + r := strings.Compare(strings.ToLower(max), strings.ToLower(current)) + if r > 0 { + return max + } + return current +} diff --git a/vendor/github.com/thoas/go-funk/min.go b/vendor/github.com/thoas/go-funk/min.go new file mode 100644 index 00000000000..81ffa77bd47 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/min.go @@ -0,0 +1,177 @@ +package funk + +import "strings" + +// MinInt validates the input, compares the elements and returns the minimum element in an array/slice. +// It accepts []int +// It returns int +func MinInt(i []int) int { + if len(i) == 0 { + panic("arg is an empty array/slice") + } + var min int + for idx := 0; idx < len(i); idx++ { + item := i[idx] + if idx == 0 { + min = item + continue + } + if item < min { + min = item + } + } + return min +} + +// MinInt8 validates the input, compares the elements and returns the minimum element in an array/slice. +// It accepts []int8 +// It returns int8 +func MinInt8(i []int8) int8 { + if len(i) == 0 { + panic("arg is an empty array/slice") + } + var min int8 + for idx := 0; idx < len(i); idx++ { + item := i[idx] + if idx == 0 { + min = item + continue + } + if item < min { + min = item + } + } + return min +} + +// MinInt16 validates the input, compares the elements and returns the minimum element in an array/slice. +// It accepts []int16 +// It returns int16 +func MinInt16(i []int16) int16 { + if len(i) == 0 { + panic("arg is an empty array/slice") + } + var min int16 + for idx := 0; idx < len(i); idx++ { + item := i[idx] + if idx == 0 { + min = item + continue + } + if item < min { + min = item + } + } + return min +} + +// MinInt32 validates the input, compares the elements and returns the minimum element in an array/slice. +// It accepts []int32 +// It returns int32 +func MinInt32(i []int32) int32 { + if len(i) == 0 { + panic("arg is an empty array/slice") + } + var min int32 + for idx := 0; idx < len(i); idx++ { + item := i[idx] + if idx == 0 { + min = item + continue + } + if item < min { + min = item + } + } + return min +} + +// MinInt64 validates the input, compares the elements and returns the minimum element in an array/slice. +// It accepts []int64 +// It returns int64 +func MinInt64(i []int64) int64 { + if len(i) == 0 { + panic("arg is an empty array/slice") + } + var min int64 + for idx := 0; idx < len(i); idx++ { + item := i[idx] + if idx == 0 { + min = item + continue + } + if item < min { + min = item + } + } + return min +} + +// MinFloat32 validates the input, compares the elements and returns the minimum element in an array/slice. +// It accepts []float32 +// It returns float32 +func MinFloat32(i []float32) float32 { + if len(i) == 0 { + panic("arg is an empty array/slice") + } + var min float32 + for idx := 0; idx < len(i); idx++ { + item := i[idx] + if idx == 0 { + min = item + continue + } + if item < min { + min = item + } + } + return min +} + +// MinFloat64 validates the input, compares the elements and returns the minimum element in an array/slice. +// It accepts []float64 +// It returns float64 +func MinFloat64(i []float64) float64 { + if len(i) == 0 { + panic("arg is an empty array/slice") + } + var min float64 + for idx := 0; idx < len(i); idx++ { + item := i[idx] + if idx == 0 { + min = item + continue + } + if item < min { + min = item + } + } + return min +} + +// MinString validates the input, compares the elements and returns the minimum element in an array/slice. +// It accepts []string +// It returns string +func MinString(i []string) string { + if len(i) == 0 { + panic("arg is an empty array/slice") + } + var min string + for idx := 0; idx < len(i); idx++ { + item := i[idx] + if idx == 0 { + min = item + continue + } + min = compareStringsMin(min, item) + } + return min +} + +func compareStringsMin(min, current string) string { + r := strings.Compare(strings.ToLower(min), strings.ToLower(current)) + if r < 0 { + return min + } + return current +} diff --git a/vendor/github.com/thoas/go-funk/operation.go b/vendor/github.com/thoas/go-funk/operation.go new file mode 100644 index 00000000000..170948b4a6c --- /dev/null +++ b/vendor/github.com/thoas/go-funk/operation.go @@ -0,0 +1,69 @@ +package funk + +import ( + "fmt" + "reflect" +) + +func calculate(arr interface{}, name string, operation rune) float64 { + value := redirectValue(reflect.ValueOf(arr)) + valueType := value.Type() + + kind := value.Kind() + + if kind == reflect.Array || kind == reflect.Slice { + length := value.Len() + + if length == 0 { + return 0 + } + + result := map[rune]float64{ + '+': 0.0, + '*': 1, + }[operation] + + for i := 0; i < length; i++ { + elem := redirectValue(value.Index(i)).Interface() + + var value float64 + switch e := elem.(type) { + case int: + value = float64(e) + case int8: + value = float64(e) + case int16: + value = float64(e) + case int32: + value = float64(e) + case int64: + value = float64(e) + case float32: + value = float64(e) + case float64: + value = e + } + + switch operation { + case '+': + result += value + case '*': + result *= value + } + } + + return result + } + + panic(fmt.Sprintf("Type %s is not supported by %s", valueType.String(), name)) +} + +// Sum computes the sum of the values in array. +func Sum(arr interface{}) float64 { + return calculate(arr, "Sum", '+') +} + +// Product computes the product of the values in array. +func Product(arr interface{}) float64 { + return calculate(arr, "Product", '*') +} diff --git a/vendor/github.com/thoas/go-funk/options.go b/vendor/github.com/thoas/go-funk/options.go new file mode 100644 index 00000000000..5d08c24efce --- /dev/null +++ b/vendor/github.com/thoas/go-funk/options.go @@ -0,0 +1,24 @@ +package funk + +type options struct { + allowZero bool +} + +type option func(*options) + +func newOptions(values ...option) *options { + opts := &options{ + allowZero: false, + } + for _, o := range values { + o(opts) + } + return opts +} + +// WithAllowZero allows zero values. +func WithAllowZero() func(*options) { + return func(opts *options) { + opts.allowZero = true + } +} diff --git a/vendor/github.com/thoas/go-funk/permutation.go b/vendor/github.com/thoas/go-funk/permutation.go new file mode 100644 index 00000000000..0b109930625 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/permutation.go @@ -0,0 +1,29 @@ +package funk + +import "errors" + +// NextPermutation Implement next permutation, +// which rearranges numbers into the lexicographically next greater permutation of numbers. +func NextPermutation(nums []int) error { + n := len(nums) + if n == 0 { + return errors.New("nums is empty") + } + + i := n - 2 + + for i >= 0 && nums[i] >= nums[i+1] { + i-- + } + + if i >= 0 { + j := n - 1 + for j >= 0 && nums[i] >= nums[j] { + j-- + } + nums[i], nums[j] = nums[j], nums[i] + } + + ReverseInt(nums[i+1:]) + return nil +} diff --git a/vendor/github.com/thoas/go-funk/predicate.go b/vendor/github.com/thoas/go-funk/predicate.go new file mode 100644 index 00000000000..e6b4131de2e --- /dev/null +++ b/vendor/github.com/thoas/go-funk/predicate.go @@ -0,0 +1,47 @@ +package funk + +import ( + "reflect" +) + +// predicatesImpl contains the common implementation of AnyPredicates and AllPredicates. +func predicatesImpl(value interface{}, wantedAnswer bool, predicates interface{}) bool { + if !IsCollection(predicates) { + panic("Predicates parameter must be an iteratee") + } + + predicatesValue := reflect.ValueOf(predicates) + inputValue := reflect.ValueOf(value) + + for i := 0; i < predicatesValue.Len(); i++ { + funcValue := predicatesValue.Index(i) + if !IsFunction(funcValue.Interface()) { + panic("Got non function as predicate") + } + + funcType := funcValue.Type() + if !IsPredicate(funcValue.Interface()) { + panic("Predicate function must have 1 parameter and must return boolean") + } + + if !inputValue.Type().ConvertibleTo(funcType.In(0)) { + panic("Given value is not compatible with type of parameter for the predicate.") + } + if result := funcValue.Call([]reflect.Value{inputValue}); wantedAnswer == result[0].Bool() { + return wantedAnswer + } + } + + return !wantedAnswer +} + +// AnyPredicates gets a value and a series of predicates, and return true if at least one of the predicates +// is true. +func AnyPredicates(value interface{}, predicates interface{}) bool { + return predicatesImpl(value, true, predicates) +} + +// AllPredicates gets a value and a series of predicates, and return true if all of the predicates are true. +func AllPredicates(value interface{}, predicates interface{}) bool { + return predicatesImpl(value, false, predicates) +} diff --git a/vendor/github.com/thoas/go-funk/presence.go b/vendor/github.com/thoas/go-funk/presence.go new file mode 100644 index 00000000000..9ab0f0f4270 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/presence.go @@ -0,0 +1,207 @@ +package funk + +import ( + "fmt" + "reflect" + "strings" +) + +// Filter iterates over elements of collection, returning an array of +// all elements predicate returns truthy for. +func Filter(arr interface{}, predicate interface{}) interface{} { + if !IsIteratee(arr) { + panic("First parameter must be an iteratee") + } + + if !IsFunction(predicate, 1, 1) { + panic("Second argument must be function") + } + + funcValue := reflect.ValueOf(predicate) + + funcType := funcValue.Type() + + if funcType.Out(0).Kind() != reflect.Bool { + panic("Return argument should be a boolean") + } + + arrValue := reflect.ValueOf(arr) + + arrType := arrValue.Type() + + // Get slice type corresponding to array type + resultSliceType := reflect.SliceOf(arrType.Elem()) + + // MakeSlice takes a slice kind type, and makes a slice. + resultSlice := reflect.MakeSlice(resultSliceType, 0, 0) + + for i := 0; i < arrValue.Len(); i++ { + elem := arrValue.Index(i) + + result := funcValue.Call([]reflect.Value{elem})[0].Interface().(bool) + + if result { + resultSlice = reflect.Append(resultSlice, elem) + } + } + + return resultSlice.Interface() +} + +// Find iterates over elements of collection, returning the first +// element predicate returns truthy for. +func Find(arr interface{}, predicate interface{}) interface{} { + _, val := FindKey(arr, predicate) + return val +} + +// Find iterates over elements of collection, returning the first +// element of an array and random of a map which predicate returns truthy for. +func FindKey(arr interface{}, predicate interface{}) (matchKey, matchEle interface{}) { + if !IsIteratee(arr) { + panic("First parameter must be an iteratee") + } + + if !IsFunction(predicate, 1, 1) { + panic("Second argument must be function") + } + + funcValue := reflect.ValueOf(predicate) + + funcType := funcValue.Type() + + if funcType.Out(0).Kind() != reflect.Bool { + panic("Return argument should be a boolean") + } + + arrValue := reflect.ValueOf(arr) + var keyArrs []reflect.Value + + isMap := arrValue.Kind() == reflect.Map + if isMap { + keyArrs = arrValue.MapKeys() + } + for i := 0; i < arrValue.Len(); i++ { + var ( + elem reflect.Value + key reflect.Value + ) + if isMap { + key = keyArrs[i] + elem = arrValue.MapIndex(key) + } else { + key = reflect.ValueOf(i) + elem = arrValue.Index(i) + } + + result := funcValue.Call([]reflect.Value{elem})[0].Interface().(bool) + + if result { + return key.Interface(), elem.Interface() + } + } + + return nil, nil +} + +// IndexOf gets the index at which the first occurrence of value is found in array or return -1 +// if the value cannot be found +func IndexOf(in interface{}, elem interface{}) int { + inValue := reflect.ValueOf(in) + + elemValue := reflect.ValueOf(elem) + + inType := inValue.Type() + + if inType.Kind() == reflect.String { + return strings.Index(inValue.String(), elemValue.String()) + } + + if inType.Kind() == reflect.Slice { + equalTo := equal(elem) + for i := 0; i < inValue.Len(); i++ { + if equalTo(reflect.Value{}, inValue.Index(i)) { + return i + } + } + } + + return -1 +} + +// LastIndexOf gets the index at which the last occurrence of value is found in array or return -1 +// if the value cannot be found +func LastIndexOf(in interface{}, elem interface{}) int { + inValue := reflect.ValueOf(in) + + elemValue := reflect.ValueOf(elem) + + inType := inValue.Type() + + if inType.Kind() == reflect.String { + return strings.LastIndex(inValue.String(), elemValue.String()) + } + + if inType.Kind() == reflect.Slice { + length := inValue.Len() + + equalTo := equal(elem) + for i := length - 1; i >= 0; i-- { + if equalTo(reflect.Value{}, inValue.Index(i)) { + return i + } + } + } + + return -1 +} + +// Contains returns true if an element is present in a iteratee. +func Contains(in interface{}, elem interface{}) bool { + inValue := reflect.ValueOf(in) + elemValue := reflect.ValueOf(elem) + inType := inValue.Type() + + switch inType.Kind() { + case reflect.String: + return strings.Contains(inValue.String(), elemValue.String()) + case reflect.Map: + equalTo := equal(elem, true) + for _, key := range inValue.MapKeys() { + if equalTo(key, inValue.MapIndex(key)) { + return true + } + } + case reflect.Slice, reflect.Array: + equalTo := equal(elem) + for i := 0; i < inValue.Len(); i++ { + if equalTo(reflect.Value{}, inValue.Index(i)) { + return true + } + } + default: + panic(fmt.Sprintf("Type %s is not supported by Contains, supported types are String, Map, Slice, Array", inType.String())) + } + + return false +} + +// Every returns true if every element is present in a iteratee. +func Every(in interface{}, elements ...interface{}) bool { + for _, elem := range elements { + if !Contains(in, elem) { + return false + } + } + return true +} + +// Some returns true if atleast one element is present in an iteratee. +func Some(in interface{}, elements ...interface{}) bool { + for _, elem := range elements { + if Contains(in, elem) { + return true + } + } + return false +} diff --git a/vendor/github.com/thoas/go-funk/reduce.go b/vendor/github.com/thoas/go-funk/reduce.go new file mode 100644 index 00000000000..579b82254d7 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/reduce.go @@ -0,0 +1,87 @@ +package funk + +import ( + "reflect" +) + +// Reduce takes a collection and reduces it to a single value using a reduction +// function (or a valid symbol) and an accumulator value. +func Reduce(arr, reduceFunc, acc interface{}) interface{} { + arrValue := redirectValue(reflect.ValueOf(arr)) + + if !IsIteratee(arrValue.Interface()) { + panic("First parameter must be an iteratee") + } + + returnType := reflect.TypeOf(Reduce).Out(0) + + isFunc := IsFunction(reduceFunc, 2, 1) + isRune := reflect.TypeOf(reduceFunc).Kind() == reflect.Int32 + + if !(isFunc || isRune) { + panic("Second argument must be a valid function or rune") + } + + accValue := reflect.ValueOf(acc) + sliceElemType := sliceElem(arrValue.Type()) + + if isRune { + if arrValue.Kind() == reflect.Slice && sliceElemType.Kind() == reflect.Interface { + accValue = accValue.Convert(returnType) + } else { + accValue = accValue.Convert(sliceElemType) + } + } else { + accValue = accValue.Convert(reflect.TypeOf(reduceFunc).In(0)) + } + + accType := accValue.Type() + + // Generate reduce function if was passed as rune + if isRune { + reduceSign := reduceFunc.(int32) + + if ok := map[rune]bool{'+': true, '*': true}[reduceSign]; !ok { + panic("Invalid reduce sign, allowed: '+' and '*'") + } + + in := []reflect.Type{accType, sliceElemType} + out := []reflect.Type{accType} + funcType := reflect.FuncOf(in, out, false) + + reduceFunc = reflect.MakeFunc(funcType, func(args []reflect.Value) []reflect.Value { + acc := args[0].Interface() + elem := args[1].Interface() + + var result float64 + params := []interface{}{acc, elem} + switch reduceSign { + case '+': + result = Sum(params) + case '*': + result = Product(params) + } + + return []reflect.Value{reflect.ValueOf(result).Convert(accType)} + }).Interface() + } + + funcValue := reflect.ValueOf(reduceFunc) + funcType := funcValue.Type() + + for i := 0; i < arrValue.Len(); i++ { + if accType.ConvertibleTo(funcType.In(0)) { + accValue = accValue.Convert(funcType.In(0)) + } + + arrElementValue := arrValue.Index(i) + if sliceElemType.ConvertibleTo(funcType.In(1)) { + arrElementValue = arrElementValue.Convert(funcType.In(1)) + } + + result := funcValue.Call([]reflect.Value{accValue, arrElementValue}) + accValue = result[0] + } + + return accValue.Convert(returnType).Interface() +} diff --git a/vendor/github.com/thoas/go-funk/retrieve.go b/vendor/github.com/thoas/go-funk/retrieve.go new file mode 100644 index 00000000000..da89e1d4711 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/retrieve.go @@ -0,0 +1,146 @@ +package funk + +import ( + "reflect" + "strings" +) + +// Get retrieves the value from given path, retriever can be modified with available RetrieverOptions +func Get(out interface{}, path string, opts ...option) interface{} { + options := newOptions(opts...) + + result := get(reflect.ValueOf(out), path, opts...) + // valid kind and we can return a result.Interface() without panic + if result.Kind() != reflect.Invalid && result.CanInterface() { + // if we don't allow zero and the result is a zero value return nil + if !options.allowZero && result.IsZero() { + return nil + } + // if the result kind is a pointer and its nil return nil + if result.Kind() == reflect.Ptr && result.IsNil() { + return nil + } + // return the result interface (i.e the zero value of it) + return result.Interface() + } + + return nil +} + +// GetOrElse retrieves the value of the pointer or default. +func GetOrElse(v interface{}, def interface{}) interface{} { + val := reflect.ValueOf(v) + if v == nil || (val.Kind() == reflect.Ptr && val.IsNil()) { + return def + } else if val.Kind() != reflect.Ptr { + return v + } + return val.Elem().Interface() +} + +func get(value reflect.Value, path string, opts ...option) reflect.Value { + options := newOptions(opts...) + + if value.Kind() == reflect.Slice || value.Kind() == reflect.Array { + var resultSlice reflect.Value + + length := value.Len() + + if length == 0 { + zeroElement := reflect.Zero(value.Type().Elem()) + pathValue := get(zeroElement, path) + value = reflect.MakeSlice(reflect.SliceOf(pathValue.Type()), 0, 0) + + return value + } + + for i := 0; i < length; i++ { + item := value.Index(i) + + resultValue := get(item, path) + + if resultValue.Kind() == reflect.Invalid || (resultValue.IsZero() && !options.allowZero) { + continue + } + + resultType := resultValue.Type() + + if resultSlice.Kind() == reflect.Invalid { + resultType := reflect.SliceOf(resultType) + + resultSlice = reflect.MakeSlice(resultType, 0, 0) + } + + resultSlice = reflect.Append(resultSlice, resultValue) + } + + // if the result is a slice of a slice, we need to flatten it + if resultSlice.Kind() != reflect.Invalid && resultSlice.Type().Elem().Kind() == reflect.Slice { + return flattenDeep(resultSlice) + } + + return resultSlice + } + + quoted := false + parts := strings.FieldsFunc(path, func(r rune) bool { + if r == '"' { + quoted = !quoted + } + return !quoted && r == '.' + }) + + for i, part := range parts { + parts[i] = strings.Trim(part, "\"") + } + + for _, part := range parts { + value = redirectValue(value) + kind := value.Kind() + + switch kind { + case reflect.Invalid: + continue + case reflect.Struct: + if isNilIndirection(value, part) { + return reflect.ValueOf(nil) + } + value = value.FieldByName(part) + case reflect.Map: + value = value.MapIndex(reflect.ValueOf(part)) + case reflect.Slice, reflect.Array: + value = get(value, part) + default: + return reflect.ValueOf(nil) + } + } + + return value +} + +func isNilIndirection(v reflect.Value, name string) bool { + vType := v.Type() + for i := 0; i < vType.NumField(); i++ { + field := vType.Field(i) + if !isEmbeddedStructPointerField(field) { + return false + } + + fieldType := field.Type.Elem() + + _, found := fieldType.FieldByName(name) + if found { + return v.Field(i).IsNil() + } + } + + return false +} + +func isEmbeddedStructPointerField(field reflect.StructField) bool { + if !field.Anonymous { + return false + } + + return field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct +} diff --git a/vendor/github.com/thoas/go-funk/scan.go b/vendor/github.com/thoas/go-funk/scan.go new file mode 100644 index 00000000000..197330fb646 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/scan.go @@ -0,0 +1,205 @@ +package funk + +import ( + "fmt" + "reflect" +) + +// ForEach iterates over elements of collection and invokes iteratee +// for each element. +func ForEach(arr interface{}, predicate interface{}) { + if !IsIteratee(arr) { + panic("First parameter must be an iteratee") + } + + var ( + funcValue = reflect.ValueOf(predicate) + arrValue = reflect.ValueOf(arr) + arrType = arrValue.Type() + funcType = funcValue.Type() + ) + + if arrType.Kind() == reflect.Slice || arrType.Kind() == reflect.Array { + if !IsFunction(predicate, 1, 0) { + panic("Second argument must be a function with one parameter") + } + + arrElemType := arrValue.Type().Elem() + arrElemPointerType := reflect.New(arrElemType).Type() + usePointer := arrElemPointerType.ConvertibleTo(funcType.In(0)) + + // Checking whether element type is convertible to function's first argument's type. + if !arrElemType.ConvertibleTo(funcType.In(0)) && !usePointer { + panic("Map function's argument is not compatible with type of array.") + } + + for i := 0; i < arrValue.Len(); i++ { + if usePointer { + funcValue.Call([]reflect.Value{arrValue.Index(i).Addr()}) + } else { + funcValue.Call([]reflect.Value{arrValue.Index(i)}) + } + } + } + + if arrType.Kind() == reflect.Map { + if !IsFunction(predicate, 2, 0) { + panic("Second argument must be a function with two parameters") + } + + // Type checking for Map = (key, value) + keyType := arrType.Key() + valueType := arrType.Elem() + + if !keyType.ConvertibleTo(funcType.In(0)) { + panic(fmt.Sprintf("function first argument is not compatible with %s", keyType.String())) + } + + if !valueType.ConvertibleTo(funcType.In(1)) { + panic(fmt.Sprintf("function second argument is not compatible with %s", valueType.String())) + } + + for _, key := range arrValue.MapKeys() { + funcValue.Call([]reflect.Value{key, arrValue.MapIndex(key)}) + } + } +} + +// ForEachRight iterates over elements of collection from the right and invokes iteratee +// for each element. +func ForEachRight(arr interface{}, predicate interface{}) { + if !IsIteratee(arr) { + panic("First parameter must be an iteratee") + } + + var ( + funcValue = reflect.ValueOf(predicate) + arrValue = reflect.ValueOf(arr) + arrType = arrValue.Type() + funcType = funcValue.Type() + ) + + if arrType.Kind() == reflect.Slice || arrType.Kind() == reflect.Array { + if !IsFunction(predicate, 1, 0) { + panic("Second argument must be a function with one parameter") + } + + arrElemType := arrValue.Type().Elem() + arrElemPointerType := reflect.New(arrElemType).Type() + usePointer := arrElemPointerType.ConvertibleTo(funcType.In(0)) + + // Checking whether element type is convertible to function's first argument's type. + if !arrElemType.ConvertibleTo(funcType.In(0)) && !usePointer { + panic("Map function's argument is not compatible with type of array.") + } + + for i := arrValue.Len() - 1; i >= 0; i-- { + if usePointer { + funcValue.Call([]reflect.Value{arrValue.Index(i).Addr()}) + } else { + funcValue.Call([]reflect.Value{arrValue.Index(i)}) + } + + } + } + + if arrType.Kind() == reflect.Map { + if !IsFunction(predicate, 2, 0) { + panic("Second argument must be a function with two parameters") + } + + // Type checking for Map = (key, value) + keyType := arrType.Key() + valueType := arrType.Elem() + + if !keyType.ConvertibleTo(funcType.In(0)) { + panic(fmt.Sprintf("function first argument is not compatible with %s", keyType.String())) + } + + if !valueType.ConvertibleTo(funcType.In(1)) { + panic(fmt.Sprintf("function second argument is not compatible with %s", valueType.String())) + } + + keys := Reverse(arrValue.MapKeys()).([]reflect.Value) + + for _, key := range keys { + funcValue.Call([]reflect.Value{key, arrValue.MapIndex(key)}) + } + } +} + +// Head gets the first element of array. +func Head(arr interface{}) interface{} { + value := redirectValue(reflect.ValueOf(arr)) + valueType := value.Type() + + kind := value.Kind() + + if kind == reflect.Array || kind == reflect.Slice { + if value.Len() == 0 { + return nil + } + + return value.Index(0).Interface() + } + + panic(fmt.Sprintf("Type %s is not supported by Head", valueType.String())) +} + +// Last gets the last element of array. +func Last(arr interface{}) interface{} { + value := redirectValue(reflect.ValueOf(arr)) + valueType := value.Type() + + kind := value.Kind() + + if kind == reflect.Array || kind == reflect.Slice { + if value.Len() == 0 { + return nil + } + + return value.Index(value.Len() - 1).Interface() + } + + panic(fmt.Sprintf("Type %s is not supported by Last", valueType.String())) +} + +// Initial gets all but the last element of array. +func Initial(arr interface{}) interface{} { + value := redirectValue(reflect.ValueOf(arr)) + valueType := value.Type() + + kind := value.Kind() + + if kind == reflect.Array || kind == reflect.Slice { + length := value.Len() + + if length <= 1 { + return arr + } + + return value.Slice(0, length-1).Interface() + } + + panic(fmt.Sprintf("Type %s is not supported by Initial", valueType.String())) +} + +// Tail gets all but the first element of array. +func Tail(arr interface{}) interface{} { + value := redirectValue(reflect.ValueOf(arr)) + valueType := value.Type() + + kind := value.Kind() + + if kind == reflect.Array || kind == reflect.Slice { + length := value.Len() + + if length <= 1 { + return arr + } + + return value.Slice(1, length).Interface() + } + + panic(fmt.Sprintf("Type %s is not supported by Initial", valueType.String())) +} diff --git a/vendor/github.com/thoas/go-funk/short_if.go b/vendor/github.com/thoas/go-funk/short_if.go new file mode 100644 index 00000000000..537ff56f0a7 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/short_if.go @@ -0,0 +1,8 @@ +package funk + +func ShortIf(condition bool, a interface{}, b interface{}) interface{} { + if condition { + return a + } + return b +} diff --git a/vendor/github.com/thoas/go-funk/subset.go b/vendor/github.com/thoas/go-funk/subset.go new file mode 100644 index 00000000000..de47be15176 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/subset.go @@ -0,0 +1,41 @@ +package funk + +import ( + "reflect" +) + +// Subset returns true if collection x is a subset of y. +func Subset(x interface{}, y interface{}) bool { + if !IsCollection(x) { + panic("First parameter must be a collection") + } + if !IsCollection(y) { + panic("Second parameter must be a collection") + } + + xValue := reflect.ValueOf(x) + xType := xValue.Type() + + yValue := reflect.ValueOf(y) + yType := yValue.Type() + + if NotEqual(xType, yType) { + panic("Parameters must have the same type") + } + + if xValue.Len() == 0 { + return true + } + + if yValue.Len() == 0 || yValue.Len() < xValue.Len() { + return false + } + + for i := 0; i < xValue.Len(); i++ { + if !Contains(yValue.Interface(), xValue.Index(i).Interface()) { + return false + } + } + + return true +} diff --git a/vendor/github.com/thoas/go-funk/subtraction.go b/vendor/github.com/thoas/go-funk/subtraction.go new file mode 100644 index 00000000000..10a0a982021 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/subtraction.go @@ -0,0 +1,87 @@ +package funk + +import ( + "reflect" +) + +// Subtract returns the subtraction between two collections. +func Subtract(x interface{}, y interface{}) interface{} { + if !IsCollection(x) { + panic("First parameter must be a collection") + } + if !IsCollection(y) { + panic("Second parameter must be a collection") + } + + hash := map[interface{}]struct{}{} + + xValue := reflect.ValueOf(x) + xType := xValue.Type() + + yValue := reflect.ValueOf(y) + yType := yValue.Type() + + if NotEqual(xType, yType) { + panic("Parameters must have the same type") + } + + zType := reflect.SliceOf(xType.Elem()) + zSlice := reflect.MakeSlice(zType, 0, 0) + + for i := 0; i < xValue.Len(); i++ { + v := xValue.Index(i).Interface() + hash[v] = struct{}{} + } + + for i := 0; i < yValue.Len(); i++ { + v := yValue.Index(i).Interface() + _, ok := hash[v] + if ok { + delete(hash, v) + } + } + + for i := 0; i < xValue.Len(); i++ { + v := xValue.Index(i).Interface() + _, ok := hash[v] + if ok { + zSlice = reflect.Append(zSlice, xValue.Index(i)) + } + } + + return zSlice.Interface() +} + +// SubtractString returns the subtraction between two collections of string +func SubtractString(x []string, y []string) []string { + if len(x) == 0 { + return []string{} + } + + if len(y) == 0 { + return x + } + + slice := []string{} + hash := map[string]struct{}{} + + for _, v := range x { + hash[v] = struct{}{} + } + + for _, v := range y { + _, ok := hash[v] + if ok { + delete(hash, v) + } + } + + for _, v := range x { + _, ok := hash[v] + if ok { + slice = append(slice, v) + } + } + + return slice +} diff --git a/vendor/github.com/thoas/go-funk/transform.go b/vendor/github.com/thoas/go-funk/transform.go new file mode 100644 index 00000000000..73ae76ac0a3 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/transform.go @@ -0,0 +1,618 @@ +package funk + +import ( + "fmt" + "math/rand" + "reflect" + "strings" +) + +// Chunk creates an array of elements split into groups with the length of size. +// If array can't be split evenly, the final chunk will be +// the remaining element. +func Chunk(arr interface{}, size int) interface{} { + if !IsIteratee(arr) { + panic("First parameter must be neither array nor slice") + } + + if size == 0 { + return arr + } + + arrValue := reflect.ValueOf(arr) + + arrType := arrValue.Type() + + resultSliceType := reflect.SliceOf(arrType) + + // Initialize final result slice which will contains slice + resultSlice := reflect.MakeSlice(resultSliceType, 0, 0) + + itemType := arrType.Elem() + + var itemSlice reflect.Value + + itemSliceType := reflect.SliceOf(itemType) + + length := arrValue.Len() + + for i := 0; i < length; i++ { + if i%size == 0 || i == 0 { + if itemSlice.Kind() != reflect.Invalid { + resultSlice = reflect.Append(resultSlice, itemSlice) + } + + itemSlice = reflect.MakeSlice(itemSliceType, 0, 0) + } + + itemSlice = reflect.Append(itemSlice, arrValue.Index(i)) + + if i == length-1 { + resultSlice = reflect.Append(resultSlice, itemSlice) + } + } + + return resultSlice.Interface() +} + +// ToMap transforms a collection of instances to a Map. +// []T => map[type of T.]T +func ToMap(in interface{}, pivot string) interface{} { + // input value must be a collection + if !IsCollection(in) { + panic(fmt.Sprintf("%v must be a slict or an array", in)) + } + + value := reflect.ValueOf(in) + + inType := value.Type() + structType := inType.Elem() + + // retrieve the struct in the slice to deduce key type + if structType.Kind() == reflect.Ptr { + structType = structType.Elem() + } + + field, ok := structType.FieldByName(pivot) + if !ok { + panic(fmt.Sprintf("`%s` must be a field of the struct %s", pivot, structType.Name())) + } + + // value of the map will be the input type + collectionType := reflect.MapOf(field.Type, inType.Elem()) + + // create a map from scratch + collection := reflect.MakeMap(collectionType) + + for i := 0; i < value.Len(); i++ { + instance := value.Index(i) + var field reflect.Value + + if instance.Kind() == reflect.Ptr { + field = instance.Elem().FieldByName(pivot) + } else { + field = instance.FieldByName(pivot) + } + + collection.SetMapIndex(field, instance) + } + + return collection.Interface() +} + +// ToSet transforms a collection of instances to a Set. +// []T => map[T]struct{} +func ToSet(in interface{}) interface{} { + // input value must be a collection + if !IsCollection(in) { + panic(fmt.Sprintf("%v must be a slice or an array", in)) + } + + var ( + empty = struct{}{} + emptyType = reflect.TypeOf(empty) + emptyValue = reflect.ValueOf(empty) + ) + + value := reflect.ValueOf(in) + elemType := value.Type().Elem() + + // key of the set will be the input type + collection := reflect.MakeMap(reflect.MapOf(elemType, emptyType)) + + for i := 0; i < value.Len(); i++ { + collection.SetMapIndex(value.Index(i), emptyValue) + } + + return collection.Interface() +} + +func mapSlice(arrValue reflect.Value, funcValue reflect.Value) reflect.Value { + funcType := funcValue.Type() + + if funcType.NumIn() != 1 || funcType.NumOut() == 0 || funcType.NumOut() > 2 { + panic("Map function with an array must have one parameter and must return one or two parameters") + } + + arrElemType := arrValue.Type().Elem() + + // Checking whether element type is convertible to function's first argument's type. + if !arrElemType.ConvertibleTo(funcType.In(0)) { + panic("Map function's argument is not compatible with type of array.") + } + + if funcType.NumOut() == 1 { + // Get slice type corresponding to function's return value's type. + resultSliceType := reflect.SliceOf(funcType.Out(0)) + + // MakeSlice takes a slice kind type, and makes a slice. + resultSlice := reflect.MakeSlice(resultSliceType, 0, 0) + + for i := 0; i < arrValue.Len(); i++ { + result := funcValue.Call([]reflect.Value{arrValue.Index(i)})[0] + + resultSlice = reflect.Append(resultSlice, result) + } + + return resultSlice + } + + if funcType.NumOut() == 2 { + // value of the map will be the input type + collectionType := reflect.MapOf(funcType.Out(0), funcType.Out(1)) + + // create a map from scratch + collection := reflect.MakeMap(collectionType) + + for i := 0; i < arrValue.Len(); i++ { + results := funcValue.Call([]reflect.Value{arrValue.Index(i)}) + + collection.SetMapIndex(results[0], results[1]) + } + + return collection + } + + return reflect.Value{} +} + +func mapMap(arrValue reflect.Value, funcValue reflect.Value) reflect.Value { + funcType := funcValue.Type() + + if funcType.NumIn() != 2 || funcType.NumOut() == 0 || funcType.NumOut() > 2 { + panic("Map function with a map must have two parameters and must return one or two parameters") + } + + // Only one returned parameter, should be a slice + if funcType.NumOut() == 1 { + // Get slice type corresponding to function's return value's type. + resultSliceType := reflect.SliceOf(funcType.Out(0)) + + // MakeSlice takes a slice kind type, and makes a slice. + resultSlice := reflect.MakeSlice(resultSliceType, 0, 0) + + for _, key := range arrValue.MapKeys() { + results := funcValue.Call([]reflect.Value{key, arrValue.MapIndex(key)}) + + result := results[0] + + resultSlice = reflect.Append(resultSlice, result) + } + + return resultSlice + } + + // two parameters, should be a map + if funcType.NumOut() == 2 { + // value of the map will be the input type + collectionType := reflect.MapOf(funcType.Out(0), funcType.Out(1)) + + // create a map from scratch + collection := reflect.MakeMap(collectionType) + + for _, key := range arrValue.MapKeys() { + results := funcValue.Call([]reflect.Value{key, arrValue.MapIndex(key)}) + + collection.SetMapIndex(results[0], results[1]) + + } + + return collection + } + + return reflect.Value{} +} + +// Map manipulates an iteratee and transforms it to another type. +func Map(arr interface{}, mapFunc interface{}) interface{} { + result := mapFn(arr, mapFunc, "Map") + + if result.IsValid() { + return result.Interface() + } + + return nil +} + +func mapFn(arr interface{}, mapFunc interface{}, funcName string) reflect.Value { + if !IsIteratee(arr) { + panic("First parameter must be an iteratee") + } + + if !IsFunction(mapFunc) { + panic("Second argument must be function") + } + + var ( + funcValue = reflect.ValueOf(mapFunc) + arrValue = reflect.ValueOf(arr) + arrType = arrValue.Type() + ) + + kind := arrType.Kind() + + if kind == reflect.Slice || kind == reflect.Array { + return mapSlice(arrValue, funcValue) + } else if kind == reflect.Map { + return mapMap(arrValue, funcValue) + } + + panic(fmt.Sprintf("Type %s is not supported by "+funcName, arrType.String())) +} + +// FlatMap manipulates an iteratee and transforms it to a flattened collection of another type. +func FlatMap(arr interface{}, mapFunc interface{}) interface{} { + result := mapFn(arr, mapFunc, "FlatMap") + + if result.IsValid() { + return flatten(result).Interface() + } + + return nil +} + +// Flatten flattens a two-dimensional array. +func Flatten(out interface{}) interface{} { + return flatten(reflect.ValueOf(out)).Interface() +} + +func flatten(value reflect.Value) reflect.Value { + sliceType := value.Type() + + if (value.Kind() != reflect.Slice && value.Kind() != reflect.Array) || + (sliceType.Elem().Kind() != reflect.Slice && sliceType.Elem().Kind() != reflect.Array) { + panic("Argument must be an array or slice of at least two dimensions") + } + + resultSliceType := sliceType.Elem().Elem() + + resultSlice := reflect.MakeSlice(reflect.SliceOf(resultSliceType), 0, 0) + + length := value.Len() + + for i := 0; i < length; i++ { + item := value.Index(i) + + resultSlice = reflect.AppendSlice(resultSlice, item) + } + + return resultSlice +} + +// FlattenDeep recursively flattens array. +func FlattenDeep(out interface{}) interface{} { + return flattenDeep(reflect.ValueOf(out)).Interface() +} + +func flattenDeep(value reflect.Value) reflect.Value { + sliceType := sliceElem(value.Type()) + + resultSlice := reflect.MakeSlice(reflect.SliceOf(sliceType), 0, 0) + + return flattenRecursive(value, resultSlice) +} + +func flattenRecursive(value reflect.Value, result reflect.Value) reflect.Value { + length := value.Len() + + for i := 0; i < length; i++ { + item := value.Index(i) + kind := item.Kind() + + if kind == reflect.Slice || kind == reflect.Array { + result = flattenRecursive(item, result) + } else { + result = reflect.Append(result, item) + } + } + + return result +} + +// Shuffle creates an array of shuffled values +func Shuffle(in interface{}) interface{} { + value := reflect.ValueOf(in) + valueType := value.Type() + + kind := value.Kind() + + if kind == reflect.Array || kind == reflect.Slice { + length := value.Len() + + resultSlice := makeSlice(value, length) + + for i, v := range rand.Perm(length) { + resultSlice.Index(i).Set(value.Index(v)) + } + + return resultSlice.Interface() + } + + panic(fmt.Sprintf("Type %s is not supported by Shuffle", valueType.String())) +} + +// Reverse transforms an array the first element will become the last, +// the second element will become the second to last, etc. +func Reverse(in interface{}) interface{} { + value := reflect.ValueOf(in) + valueType := value.Type() + + kind := value.Kind() + + if kind == reflect.String { + return ReverseString(in.(string)) + } + + if kind == reflect.Array || kind == reflect.Slice { + length := value.Len() + + resultSlice := makeSlice(value, length) + + j := 0 + for i := length - 1; i >= 0; i-- { + resultSlice.Index(j).Set(value.Index(i)) + j++ + } + + return resultSlice.Interface() + } + + panic(fmt.Sprintf("Type %s is not supported by Reverse", valueType.String())) +} + +// Uniq creates an array with unique values. +func Uniq(in interface{}) interface{} { + value := reflect.ValueOf(in) + valueType := value.Type() + + kind := value.Kind() + + if kind == reflect.Array || kind == reflect.Slice { + length := value.Len() + + result := makeSlice(value, 0) + + seen := make(map[interface{}]bool, length) + + for i := 0; i < length; i++ { + val := value.Index(i) + v := val.Interface() + + if _, ok := seen[v]; ok { + continue + } + + seen[v] = true + result = reflect.Append(result, val) + } + + return result.Interface() + } + + panic(fmt.Sprintf("Type %s is not supported by Uniq", valueType.String())) +} + +// Uniq creates an array with unique values. +func UniqBy(in interface{}, mapFunc interface{}) interface{} { + if !IsFunction(mapFunc) { + panic("Second argument must be function") + } + + value := reflect.ValueOf(in) + valueType := value.Type() + + kind := value.Kind() + + funcValue := reflect.ValueOf(mapFunc) + + if kind == reflect.Array || kind == reflect.Slice { + length := value.Len() + + result := makeSlice(value, 0) + + seen := make(map[interface{}]bool, length) + + for i := 0; i < length; i++ { + val := value.Index(i) + v := funcValue.Call([]reflect.Value{val})[0].Interface() + + if _, ok := seen[v]; ok { + continue + } + + seen[v] = true + result = reflect.Append(result, val) + } + + return result.Interface() + } + + panic(fmt.Sprintf("Type %s is not supported by Uniq", valueType.String())) +} + +// ConvertSlice converts a slice type to another, +// a perfect example would be to convert a slice of struct to a slice of interface. +func ConvertSlice(in interface{}, out interface{}) { + srcValue := reflect.ValueOf(in) + + dstValue := reflect.ValueOf(out) + + if dstValue.Kind() != reflect.Ptr { + panic("Second argument must be a pointer") + } + + dstValue = dstValue.Elem() + + if srcValue.Kind() != reflect.Slice && srcValue.Kind() != reflect.Array { + panic("First argument must be an array or slice") + } + + if dstValue.Kind() != reflect.Slice && dstValue.Kind() != reflect.Array { + panic("Second argument must be an array or slice") + } + + // returns value that points to dstValue + direct := reflect.Indirect(dstValue) + + length := srcValue.Len() + + for i := 0; i < length; i++ { + dstValue = reflect.Append(dstValue, srcValue.Index(i)) + } + + direct.Set(dstValue) +} + +// Drop creates an array/slice with `n` elements dropped from the beginning. +func Drop(in interface{}, n int) interface{} { + value := reflect.ValueOf(in) + valueType := value.Type() + + kind := value.Kind() + + if kind == reflect.Array || kind == reflect.Slice { + length := value.Len() + + resultSlice := makeSlice(value, length-n) + + j := 0 + for i := n; i < length; i++ { + resultSlice.Index(j).Set(value.Index(i)) + j++ + } + + return resultSlice.Interface() + + } + + panic(fmt.Sprintf("Type %s is not supported by Drop", valueType.String())) +} + +// Prune returns a copy of "in" that only contains fields in "paths" +// which are looked up using struct field name. +// For lookup paths by field tag instead, use funk.PruneByTag() +func Prune(in interface{}, paths []string) (interface{}, error) { + return pruneByTag(in, paths, nil /*tag*/) +} + +// pruneByTag returns a copy of "in" that only contains fields in "paths" +// which are looked up using struct field Tag "tag". +func PruneByTag(in interface{}, paths []string, tag string) (interface{}, error) { + return pruneByTag(in, paths, &tag) +} + +// pruneByTag returns a copy of "in" that only contains fields in "paths" +// which are looked up using struct field Tag "tag". If tag is nil, +// traverse paths using struct field name +func pruneByTag(in interface{}, paths []string, tag *string) (interface{}, error) { + inValue := reflect.ValueOf(in) + + ret := reflect.New(inValue.Type()).Elem() + + for _, path := range paths { + parts := strings.Split(path, ".") + if err := prune(inValue, ret, parts, tag); err != nil { + return nil, err + } + } + return ret.Interface(), nil +} + +func prune(inValue reflect.Value, ret reflect.Value, parts []string, tag *string) error { + if len(parts) == 0 { + // we reached the location that ret needs to hold inValue + // Note: The value at the end of the path is not copied, maybe we need to change. + // ret and the original data holds the same reference to this value + ret.Set(inValue) + return nil + } + + inKind := inValue.Kind() + + switch inKind { + case reflect.Ptr: + if inValue.IsNil() { + // TODO validate + return nil + } + if ret.IsNil() { + // init ret and go to next level + ret.Set(reflect.New(inValue.Type().Elem())) + } + return prune(inValue.Elem(), ret.Elem(), parts, tag) + case reflect.Struct: + part := parts[0] + var fValue reflect.Value + var fRet reflect.Value + if tag == nil { + // use field name + fValue = inValue.FieldByName(part) + if !fValue.IsValid() { + return fmt.Errorf("field name %v is not found in struct %v", part, inValue.Type().String()) + } + fRet = ret.FieldByName(part) + } else { + // search tag that has key equal to part + found := false + for i := 0; i < inValue.NumField(); i++ { + f := inValue.Type().Field(i) + if key, ok := f.Tag.Lookup(*tag); ok { + if key == part { + fValue = inValue.Field(i) + fRet = ret.Field(i) + found = true + break + } + } + } + if !found { + return fmt.Errorf("struct tag %v is not found with key %v", *tag, part) + } + } + // init Ret is zero and go down one more level + if fRet.IsZero() { + fRet.Set(reflect.New(fValue.Type()).Elem()) + } + return prune(fValue, fRet, parts[1:], tag) + case reflect.Array, reflect.Slice: + // set all its elements + length := inValue.Len() + // init ret + if ret.IsZero() { + if inKind == reflect.Slice { + ret.Set(reflect.MakeSlice(inValue.Type(), length /*len*/, length /*cap*/)) + } else { // array + ret.Set(reflect.New(inValue.Type()).Elem()) + } + } + for j := 0; j < length; j++ { + if err := prune(inValue.Index(j), ret.Index(j), parts, tag); err != nil { + return err + } + } + default: + return fmt.Errorf("path %v cannot be looked up on kind of %v", strings.Join(parts, "."), inValue.Kind()) + } + + return nil +} diff --git a/vendor/github.com/thoas/go-funk/typesafe.go b/vendor/github.com/thoas/go-funk/typesafe.go new file mode 100644 index 00000000000..2b240e5baff --- /dev/null +++ b/vendor/github.com/thoas/go-funk/typesafe.go @@ -0,0 +1,1200 @@ +package funk + +import ( + "math/rand" +) + +// InBools is an alias of ContainsBool, returns true if a bool is present in a iteratee. +func InBools(s []bool, v bool) bool { + return ContainsBool(s, v) +} + +// InInts is an alias of ContainsInt, returns true if an int is present in a iteratee. +func InInts(s []int, v int) bool { + return ContainsInt(s, v) +} + +// InInt32s is an alias of ContainsInt32, returns true if an int32 is present in a iteratee. +func InInt32s(s []int32, v int32) bool { + return ContainsInt32(s, v) +} + +// InInt64s is an alias of ContainsInt64, returns true if an int64 is present in a iteratee. +func InInt64s(s []int64, v int64) bool { + return ContainsInt64(s, v) +} + +// InUInts is an alias of ContainsUInt, returns true if an uint is present in a iteratee. +func InUInts(s []uint, v uint) bool { + return ContainsUInt(s, v) +} + +// InUInt32s is an alias of ContainsUInt32, returns true if an uint32 is present in a iteratee. +func InUInt32s(s []uint32, v uint32) bool { + return ContainsUInt32(s, v) +} + +// InUInt64s is an alias of ContainsUInt64, returns true if an uint64 is present in a iteratee. +func InUInt64s(s []uint64, v uint64) bool { + return ContainsUInt64(s, v) +} + +// InStrings is an alias of ContainsString, returns true if a string is present in a iteratee. +func InStrings(s []string, v string) bool { + return ContainsString(s, v) +} + +// InFloat32s is an alias of ContainsFloat32, returns true if a float32 is present in a iteratee. +func InFloat32s(s []float32, v float32) bool { + return ContainsFloat32(s, v) +} + +// InFloat64s is an alias of ContainsFloat64, returns true if a float64 is present in a iteratee. +func InFloat64s(s []float64, v float64) bool { + return ContainsFloat64(s, v) +} + +// FindFloat64 iterates over a collection of float64, returning an array of +// all float64 elements predicate returns truthy for. +func FindFloat64(s []float64, cb func(s float64) bool) (float64, bool) { + for _, i := range s { + result := cb(i) + + if result { + return i, true + } + } + + return 0.0, false +} + +// FindFloat32 iterates over a collection of float32, returning the first +// float32 element predicate returns truthy for. +func FindFloat32(s []float32, cb func(s float32) bool) (float32, bool) { + for _, i := range s { + result := cb(i) + + if result { + return i, true + } + } + + return 0.0, false +} + +// FindInt iterates over a collection of int, returning the first +// int element predicate returns truthy for. +func FindInt(s []int, cb func(s int) bool) (int, bool) { + for _, i := range s { + result := cb(i) + + if result { + return i, true + } + } + + return 0, false +} + +// FindInt32 iterates over a collection of int32, returning the first +// int32 element predicate returns truthy for. +func FindInt32(s []int32, cb func(s int32) bool) (int32, bool) { + for _, i := range s { + result := cb(i) + + if result { + return i, true + } + } + + return 0, false +} + +// FindInt64 iterates over a collection of int64, returning the first +// int64 element predicate returns truthy for. +func FindInt64(s []int64, cb func(s int64) bool) (int64, bool) { + for _, i := range s { + result := cb(i) + + if result { + return i, true + } + } + + return 0, false +} + +// FindString iterates over a collection of string, returning the first +// string element predicate returns truthy for. +func FindString(s []string, cb func(s string) bool) (string, bool) { + for _, i := range s { + result := cb(i) + + if result { + return i, true + } + } + + return "", false +} + +// FilterBool iterates over a collection of bool, returning an array of +// all bool elements predicate returns truthy for. +func FilterBool(s []bool, cb func(s bool) bool) []bool { + results := []bool{} + + for _, i := range s { + result := cb(i) + + if result { + results = append(results, i) + } + } + + return results +} + +// FilterFloat64 iterates over a collection of float64, returning an array of +// all float64 elements predicate returns truthy for. +func FilterFloat64(s []float64, cb func(s float64) bool) []float64 { + results := []float64{} + + for _, i := range s { + result := cb(i) + + if result { + results = append(results, i) + } + } + + return results +} + +// FilterFloat32 iterates over a collection of float32, returning an array of +// all float32 elements predicate returns truthy for. +func FilterFloat32(s []float32, cb func(s float32) bool) []float32 { + results := []float32{} + + for _, i := range s { + result := cb(i) + + if result { + results = append(results, i) + } + } + + return results +} + +// FilterInt iterates over a collection of int, returning an array of +// all int elements predicate returns truthy for. +func FilterInt(s []int, cb func(s int) bool) []int { + results := []int{} + + for _, i := range s { + result := cb(i) + + if result { + results = append(results, i) + } + } + + return results +} + +// FilterInt32 iterates over a collection of int32, returning an array of +// all int32 elements predicate returns truthy for. +func FilterInt32(s []int32, cb func(s int32) bool) []int32 { + results := []int32{} + + for _, i := range s { + result := cb(i) + + if result { + results = append(results, i) + } + } + + return results +} + +// FilterInt64 iterates over a collection of int64, returning an array of +// all int64 elements predicate returns truthy for. +func FilterInt64(s []int64, cb func(s int64) bool) []int64 { + results := []int64{} + + for _, i := range s { + result := cb(i) + + if result { + results = append(results, i) + } + } + + return results +} + +// FilterUInt iterates over a collection of uint, returning an array of +// all uint elements predicate returns truthy for. +func FilterUInt(s []uint, cb func(s uint) bool) []uint { + results := []uint{} + + for _, i := range s { + result := cb(i) + + if result { + results = append(results, i) + } + } + + return results +} + +// FilterUInt32 iterates over a collection of uint32, returning an array of +// all uint32 elements predicate returns truthy for. +func FilterUInt32(s []uint32, cb func(s uint32) bool) []uint32 { + results := []uint32{} + + for _, i := range s { + result := cb(i) + + if result { + results = append(results, i) + } + } + + return results +} + +// FilterUInt64 iterates over a collection of uint64, returning an array of +// all uint64 elements predicate returns truthy for. +func FilterUInt64(s []uint64, cb func(s uint64) bool) []uint64 { + results := []uint64{} + + for _, i := range s { + result := cb(i) + + if result { + results = append(results, i) + } + } + + return results +} + +// FilterString iterates over a collection of string, returning an array of +// all string elements predicate returns truthy for. +func FilterString(s []string, cb func(s string) bool) []string { + results := []string{} + + for _, i := range s { + result := cb(i) + + if result { + results = append(results, i) + } + } + + return results +} + +// ContainsBool returns true if a boolean is present in a iteratee. +func ContainsBool(s []bool, v bool) bool { + for _, vv := range s { + if vv == v { + return true + } + } + return false +} + +// ContainsInt returns true if an int is present in a iteratee. +func ContainsInt(s []int, v int) bool { + for _, vv := range s { + if vv == v { + return true + } + } + return false +} + +// ContainsInt32 returns true if an int32 is present in a iteratee. +func ContainsInt32(s []int32, v int32) bool { + for _, vv := range s { + if vv == v { + return true + } + } + return false +} + +// ContainsInt64 returns true if an int64 is present in a iteratee. +func ContainsInt64(s []int64, v int64) bool { + for _, vv := range s { + if vv == v { + return true + } + } + return false +} + +// ContainsUInt returns true if an uint is present in a iteratee. +func ContainsUInt(s []uint, v uint) bool { + for _, vv := range s { + if vv == v { + return true + } + } + return false +} + +// ContainsUInt32 returns true if an uint32 is present in a iteratee. +func ContainsUInt32(s []uint32, v uint32) bool { + for _, vv := range s { + if vv == v { + return true + } + } + return false +} + +// ContainsUInt64 returns true if an uint64 is present in a iteratee. +func ContainsUInt64(s []uint64, v uint64) bool { + for _, vv := range s { + if vv == v { + return true + } + } + return false +} + +// ContainsString returns true if a string is present in a iteratee. +func ContainsString(s []string, v string) bool { + for _, vv := range s { + if vv == v { + return true + } + } + return false +} + +// ContainsFloat32 returns true if a float32 is present in a iteratee. +func ContainsFloat32(s []float32, v float32) bool { + for _, vv := range s { + if vv == v { + return true + } + } + return false +} + +// ContainsFloat64 returns true if a float64 is present in a iteratee. +func ContainsFloat64(s []float64, v float64) bool { + for _, vv := range s { + if vv == v { + return true + } + } + return false +} + +// SumInt32 sums a int32 iteratee and returns the sum of all elements +func SumInt32(s []int32) (sum int32) { + for _, v := range s { + sum += v + } + return +} + +// SumInt64 sums a int64 iteratee and returns the sum of all elements +func SumInt64(s []int64) (sum int64) { + for _, v := range s { + sum += v + } + return +} + +// SumInt sums a int iteratee and returns the sum of all elements +func SumInt(s []int) (sum int) { + for _, v := range s { + sum += v + } + return +} + +// SumUInt32 sums a uint32 iteratee and returns the sum of all elements +func SumUInt32(s []uint32) (sum uint32) { + for _, v := range s { + sum += v + } + return +} + +// SumUInt64 sums a uint64 iteratee and returns the sum of all elements +func SumUInt64(s []uint64) (sum uint64) { + for _, v := range s { + sum += v + } + return +} + +// SumUInt sums a uint iteratee and returns the sum of all elements +func SumUInt(s []uint) (sum uint) { + for _, v := range s { + sum += v + } + return +} + +// SumFloat64 sums a float64 iteratee and returns the sum of all elements +func SumFloat64(s []float64) (sum float64) { + for _, v := range s { + sum += v + } + return +} + +// SumFloat32 sums a float32 iteratee and returns the sum of all elements +func SumFloat32(s []float32) (sum float32) { + for _, v := range s { + sum += v + } + return +} + +// ReverseBools reverses an array of bool +func ReverseBools(s []bool) []bool { + for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { + s[i], s[j] = s[j], s[i] + } + return s +} + +// ReverseStrings reverses an array of string +func ReverseStrings(s []string) []string { + for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { + s[i], s[j] = s[j], s[i] + } + return s +} + +// ReverseInt reverses an array of int +func ReverseInt(s []int) []int { + for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { + s[i], s[j] = s[j], s[i] + } + return s +} + +// ReverseInt32 reverses an array of int32 +func ReverseInt32(s []int32) []int32 { + for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { + s[i], s[j] = s[j], s[i] + } + return s +} + +// ReverseInt64 reverses an array of int64 +func ReverseInt64(s []int64) []int64 { + for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { + s[i], s[j] = s[j], s[i] + } + return s +} + +// ReverseUInt reverses an array of int +func ReverseUInt(s []uint) []uint { + for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { + s[i], s[j] = s[j], s[i] + } + return s +} + +// ReverseUInt32 reverses an array of uint32 +func ReverseUInt32(s []uint32) []uint32 { + for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { + s[i], s[j] = s[j], s[i] + } + return s +} + +// ReverseUInt64 reverses an array of uint64 +func ReverseUInt64(s []uint64) []uint64 { + for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { + s[i], s[j] = s[j], s[i] + } + return s +} + +// ReverseFloat64 reverses an array of float64 +func ReverseFloat64(s []float64) []float64 { + for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { + s[i], s[j] = s[j], s[i] + } + return s +} + +// ReverseFloat32 reverses an array of float32 +func ReverseFloat32(s []float32) []float32 { + for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { + s[i], s[j] = s[j], s[i] + } + return s +} + +// ReverseString reverses a string +func ReverseString(s string) string { + r := []rune(s) + for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 { + r[i], r[j] = r[j], r[i] + } + return string(r) +} + +func indexOf(n int, f func(int) bool) int { + for i := 0; i < n; i++ { + if f(i) { + return i + } + } + return -1 +} + +// IndexOfBool gets the index at which the first occurrence of a bool value is found in array or return -1 +// if the value cannot be found +func IndexOfBool(a []bool, x bool) int { + return indexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// IndexOfInt gets the index at which the first occurrence of an int value is found in array or return -1 +// if the value cannot be found +func IndexOfInt(a []int, x int) int { + return indexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// IndexOfInt32 gets the index at which the first occurrence of an int32 value is found in array or return -1 +// if the value cannot be found +func IndexOfInt32(a []int32, x int32) int { + return indexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// IndexOfInt64 gets the index at which the first occurrence of an int64 value is found in array or return -1 +// if the value cannot be found +func IndexOfInt64(a []int64, x int64) int { + return indexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// IndexOfUInt gets the index at which the first occurrence of an uint value is found in array or return -1 +// if the value cannot be found +func IndexOfUInt(a []uint, x uint) int { + return indexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// IndexOfUInt32 gets the index at which the first occurrence of an uint32 value is found in array or return -1 +// if the value cannot be found +func IndexOfUInt32(a []uint32, x uint32) int { + return indexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// IndexOfUInt64 gets the index at which the first occurrence of an uint64 value is found in array or return -1 +// if the value cannot be found +func IndexOfUInt64(a []uint64, x uint64) int { + return indexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// IndexOfFloat64 gets the index at which the first occurrence of an float64 value is found in array or return -1 +// if the value cannot be found +func IndexOfFloat64(a []float64, x float64) int { + return indexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// IndexOfString gets the index at which the first occurrence of a string value is found in array or return -1 +// if the value cannot be found +func IndexOfString(a []string, x string) int { + return indexOf(len(a), func(i int) bool { return a[i] == x }) +} + +func lastIndexOf(n int, f func(int) bool) int { + for i := n - 1; i >= 0; i-- { + if f(i) { + return i + } + } + return -1 +} + +// LastIndexOfBool gets the index at which the first occurrence of a bool value is found in array or return -1 +// if the value cannot be found +func LastIndexOfBool(a []bool, x bool) int { + return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// LastIndexOfInt gets the index at which the first occurrence of an int value is found in array or return -1 +// if the value cannot be found +func LastIndexOfInt(a []int, x int) int { + return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// LastIndexOfInt32 gets the index at which the first occurrence of an int32 value is found in array or return -1 +// if the value cannot be found +func LastIndexOfInt32(a []int32, x int32) int { + return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// LastIndexOfInt64 gets the index at which the first occurrence of an int64 value is found in array or return -1 +// if the value cannot be found +func LastIndexOfInt64(a []int64, x int64) int { + return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// LastIndexOfUInt gets the index at which the first occurrence of an uint value is found in array or return -1 +// if the value cannot be found +func LastIndexOfUInt(a []uint, x uint) int { + return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// LastIndexOfUInt32 gets the index at which the first occurrence of an uint32 value is found in array or return -1 +// if the value cannot be found +func LastIndexOfUInt32(a []uint32, x uint32) int { + return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// LastIndexOfUInt64 gets the index at which the first occurrence of an uint64 value is found in array or return -1 +// if the value cannot be found +func LastIndexOfUInt64(a []uint64, x uint64) int { + return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// LastIndexOfFloat64 gets the index at which the first occurrence of an float64 value is found in array or return -1 +// if the value cannot be found +func LastIndexOfFloat64(a []float64, x float64) int { + return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// LastIndexOfFloat32 gets the index at which the first occurrence of an float32 value is found in array or return -1 +// if the value cannot be found +func LastIndexOfFloat32(a []float32, x float32) int { + return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// LastIndexOfString gets the index at which the first occurrence of a string value is found in array or return -1 +// if the value cannot be found +func LastIndexOfString(a []string, x string) int { + return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// UniqBool creates an array of bool with unique values. +func UniqBool(a []bool) []bool { + results := []bool{} + for _, value := range a { + // If results is not empty, there is at most 1 value in it + if len(results) == 0 || results[0] != value { + results = append(results, value) + } + // At most 2 unique values + if len(results) == 2 { + break + } + } + return results +} + +// UniqInt32 creates an array of int32 with unique values. +func UniqInt32(a []int32) []int32 { + var ( + length = len(a) + seen = make(map[int32]struct{}, length) + results = make([]int32, 0) + ) + + for i := 0; i < length; i++ { + v := a[i] + + if _, ok := seen[v]; ok { + continue + } + + seen[v] = struct{}{} + results = append(results, v) + } + + return results +} + +// UniqInt64 creates an array of int64 with unique values. +func UniqInt64(a []int64) []int64 { + var ( + length = len(a) + seen = make(map[int64]struct{}, length) + results = make([]int64, 0) + ) + + for i := 0; i < length; i++ { + v := a[i] + + if _, ok := seen[v]; ok { + continue + } + + seen[v] = struct{}{} + results = append(results, v) + } + + return results +} + +// UniqInt creates an array of int with unique values. +func UniqInt(a []int) []int { + var ( + length = len(a) + seen = make(map[int]struct{}, length) + results = make([]int, 0) + ) + + for i := 0; i < length; i++ { + v := a[i] + + if _, ok := seen[v]; ok { + continue + } + + seen[v] = struct{}{} + results = append(results, v) + } + + return results +} + +// UniqUInt32 creates an array of uint32 with unique values. +func UniqUInt32(a []uint32) []uint32 { + var ( + length = len(a) + seen = make(map[uint32]struct{}, length) + results = make([]uint32, 0) + ) + + for i := 0; i < length; i++ { + v := a[i] + + if _, ok := seen[v]; ok { + continue + } + + seen[v] = struct{}{} + results = append(results, v) + } + + return results +} + +// UniqUInt64 creates an array of uint64 with unique values. +func UniqUInt64(a []uint64) []uint64 { + var ( + length = len(a) + seen = make(map[uint64]struct{}, length) + results = make([]uint64, 0) + ) + + for i := 0; i < length; i++ { + v := a[i] + + if _, ok := seen[v]; ok { + continue + } + + seen[v] = struct{}{} + results = append(results, v) + } + + return results +} + +// UniqUInt creates an array of uint with unique values. +func UniqUInt(a []uint) []uint { + var ( + length = len(a) + seen = make(map[uint]struct{}, length) + results = make([]uint, 0) + ) + + for i := 0; i < length; i++ { + v := a[i] + + if _, ok := seen[v]; ok { + continue + } + + seen[v] = struct{}{} + results = append(results, v) + } + + return results +} + +// UniqString creates an array of string with unique values. +func UniqString(a []string) []string { + var ( + length = len(a) + seen = make(map[string]struct{}, length) + results = make([]string, 0) + ) + + for i := 0; i < length; i++ { + v := a[i] + + if _, ok := seen[v]; ok { + continue + } + + seen[v] = struct{}{} + results = append(results, v) + } + + return results +} + +// UniqFloat64 creates an array of float64 with unique values. +func UniqFloat64(a []float64) []float64 { + var ( + length = len(a) + seen = make(map[float64]struct{}, length) + results = make([]float64, 0) + ) + + for i := 0; i < length; i++ { + v := a[i] + + if _, ok := seen[v]; ok { + continue + } + + seen[v] = struct{}{} + results = append(results, v) + } + + return results +} + +// UniqFloat32 creates an array of float32 with unique values. +func UniqFloat32(a []float32) []float32 { + var ( + length = len(a) + seen = make(map[float32]struct{}, length) + results = make([]float32, 0) + ) + + for i := 0; i < length; i++ { + v := a[i] + + if _, ok := seen[v]; ok { + continue + } + + seen[v] = struct{}{} + results = append(results, v) + } + + return results +} + +// ShuffleBool creates an array of bool shuffled values using Fisher–Yates algorithm +func ShuffleBool(a []bool) []bool { + for i := range a { + j := rand.Intn(i + 1) + a[i], a[j] = a[j], a[i] + } + + return a +} + +// ShuffleInt creates an array of int shuffled values using Fisher–Yates algorithm +func ShuffleInt(a []int) []int { + for i := range a { + j := rand.Intn(i + 1) + a[i], a[j] = a[j], a[i] + } + + return a +} + +// ShuffleInt32 creates an array of int32 shuffled values using Fisher–Yates algorithm +func ShuffleInt32(a []int32) []int32 { + for i := range a { + j := rand.Intn(i + 1) + a[i], a[j] = a[j], a[i] + } + + return a +} + +// ShuffleInt64 creates an array of int64 shuffled values using Fisher–Yates algorithm +func ShuffleInt64(a []int64) []int64 { + for i := range a { + j := rand.Intn(i + 1) + a[i], a[j] = a[j], a[i] + } + + return a +} + +// ShuffleUInt creates an array of int shuffled values using Fisher–Yates algorithm +func ShuffleUInt(a []uint) []uint { + for i := range a { + j := rand.Intn(i + 1) + a[i], a[j] = a[j], a[i] + } + + return a +} + +// ShuffleUInt32 creates an array of uint32 shuffled values using Fisher–Yates algorithm +func ShuffleUInt32(a []uint32) []uint32 { + for i := range a { + j := rand.Intn(i + 1) + a[i], a[j] = a[j], a[i] + } + + return a +} + +// ShuffleUInt64 creates an array of uint64 shuffled values using Fisher–Yates algorithm +func ShuffleUInt64(a []uint64) []uint64 { + for i := range a { + j := rand.Intn(i + 1) + a[i], a[j] = a[j], a[i] + } + + return a +} + +// ShuffleString creates an array of string shuffled values using Fisher–Yates algorithm +func ShuffleString(a []string) []string { + for i := range a { + j := rand.Intn(i + 1) + a[i], a[j] = a[j], a[i] + } + + return a +} + +// ShuffleFloat32 creates an array of float32 shuffled values using Fisher–Yates algorithm +func ShuffleFloat32(a []float32) []float32 { + for i := range a { + j := rand.Intn(i + 1) + a[i], a[j] = a[j], a[i] + } + + return a +} + +// ShuffleFloat64 creates an array of float64 shuffled values using Fisher–Yates algorithm +func ShuffleFloat64(a []float64) []float64 { + for i := range a { + j := rand.Intn(i + 1) + a[i], a[j] = a[j], a[i] + } + + return a +} + +// DropBool creates a slice with `n` bools dropped from the beginning. +func DropBool(s []bool, n int) []bool { + return s[n:] +} + +// DropString creates a slice with `n` strings dropped from the beginning. +func DropString(s []string, n int) []string { + return s[n:] +} + +// DropInt creates a slice with `n` ints dropped from the beginning. +func DropInt(s []int, n int) []int { + return s[n:] +} + +// DropInt32 creates a slice with `n` int32s dropped from the beginning. +func DropInt32(s []int32, n int) []int32 { + return s[n:] +} + +// DropInt64 creates a slice with `n` int64s dropped from the beginning. +func DropInt64(s []int64, n int) []int64 { + return s[n:] +} + +// DropUInt creates a slice with `n` ints dropped from the beginning. +func DropUInt(s []uint, n uint) []uint { + return s[n:] +} + +// DropUInt32 creates a slice with `n` int32s dropped from the beginning. +func DropUInt32(s []uint32, n int) []uint32 { + return s[n:] +} + +// DropUInt64 creates a slice with `n` int64s dropped from the beginning. +func DropUInt64(s []uint64, n int) []uint64 { + return s[n:] +} + +// DropFloat32 creates a slice with `n` float32s dropped from the beginning. +func DropFloat32(s []float32, n int) []float32 { + return s[n:] +} + +// DropFloat64 creates a slice with `n` float64s dropped from the beginning. +func DropFloat64(s []float64, n int) []float64 { + return s[n:] +} + +// ChunkStrings creates an array of strings split into groups with the length of size. +// If array can't be split evenly, the final chunk will be +// the remaining element. +func ChunkStrings(arr []string, size int) [][]string { + var results [][]string + + for i := 0; i < len(arr); i += size { + end := i + size + + if end > len(arr) { + end = len(arr) + } + + results = append(results, arr[i:end]) + } + + return results +} + +// ChunkInts creates an array of ints split into groups with the length of size. +// If array can't be split evenly, the final chunk will be +// the remaining element. +func ChunkInts(arr []int, size int) [][]int { + var results [][]int + + for i := 0; i < len(arr); i += size { + end := i + size + + if end > len(arr) { + end = len(arr) + } + + results = append(results, arr[i:end]) + } + + return results +} + +// ChunkInt32s creates an array of int32s split into groups with the length of size. +// If array can't be split evenly, the final chunk will be +// the remaining element. +func ChunkInt32s(arr []int32, size int) [][]int32 { + var results [][]int32 + + for i := 0; i < len(arr); i += size { + end := i + size + + if end > len(arr) { + end = len(arr) + } + + results = append(results, arr[i:end]) + } + + return results +} + +// ChunkInt64s creates an array of int64s split into groups with the length of size. +// If array can't be split evenly, the final chunk will be +// the remaining element. +func ChunkInt64s(arr []int64, size int) [][]int64 { + var results [][]int64 + + for i := 0; i < len(arr); i += size { + end := i + size + + if end > len(arr) { + end = len(arr) + } + + results = append(results, arr[i:end]) + } + + return results +} + +// ChunkUInts creates an array of uints split into groups with the length of size. +// If array can't be split evenly, the final chunk will be +// the remaining element. +func ChunkUInts(arr []uint, size int) [][]uint { + var results [][]uint + + for i := 0; i < len(arr); i += size { + end := i + size + + if end > len(arr) { + end = len(arr) + } + + results = append(results, arr[i:end]) + } + + return results +} + +// ChunkUInt32s creates an array of uint32s split into groups with the length of size. +// If array can't be split evenly, the final chunk will be +// the remaining element. +func ChunkUInt32s(arr []uint32, size int) [][]uint32 { + var results [][]uint32 + + for i := 0; i < len(arr); i += size { + end := i + size + + if end > len(arr) { + end = len(arr) + } + + results = append(results, arr[i:end]) + } + + return results +} + +// ChunkUInt64s creates an array of uint64s split into groups with the length of size. +// If array can't be split evenly, the final chunk will be +// the remaining element. +func ChunkUInt64s(arr []uint64, size int) [][]uint64 { + var results [][]uint64 + + for i := 0; i < len(arr); i += size { + end := i + size + + if end > len(arr) { + end = len(arr) + } + + results = append(results, arr[i:end]) + } + + return results +} + +// ChunkFloat64s creates an array of float64s split into groups with the length of size. +// If array can't be split evenly, the final chunk will be +// the remaining element. +func ChunkFloat64s(arr []float64, size int) [][]float64 { + var results [][]float64 + + for i := 0; i < len(arr); i += size { + end := i + size + + if end > len(arr) { + end = len(arr) + } + + results = append(results, arr[i:end]) + } + + return results +} diff --git a/vendor/github.com/thoas/go-funk/union.go b/vendor/github.com/thoas/go-funk/union.go new file mode 100644 index 00000000000..559cdd543af --- /dev/null +++ b/vendor/github.com/thoas/go-funk/union.go @@ -0,0 +1,67 @@ +package funk + +import ( + "reflect" +) + +// Union returns the union between two collections. +func Union(collections ...interface{}) interface{} { + // shortcut zero/single argument + if len(collections) == 0 { + return nil + } else if len(collections) == 1 { + return collections[0] + } + + if !IsIteratee(collections[0]) { + panic("Parameter must be a collection") + } + + cType := reflect.TypeOf(collections[0]) + zLen := 0 + + for i, x := range collections { + xValue := reflect.ValueOf(x) + xType := xValue.Type() + if i > 0 && NotEqual(cType, xType) { + panic("Parameters must have the same type") + } + + zLen += xValue.Len() + } + + if cType.Kind() == reflect.Map { + zType := reflect.MapOf(cType.Key(), cType.Elem()) + zMap := reflect.MakeMap(zType) + + for _, x := range collections { + xIter := reflect.ValueOf(x).MapRange() + for xIter.Next() { + zMap.SetMapIndex(xIter.Key(), xIter.Value()) + } + } + + return zMap.Interface() + } else { + zType := reflect.SliceOf(cType.Elem()) + zSlice := reflect.MakeSlice(zType, 0, 0) + + for _, x := range collections { + xValue := reflect.ValueOf(x) + zSlice = reflect.AppendSlice(zSlice, xValue) + } + + return zSlice.Interface() + } +} + +// UnionStringMap returns the union between multiple string maps +func UnionStringMap(x ...map[string]string) map[string]string { + zMap := map[string]string{} + for _, xMap := range x { + for k, v := range xMap { + zMap[k] = v + } + } + return zMap +} diff --git a/vendor/github.com/thoas/go-funk/utils.go b/vendor/github.com/thoas/go-funk/utils.go new file mode 100644 index 00000000000..43d9a2be73a --- /dev/null +++ b/vendor/github.com/thoas/go-funk/utils.go @@ -0,0 +1,103 @@ +package funk + +import ( + "fmt" + "reflect" +) + +func equal(expectedOrPredicate interface{}, optionalIsMap ...bool) func(keyValueIfMap, actualValue reflect.Value) bool { + isMap := append(optionalIsMap, false)[0] + + if IsFunction(expectedOrPredicate) { + inTypes := []reflect.Type{nil}; if isMap { + inTypes = append(inTypes, nil) + } + + if !IsPredicate(expectedOrPredicate, inTypes...) { + panic(fmt.Sprintf("Predicate function must have %d parameter and must return boolean", len(inTypes))) + } + + predicateValue := reflect.ValueOf(expectedOrPredicate) + + return func(keyValueIfMap, actualValue reflect.Value) bool { + + if isMap && !keyValueIfMap.Type().ConvertibleTo(predicateValue.Type().In(0)) { + panic("Given key is not compatible with type of parameter for the predicate.") + } + + if (isMap && !actualValue.Type().ConvertibleTo(predicateValue.Type().In(1))) || + (!isMap && !actualValue.Type().ConvertibleTo(predicateValue.Type().In(0))) { + panic("Given value is not compatible with type of parameter for the predicate.") + } + + args := []reflect.Value{actualValue} + if isMap { + args = append([]reflect.Value{keyValueIfMap}, args...) + } + + return predicateValue.Call(args)[0].Bool() + } + } + + expected := expectedOrPredicate + + return func(keyValueIfMap, actualValue reflect.Value) bool { + if isMap { + actualValue = keyValueIfMap + } + + if expected == nil || actualValue.IsZero() { + return actualValue.Interface() == expected + } + + return reflect.DeepEqual(actualValue.Interface(), expected) + } +} + +func sliceElem(rtype reflect.Type) reflect.Type { + for { + if rtype.Kind() != reflect.Slice && rtype.Kind() != reflect.Array { + return rtype + } + + rtype = rtype.Elem() + } +} + +func redirectValue(value reflect.Value) reflect.Value { + for { + if !value.IsValid() || (value.Kind() != reflect.Ptr && value.Kind() != reflect.Interface) { + return value + } + + res := value.Elem() + + // Test for a circular type. + if res.Kind() == reflect.Ptr && value.Kind() == reflect.Ptr && value.Pointer() == res.Pointer() { + return value + } + + if !res.IsValid() && value.Kind() == reflect.Ptr { + return reflect.Zero(value.Type().Elem()) + } + + value = res + } +} + +func makeSlice(value reflect.Value, values ...int) reflect.Value { + sliceType := sliceElem(value.Type()) + + size := value.Len() + cap := size + + if len(values) > 0 { + size = values[0] + } + + if len(values) > 1 { + cap = values[1] + } + + return reflect.MakeSlice(reflect.SliceOf(sliceType), size, cap) +} diff --git a/vendor/github.com/thoas/go-funk/without.go b/vendor/github.com/thoas/go-funk/without.go new file mode 100644 index 00000000000..6e35e986993 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/without.go @@ -0,0 +1,19 @@ +package funk + +import "reflect" + +// Without creates an array excluding all given values. +func Without(in interface{}, values ...interface{}) interface{} { + if !IsCollection(in) { + panic("First parameter must be a collection") + } + + inValue := reflect.ValueOf(in) + for _, value := range values { + if NotEqual(inValue.Type().Elem(), reflect.TypeOf(value)) { + panic("Values must have the same type") + } + } + + return LeftJoin(inValue, reflect.ValueOf(values)).Interface() +} diff --git a/vendor/github.com/thoas/go-funk/zip.go b/vendor/github.com/thoas/go-funk/zip.go new file mode 100644 index 00000000000..dcd38287faf --- /dev/null +++ b/vendor/github.com/thoas/go-funk/zip.go @@ -0,0 +1,44 @@ +package funk + +import ( + "reflect" +) + +// Tuple is the return type of Zip +type Tuple struct { + Element1 interface{} + Element2 interface{} +} + +// Zip returns a list of tuples, where the i-th tuple contains the i-th element +// from each of the input iterables. The returned list is truncated in length +// to the length of the shortest input iterable. +func Zip(slice1 interface{}, slice2 interface{}) []Tuple { + if !IsCollection(slice1) || !IsCollection(slice2) { + panic("First parameter must be a collection") + } + + var ( + minLength int + inValue1 = reflect.ValueOf(slice1) + inValue2 = reflect.ValueOf(slice2) + result = []Tuple{} + length1 = inValue1.Len() + length2 = inValue2.Len() + ) + + if length1 <= length2 { + minLength = length1 + } else { + minLength = length2 + } + + for i := 0; i < minLength; i++ { + newTuple := Tuple{ + Element1: inValue1.Index(i).Interface(), + Element2: inValue2.Index(i).Interface(), + } + result = append(result, newTuple) + } + return result +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 2c5b46b850c..c86de87030d 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -832,6 +832,11 @@ github.com/kylelemons/godebug/pretty ## explicit; go 1.18 github.com/leodido/go-urn github.com/leodido/go-urn/scim/schema +# github.com/lib/pq v1.10.9 +## explicit; go 1.13 +github.com/lib/pq +github.com/lib/pq/oid +github.com/lib/pq/scram # github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de ## explicit github.com/liggitt/tabwriter @@ -1051,13 +1056,16 @@ github.com/openshift/api/route/v1 ## explicit; go 1.21 github.com/openshift/assisted-image-service/pkg/isoeditor github.com/openshift/assisted-image-service/pkg/overlay -# github.com/openshift/assisted-service/api v0.0.0 => github.com/openshift/assisted-service/api v0.0.0-20230831114549-1922eda29cf8 +# github.com/openshift/assisted-service v1.0.10-0.20230830164851-6573b5d7021d ## explicit; go 1.18 +github.com/openshift/assisted-service/pkg/validations +# github.com/openshift/assisted-service/api v0.0.0 => github.com/openshift/assisted-service/api v0.0.0-20241001055825-63e8b0d3ad63 +## explicit; go 1.21 github.com/openshift/assisted-service/api/common github.com/openshift/assisted-service/api/hiveextension/v1beta1 github.com/openshift/assisted-service/api/v1beta1 -# github.com/openshift/assisted-service/client v0.0.0 => github.com/openshift/assisted-service/client v0.0.0-20230831114549-1922eda29cf8 -## explicit; go 1.18 +# github.com/openshift/assisted-service/client v0.0.0 => github.com/openshift/assisted-service/client v0.0.0-20241001055825-63e8b0d3ad63 +## explicit; go 1.21 github.com/openshift/assisted-service/client github.com/openshift/assisted-service/client/events github.com/openshift/assisted-service/client/installer @@ -1065,8 +1073,8 @@ github.com/openshift/assisted-service/client/managed_domains github.com/openshift/assisted-service/client/manifests github.com/openshift/assisted-service/client/operators github.com/openshift/assisted-service/client/versions -# github.com/openshift/assisted-service/models v0.0.0 => github.com/openshift/assisted-service/models v0.0.0-20230831114549-1922eda29cf8 -## explicit; go 1.18 +# github.com/openshift/assisted-service/models v0.0.0 => github.com/openshift/assisted-service/models v0.0.0-20241001055825-63e8b0d3ad63 +## explicit; go 1.21 github.com/openshift/assisted-service/models # github.com/openshift/client-go v0.0.0-20241001162912-da6d55e4611f ## explicit; go 1.22.0 @@ -1120,18 +1128,21 @@ github.com/openshift/cluster-api-provider-libvirt/pkg/apis/libvirtproviderconfig ## explicit; go 1.17 github.com/openshift/cluster-api-provider-ovirt/pkg/apis github.com/openshift/cluster-api-provider-ovirt/pkg/apis/ovirtprovider/v1beta1 -# github.com/openshift/custom-resource-status v1.1.2 +# github.com/openshift/custom-resource-status v1.1.3-0.20220503160415-f2fdb4999d87 ## explicit; go 1.12 github.com/openshift/custom-resource-status/conditions/v1 -# github.com/openshift/hive/apis v0.0.0-20220222213051-def9088fdb5a -## explicit; go 1.17 +# github.com/openshift/hive/apis v0.0.0-20231220215202-ad99b9e52d27 +## explicit; go 1.20 github.com/openshift/hive/apis/hive/v1 github.com/openshift/hive/apis/hive/v1/agent +github.com/openshift/hive/apis/hive/v1/alibabacloud github.com/openshift/hive/apis/hive/v1/aws github.com/openshift/hive/apis/hive/v1/azure github.com/openshift/hive/apis/hive/v1/baremetal github.com/openshift/hive/apis/hive/v1/gcp github.com/openshift/hive/apis/hive/v1/ibmcloud +github.com/openshift/hive/apis/hive/v1/metricsconfig +github.com/openshift/hive/apis/hive/v1/none github.com/openshift/hive/apis/hive/v1/openstack github.com/openshift/hive/apis/hive/v1/ovirt github.com/openshift/hive/apis/hive/v1/vsphere @@ -1262,6 +1273,9 @@ github.com/syndtr/gocapability/capability # github.com/thedevsaddam/retry v0.0.0-20200324223450-9769a859cc6d ## explicit; go 1.13 github.com/thedevsaddam/retry +# github.com/thoas/go-funk v0.9.3 +## explicit; go 1.13 +github.com/thoas/go-funk # github.com/ulikunitz/xz v0.5.12 ## explicit; go 1.12 github.com/ulikunitz/xz @@ -2497,8 +2511,8 @@ sigs.k8s.io/yaml/goyaml.v3 # github.com/metal3-io/baremetal-operator/pkg/hardwareutils => github.com/openshift/baremetal-operator/pkg/hardwareutils v0.0.0-20231128154154-6736c9b9c6c8 # k8s.io/cloud-provider-vsphere => github.com/openshift/cloud-provider-vsphere v1.19.1-0.20240626105621-6464d0bb4928 # sigs.k8s.io/cluster-api-provider-ibmcloud => sigs.k8s.io/cluster-api-provider-ibmcloud v0.9.0-alpha.0.0.20240913094112-c6bcd313bce0 -# github.com/openshift/assisted-service/api => github.com/openshift/assisted-service/api v0.0.0-20230831114549-1922eda29cf8 -# github.com/openshift/assisted-service/client => github.com/openshift/assisted-service/client v0.0.0-20230831114549-1922eda29cf8 -# github.com/openshift/assisted-service/models => github.com/openshift/assisted-service/models v0.0.0-20230831114549-1922eda29cf8 +# github.com/openshift/assisted-service/api => github.com/openshift/assisted-service/api v0.0.0-20241001055825-63e8b0d3ad63 +# github.com/openshift/assisted-service/client => github.com/openshift/assisted-service/client v0.0.0-20241001055825-63e8b0d3ad63 +# github.com/openshift/assisted-service/models => github.com/openshift/assisted-service/models v0.0.0-20241001055825-63e8b0d3ad63 # github.com/containerd/containerd => github.com/containerd/containerd v1.6.26 # github.com/vmware-tanzu/vm-operator/pkg/constants/testlabels => github.com/vmware-tanzu/vm-operator/pkg/constants/testlabels v0.0.0-20240404200847-de75746a9505