diff --git a/go.mod b/go.mod index 76d850ba2a..ddf7d8ac0b 100644 --- a/go.mod +++ b/go.mod @@ -38,3 +38,5 @@ require ( knative.dev/networking v0.0.0-20210331064822-999a7708876c knative.dev/pkg v0.0.0-20210331065221-952fdd90dbb0 ) + +replace knative.dev/eventing => github.com/openshift/knative-eventing v0.99.1-0.20210610110214-7043d7e4f943 diff --git a/go.sum b/go.sum index eba0152fa9..59a440971c 100644 --- a/go.sum +++ b/go.sum @@ -628,6 +628,8 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y github.com/onsi/gomega v1.10.2 h1:aY/nuoWlKJud2J6U0E3NWsjlg+0GtwXxgEqthRdzlcs= github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/openshift/knative-eventing v0.99.1-0.20210610110214-7043d7e4f943 h1:qrs+/c8zUnP399NixAUlqmAQ4PH1HQPOKGwa8/811lQ= +github.com/openshift/knative-eventing v0.99.1-0.20210610110214-7043d7e4f943/go.mod h1:EmyNMt16keS1pusIL5GxxueP06nxRtl+fiZIy1Il5Ws= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= @@ -641,9 +643,10 @@ github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.8.0 h1:Keo9qb7iRJs2voHvunFtuuYFsbWeOBh8/P9v/kVMFtw= -github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs= +github.com/pelletier/go-toml/v2 v2.0.0-beta.2 h1:f/g66OWmYXmVnYL3UAhqpM9YuWKFR2vjYfFNSDQcHPQ= +github.com/pelletier/go-toml/v2 v2.0.0-beta.2/go.mod h1:+X+aW6gUj6Hda43TeYHVCIvYNG/jqY/8ZFXAeXXHl+Q= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc= @@ -1276,8 +1279,6 @@ k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6 h1:+WnxoVtG8TMiudHBSEtrVL k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= k8s.io/utils v0.0.0-20200729134348-d5654de09c73 h1:uJmqzgNWG7XyClnU/mLPBWwfKKF1K8Hf8whTseBgJcg= k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -knative.dev/eventing v0.22.0 h1:esDaddfSmiVhLKDnUCVVunSdNeHklVEz0fsqs3NupuQ= -knative.dev/eventing v0.22.0/go.mod h1:LOG7bh0eZQkbYANcnORwke6Yy6aUu62o8GeByaOFfRQ= knative.dev/hack v0.0.0-20210325223819-b6ab329907d3 h1:km0Rrh0T9/wA2pivQm1hqSPVwgNgGCHC2WNn3GakZmE= knative.dev/hack v0.0.0-20210325223819-b6ab329907d3/go.mod h1:PHt8x8yX5Z9pPquBEfIj0X66f8iWkWfR0S/sarACJrI= knative.dev/hack/schema v0.0.0-20210325223819-b6ab329907d3/go.mod h1:ffjwmdcrH5vN3mPhO8RrF2KfNnbHeCE2C60A+2cv3U0= diff --git a/openshift/ci-operator/knative-test-images/wathola-kafka-sender/Dockerfile b/openshift/ci-operator/knative-test-images/wathola-kafka-sender/Dockerfile new file mode 100644 index 0000000000..fedd01d828 --- /dev/null +++ b/openshift/ci-operator/knative-test-images/wathola-kafka-sender/Dockerfile @@ -0,0 +1,5 @@ +# Do not edit! This file was generate via Makefile +FROM openshift/origin-base + +ADD wathola-kafka-sender /usr/bin/wathola-kafka-sender +ENTRYPOINT ["/usr/bin/wathola-kafka-sender"] diff --git a/test/e2e-common.sh b/test/e2e-common.sh index d144824dfe..c29b336173 100755 --- a/test/e2e-common.sh +++ b/test/e2e-common.sh @@ -489,8 +489,8 @@ function test_consolidated_channel_plain() { install_consolidated_channel_crds || return 1 install_consolidated_sources_crds || return 1 - go_test_e2e -tags=e2e,source -timeout=40m -test.parallel=${TEST_PARALLEL} ./test/e2e -channels=messaging.knative.dev/v1beta1:KafkaChannel || fail_test - go_test_e2e -tags=e2e,source -timeout=5m -test.parallel=${TEST_PARALLEL} ./test/conformance -channels=messaging.knative.dev/v1beta1:KafkaChannel -sources=sources.knative.dev/v1beta1:KafkaSource || fail_test + go_test_e2e -tags=e2e,consolidated,source -timeout=40m -test.parallel=${TEST_PARALLEL} ./test/e2e -channels=messaging.knative.dev/v1beta1:KafkaChannel || fail_test + go_test_e2e -tags=e2e,consolidated,source -timeout=5m -test.parallel=${TEST_PARALLEL} ./test/conformance -channels=messaging.knative.dev/v1beta1:KafkaChannel -sources=sources.knative.dev/v1beta1:KafkaSource || fail_test uninstall_sources_crds || return 1 uninstall_channel_crds || return 1 @@ -506,7 +506,7 @@ function test_consolidated_channel_tls() { install_consolidated_channel_crds || return 1 - go_test_e2e -tags=e2e -timeout=40m -test.parallel=${TEST_PARALLEL} ./test/e2e -channels=messaging.knative.dev/v1beta1:KafkaChannel || fail_test + go_test_e2e -tags=e2e,consolidated -timeout=40m -test.parallel=${TEST_PARALLEL} ./test/e2e -channels=messaging.knative.dev/v1beta1:KafkaChannel || fail_test uninstall_channel_crds || return 1 } @@ -521,7 +521,7 @@ function test_consolidated_channel_sasl() { install_consolidated_channel_crds || return 1 - go_test_e2e -tags=e2e -timeout=40m -test.parallel=${TEST_PARALLEL} ./test/e2e -channels=messaging.knative.dev/v1beta1:KafkaChannel || fail_test + go_test_e2e -tags=e2e,consolidated -timeout=40m -test.parallel=${TEST_PARALLEL} ./test/e2e -channels=messaging.knative.dev/v1beta1:KafkaChannel || fail_test uninstall_channel_crds || return 1 } diff --git a/test/e2e/channel_subscription_ready_test.go b/test/e2e/channel_subscription_ready_test.go new file mode 100644 index 0000000000..7da9384184 --- /dev/null +++ b/test/e2e/channel_subscription_ready_test.go @@ -0,0 +1,32 @@ +// +build e2e +// +build consolidated + +/* +Copyright 2021 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + "context" + "testing" + + "knative.dev/eventing-kafka/test/e2e/helpers" +) + +func TestChannelSubscriptionScaleReadyV1(t *testing.T) { + t.Skipf("Skipping test due to flakiness.") + helpers.ChannelSubscriptionScaleReadyHelper(context.Background(), t, channelTestRunner) +} diff --git a/test/e2e/helpers/channel_subscription_ready_test_helper.go b/test/e2e/helpers/channel_subscription_ready_test_helper.go new file mode 100644 index 0000000000..0dafbb919b --- /dev/null +++ b/test/e2e/helpers/channel_subscription_ready_test_helper.go @@ -0,0 +1,141 @@ +/* +Copyright 2021 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package helpers + +import ( + "context" + "fmt" + "testing" + + cloudevents "github.com/cloudevents/sdk-go/v2" + . "github.com/cloudevents/sdk-go/v2/test" + "github.com/google/uuid" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + channelsv1beta1 "knative.dev/eventing-kafka/pkg/apis/messaging/v1beta1" + contribtestlib "knative.dev/eventing-kafka/test/lib" + testlib "knative.dev/eventing/test/lib" + "knative.dev/eventing/test/lib/recordevents" + "knative.dev/eventing/test/lib/resources" +) + +const ( + kafkaChannelName = "kafka-sub-ready-channel" + kafkaSub0 = "kafka-sub-0" + kafkaSub1 = "kafka-sub-1" + recordEventsPodName = "e2e-channel-sub-ready-recordevents-pod" + eventSenderName = "e2e-channel-event-sender-pod" +) + +func scaleDispatcherDeployment(ctx context.Context, t *testing.T, desiredReplicas int32, client *testlib.Client) { + dispatcherDeployment, err := client.Kube.AppsV1().Deployments("knative-eventing").Get(ctx, "kafka-ch-dispatcher", metav1.GetOptions{}) + if err != nil { + t.Fatalf("Unable to get kafka-ch-dispatcher deployment: %v", err) + } + if *dispatcherDeployment.Spec.Replicas != desiredReplicas { + desired := dispatcherDeployment.DeepCopy() + *desired.Spec.Replicas = desiredReplicas + _, err := client.Kube.AppsV1().Deployments("knative-eventing").Update(ctx, desired, metav1.UpdateOptions{}) + if err != nil { + t.Fatalf("Unable to update kafka-ch-dispatcher deployment to %d replica: %v", desiredReplicas, err) + } + // we actively do NOT wait for deployments to be ready so we can check state below + } +} + +func readyDispatcherPodsCheck(ctx context.Context, t *testing.T, client *testlib.Client) int32 { + dispatcherDeployment, err := client.Kube.AppsV1().Deployments("knative-eventing").Get(ctx, "kafka-ch-dispatcher", metav1.GetOptions{}) + if err != nil { + t.Fatalf("Unable to get kafka-ch-dispatcher deployment: %v", err) + } + return dispatcherDeployment.Status.ReadyReplicas +} + +func createKafkaChannel(client *testlib.Client, kafkaChannelMeta metav1.TypeMeta, kafkaChannelName string) { + kafkaChannelV1Beta1 := &channelsv1beta1.KafkaChannel{ + ObjectMeta: metav1.ObjectMeta{ + Name: kafkaChannelName, + }, + Spec: channelsv1beta1.KafkaChannelSpec{ + NumPartitions: 3, + }, + } + contribtestlib.CreateKafkaChannelV1Beta1OrFail(client, kafkaChannelV1Beta1) +} + +func ChannelSubscriptionScaleReadyHelper( + ctx context.Context, + t *testing.T, + channelTestRunner testlib.ComponentsTestRunner, + options ...testlib.SetupClientOption) { + + eventSource := fmt.Sprintf("http://%s.svc/", eventSenderName) + + channelTestRunner.RunTests(t, testlib.FeatureBasic, func(st *testing.T, kafkaChannelMeta metav1.TypeMeta) { + client := testlib.Setup(st, true, options...) + defer testlib.TearDown(client) + + scaleDispatcherDeployment(ctx, st, 1, client) + createKafkaChannel(client, kafkaChannelMeta, kafkaChannelName) + client.WaitForResourceReadyOrFail(kafkaChannelName, &kafkaChannelMeta) + + eventTracker, _ := recordevents.StartEventRecordOrFail(ctx, client, recordEventsPodName) + client.CreateSubscriptionOrFail( + kafkaSub0, + kafkaChannelName, + &kafkaChannelMeta, + resources.WithSubscriberForSubscription(recordEventsPodName), + ) + client.WaitForAllTestResourcesReadyOrFail(ctx) + + scaleDispatcherDeployment(ctx, st, 4, client) + client.WaitForResourceReadyOrFail(kafkaSub0, testlib.SubscriptionTypeMeta) //this should still be ready + + client.CreateSubscriptionOrFail( + kafkaSub1, + kafkaChannelName, + &kafkaChannelMeta, + resources.WithSubscriberForSubscription(recordEventsPodName), + ) + for readyDispatcherPodsCheck(ctx, st, client) < 3 { + subObj, err := client.Eventing.MessagingV1().Subscriptions(client.Namespace).Get(ctx, kafkaSub1, metav1.GetOptions{}) + if err != nil { + st.Fatalf("Could not get v1 subscription object %q: %v", subObj.Name, err) + } + if subObj.Status.IsReady() { + st.Fatalf("Subscription: %s, marked ready before dispatcher pods ready", subObj.Name) + } + } + client.WaitForResourceReadyOrFail(kafkaSub1, testlib.SubscriptionTypeMeta) + // send CloudEvent to the first channel + event := cloudevents.NewEvent() + event.SetID("test") + event.SetSource(eventSource) + event.SetType(testlib.DefaultEventType) + + body := fmt.Sprintf(`{"msg":"TestSingleEvent %s"}`, uuid.New().String()) + if err := event.SetData(cloudevents.ApplicationJSON, []byte(body)); err != nil { + st.Fatalf("Cannot set the payload of the event: %s", err.Error()) + } + + client.SendEventToAddressable(ctx, eventSenderName, kafkaChannelName, &kafkaChannelMeta, event) + // verify the logger service receives the event + eventTracker.AssertAtLeast(1, recordevents.MatchEvent( + HasSource(eventSource), + HasData([]byte(body)), + )) + }) +} diff --git a/test/e2e/helpers/kafka_helper.go b/test/e2e/helpers/kafka_helper.go index 012eaa01b7..1464fafc87 100644 --- a/test/e2e/helpers/kafka_helper.go +++ b/test/e2e/helpers/kafka_helper.go @@ -215,7 +215,7 @@ func MustPublishKafkaMessageViaBinding(client *testlib.Client, selector map[stri } } -func MustCreateTopic(client *testlib.Client, clusterName string, clusterNamespace string, topicName string) { +func MustCreateTopic(client *testlib.Client, clusterName, clusterNamespace, topicName string, partitions int) { obj := unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": topicGVR.GroupVersion().String(), @@ -227,7 +227,7 @@ func MustCreateTopic(client *testlib.Client, clusterName string, clusterNamespac }, }, "spec": map[string]interface{}{ - "partitions": 10, + "partitions": partitions, "replicas": 1, }, }, diff --git a/test/e2e/kafka_binding_test.go b/test/e2e/kafka_binding_test.go index 4d9f0d3b1f..2c0c474490 100644 --- a/test/e2e/kafka_binding_test.go +++ b/test/e2e/kafka_binding_test.go @@ -44,7 +44,7 @@ func testKafkaBinding(t *testing.T, version string, messageKey string, messageHe defer testlib.TearDown(client) - helpers.MustCreateTopic(client, kafkaClusterName, kafkaClusterNamespace, kafkaTopicName) + helpers.MustCreateTopic(client, kafkaClusterName, kafkaClusterNamespace, kafkaTopicName, 10) t.Logf("Creating EventRecord") eventTracker, _ := recordevents.StartEventRecordOrFail(context.Background(), client, loggerPodName) diff --git a/test/e2e/kafka_source.go b/test/e2e/kafka_source.go index 1d6d16914c..a6ae662fc1 100644 --- a/test/e2e/kafka_source.go +++ b/test/e2e/kafka_source.go @@ -379,7 +379,7 @@ func testKafkaSource(t *testing.T, name string, version string, messageKey strin t.Fatalf("could not copy secret(%s): %v", kafkaTLSSecret, err) } } - helpers.MustCreateTopic(client, kafkaClusterName, kafkaClusterNamespace, kafkaTopicName) + helpers.MustCreateTopic(client, kafkaClusterName, kafkaClusterNamespace, kafkaTopicName, 10) if len(recordEventPodName) > 63 { recordEventPodName = recordEventPodName[:63] } diff --git a/test/e2e/kafka_source_reconciler_test.go b/test/e2e/kafka_source_reconciler_test.go index 51bf6eb19f..48da13c0a8 100644 --- a/test/e2e/kafka_source_reconciler_test.go +++ b/test/e2e/kafka_source_reconciler_test.go @@ -109,7 +109,7 @@ func testKafkaSourceReconciler(c *testlib.Client, name string, doAction func(c * } func createKafkaSourceWithSinkMissing(c *testlib.Client) { - helpers.MustCreateTopic(c, kafkaClusterName, kafkaClusterNamespace, rtKafkaTopicName) + helpers.MustCreateTopic(c, kafkaClusterName, kafkaClusterNamespace, rtKafkaTopicName, 10) contribtestlib.CreateKafkaSourceV1Beta1OrFail(c, contribresources.KafkaSourceV1Beta1( kafkaBootstrapUrlPlain, diff --git a/test/e2e/kafka_source_test.go b/test/e2e/kafka_source_test.go index 40e3060542..3a30b45d4f 100644 --- a/test/e2e/kafka_source_test.go +++ b/test/e2e/kafka_source_test.go @@ -103,7 +103,7 @@ func testKafkaSourceUpdate(t *testing.T, name string, test updateTest) { defer testlib.TearDown(client) t.Logf("Creating topic: %s\n", defaultKafkaSource.topicName+name) - helpers.MustCreateTopic(client, kafkaClusterName, kafkaClusterNamespace, defaultKafkaSource.topicName+name) + helpers.MustCreateTopic(client, kafkaClusterName, kafkaClusterNamespace, defaultKafkaSource.topicName+name, 10) t.Logf("Copying secrets: %s\n", defaultKafkaSource.topicName+name) _, err := utils.CopySecret(client.Kube.CoreV1(), "knative-eventing", kafkaTLSSecret, client.Namespace, "default") @@ -149,7 +149,7 @@ func testKafkaSourceUpdate(t *testing.T, name string, test updateTest) { t.Fatalf("Unabled to Get kafkasource: %s/%s\n", client.Namespace, kafkaSourceName) } if test.topicName != defaultKafkaSource.topicName { - helpers.MustCreateTopic(client, kafkaClusterName, kafkaClusterNamespace, test.topicName+name) + helpers.MustCreateTopic(client, kafkaClusterName, kafkaClusterNamespace, test.topicName+name, 10) ksObj.Spec.Topics = []string{test.topicName + name} eventSourceName = sourcesv1beta1.KafkaEventSource(client.Namespace, kafkaSourceName, test.topicName+name) } diff --git a/test/lib/creation.go b/test/lib/creation.go index 01e281d2f7..917828b99f 100644 --- a/test/lib/creation.go +++ b/test/lib/creation.go @@ -25,11 +25,41 @@ import ( bindingsv1alpha1 "knative.dev/eventing-kafka/pkg/apis/bindings/v1alpha1" bindingsv1beta1 "knative.dev/eventing-kafka/pkg/apis/bindings/v1beta1" + channelsv1beta1 "knative.dev/eventing-kafka/pkg/apis/messaging/v1beta1" sourcesv1alpha1 "knative.dev/eventing-kafka/pkg/apis/sources/v1alpha1" sourcesv1beta1 "knative.dev/eventing-kafka/pkg/apis/sources/v1beta1" kafkaclientset "knative.dev/eventing-kafka/pkg/client/clientset/versioned" ) +func CreateKafkaChannelV1Beta1OrFail(c *testlib.Client, kafkaChannel *channelsv1beta1.KafkaChannel) { + kafkaChannelClientSet, err := kafkaclientset.NewForConfig(c.Config) + if err != nil { + c.T.Fatalf("Failed to create v1beta1 KafkaChannel client: %v", err) + } + + kChannels := kafkaChannelClientSet.MessagingV1beta1().KafkaChannels(c.Namespace) + if createdKafkaChannel, err := kChannels.Create(context.Background(), kafkaChannel, metav1.CreateOptions{}); err != nil { + c.T.Fatalf("Failed to create v1beta1 KafkaChannel %q: %v", kafkaChannel.Name, err) + } else { + c.Tracker.AddObj(createdKafkaChannel) + } +} + +func GetKafkaChannelV1Beta1OrFail(c *testlib.Client, kafkaChannel string) *channelsv1beta1.KafkaChannel { + kafkaChannelClientSet, err := kafkaclientset.NewForConfig(c.Config) + if err != nil { + c.T.Fatalf("Failed to create v1beta1 KafkaChannel client: %v", err) + } + + kChannels := kafkaChannelClientSet.MessagingV1beta1().KafkaChannels(c.Namespace) + if kcObj, err := kChannels.Get(context.Background(), kafkaChannel, metav1.GetOptions{}); err != nil { + c.T.Fatalf("Failed to get v1beta1 KafkaChannel %q: %v", kafkaChannel, err) + } else { + return kcObj + } + return nil +} + func CreateKafkaSourceV1Alpha1OrFail(c *testlib.Client, kafkaSource *sourcesv1alpha1.KafkaSource) { kafkaSourceClientSet, err := kafkaclientset.NewForConfig(c.Config) if err != nil { diff --git a/test/lib/setupclientoptions/sources.go b/test/lib/setupclientoptions/sources.go index a437ae7b03..9f69f18105 100644 --- a/test/lib/setupclientoptions/sources.go +++ b/test/lib/setupclientoptions/sources.go @@ -42,7 +42,7 @@ func KafkaSourceV1B1ClientSetupOption(name string, kafkaClusterName string, kafk consumerGroup = uuid.New().String() ) - helpers.MustCreateTopic(client, kafkaClusterName, kafkaClusterNamespace, kafkaTopicName) + helpers.MustCreateTopic(client, kafkaClusterName, kafkaClusterNamespace, kafkaTopicName, 10) recordevents.StartEventRecordOrFail(context.Background(), client, recordEventsPodName) diff --git a/test/test_images/wathola-kafka-sender/main.go b/test/test_images/wathola-kafka-sender/main.go new file mode 100644 index 0000000000..9f645098c1 --- /dev/null +++ b/test/test_images/wathola-kafka-sender/main.go @@ -0,0 +1,35 @@ +/* +Copyright 2021 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "go.uber.org/zap" + "knative.dev/eventing-kafka/test/upgrade/continual" + "knative.dev/eventing/test/upgrade/prober/wathola/sender" + "knative.dev/pkg/signals" +) + +func main() { + ctx := signals.NewContext() + log, err := zap.NewDevelopment() + if err != nil { + panic(err) + } + es := continual.CreateKafkaSender(ctx, log.Sugar()) + sender.RegisterEventSender(es) + sender.New().SendContinually() +} diff --git a/test/test_images/wathola-kafka-sender/pod.yaml b/test/test_images/wathola-kafka-sender/pod.yaml new file mode 100644 index 0000000000..5125c4c61d --- /dev/null +++ b/test/test_images/wathola-kafka-sender/pod.yaml @@ -0,0 +1,10 @@ +# This file is required to trigger ko resolve in upload-test-images.sh + +apiVersion: v1 +kind: Pod +metadata: + name: wathola-kafka-sender +spec: + containers: + - name: wathola-kafka-sender + image: ko://knative.dev/eventing-kafka/test/test_images/wathola-kafka-sender diff --git a/test/upgrade/config.toml b/test/upgrade/config.toml deleted file mode 100644 index 7b6514a2ee..0000000000 --- a/test/upgrade/config.toml +++ /dev/null @@ -1,11 +0,0 @@ -# logLevel = 5 # DEBUG(5) -[sender] -address = '{{- .BrokerURL -}}' -interval = {{ .Config.Interval.Nanoseconds }} - -[forwarder] -target = 'http://wathola-receiver.{{- .Config.Namespace -}}.svc.cluster.local' - -[receiver] - [receiver.errors] - unavailablePeriodToReport = 35_000_000_000 # 35sec in nsec. diff --git a/test/upgrade/continual.go b/test/upgrade/continual.go index a2fc74f340..a76f5b8b44 100644 --- a/test/upgrade/continual.go +++ b/test/upgrade/continual.go @@ -17,181 +17,25 @@ limitations under the License. package upgrade import ( - "context" - "fmt" - "time" - - "github.com/kelseyhightower/envconfig" - "github.com/stretchr/testify/assert" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - eventingduckv1beta1 "knative.dev/eventing/pkg/apis/duck/v1beta1" - testlib "knative.dev/eventing/test/lib" - "knative.dev/eventing/test/lib/resources" - "knative.dev/eventing/test/upgrade/prober" + "knative.dev/eventing-kafka/test/upgrade/continual" pkgupgrade "knative.dev/pkg/test/upgrade" ) -var ( - defaultCustomizeConfig = func(config *prober.Config) { - config.FailOnErrors = true - config.Interval = 2 * time.Millisecond - config.BrokerOpts = append(config.BrokerOpts, - resources.WithDeliveryForBrokerV1Beta1(&eventingduckv1beta1.DeliverySpec{ - Retry: &retryCount, - BackoffPolicy: &backoffPolicy, - BackoffDelay: &backoffDelay, - })) - } - retryCount = int32(12) - backoffPolicy = eventingduckv1beta1.BackoffPolicyExponential - backoffDelay = "PT1S" -) - -// ChannelContinualTest tests channel operation in continual manner during the -// whole upgrade and downgrade process asserting that all event are propagated -// well. -func ChannelContinualTest(opts ContinualTestOptions) pkgupgrade.BackgroundOperation { - opts = fillInDefaults(opts) - ctx := context.Background() - var client *testlib.Client - var probe prober.Prober - setup := func(c pkgupgrade.Context) { - // setup - client = testlib.Setup(c.T, false) - opts.SetKafkaChannelAsDefault(KafkaChannelAsDefaultOptions{ - UpgradeCtx: c, - Ctx: ctx, - Client: client, - ReplicationOptions: *opts.ReplicationOptions, - RetryOptions: *opts.RetryOptions, - }) - config := prober.NewConfig(client.Namespace) - // TODO: knative/eventing#5176 - this is cumbersome - config.ConfigTemplate = "../../../../../../test/upgrade/config.toml" - opts.ConfigCustomize(config) - // envconfig.Process invocation is repeated from within prober.NewConfig to - // make sure every knob is configurable, but using defaults from Eventing - // Kafka instead of Core. The prefix is also changed. - err := envconfig.Process("eventing_kafka_source_upgrade_tests", config) - assert.NoError(c.T, err) - - probe = prober.RunEventProber(ctx, c.Log, client, config) - } - verify := func(c pkgupgrade.Context) { - // verify - if client != nil { - defer testlib.TearDown(client) - } - if probe != nil { - prober.AssertEventProber(ctx, c.T, probe) - } +// ChannelContinualTests returns background operations to test channel +// functionality in continual manner during the whole upgrade and downgrade +// process asserting that all events are propagated well. +func ChannelContinualTests(opts continual.ChannelTestOptions) []pkgupgrade.BackgroundOperation { + return []pkgupgrade.BackgroundOperation{ + continual.ChannelTest(opts), + continual.BrokerBackedByChannelTest(opts), } - return pkgupgrade.NewBackgroundVerification( - "ChannelContinualTest", setup, verify) } -// SourceContinualTest tests source operation in continual manner during the -// whole upgrade and downgrade process asserting that all event are propagated +// SourceContinualTests tests source operation in continual manner during the +// whole upgrade and downgrade process asserting that all events are propagated // well. -func SourceContinualTest(_ ContinualTestOptions) pkgupgrade.BackgroundOperation { - setup := func(c pkgupgrade.Context) { - // TODO: not yet implemented - c.Log.Warn("TODO: not yet implemented") - } - verify := func(c pkgupgrade.Context) { - // TODO: not yet implemented - c.Log.Warn("TODO: not yet implemented") - } - return pkgupgrade.NewBackgroundVerification( - "SourceContinualTest", setup, verify) -} - -// ReplicationOptions hold options for replication. -type ReplicationOptions struct { - NumPartitions int - ReplicationFactor int -} - -// RetryOptions holds options for retries. -type RetryOptions struct { - RetryCount int - BackoffPolicy eventingduckv1beta1.BackoffPolicyType - BackoffDelay string -} - -// ContinualTestOptions holds options for EventingKafka continual tests. -type ContinualTestOptions struct { - ConfigCustomize func(config *prober.Config) - SetKafkaChannelAsDefault func(ctx KafkaChannelAsDefaultOptions) - *ReplicationOptions - *RetryOptions -} - -// KafkaChannelAsDefaultOptions holds a options to run SetKafkaChannelAsDefault func. -type KafkaChannelAsDefaultOptions struct { - UpgradeCtx pkgupgrade.Context - Ctx context.Context - *testlib.Client - ReplicationOptions - RetryOptions -} - -func fillInDefaults(opts ContinualTestOptions) ContinualTestOptions { - o := opts - if opts.ConfigCustomize == nil { - o.ConfigCustomize = defaultCustomizeConfig - } - if opts.SetKafkaChannelAsDefault == nil { - o.SetKafkaChannelAsDefault = configureKafkaChannelAsDefault - } - if opts.RetryOptions == nil { - o.RetryOptions = &RetryOptions{ - RetryCount: int(retryCount), - BackoffPolicy: backoffPolicy, - BackoffDelay: backoffDelay, - } +func SourceContinualTests(opts continual.SourceTestOptions) []pkgupgrade.BackgroundOperation { + return []pkgupgrade.BackgroundOperation{ + continual.SourceTest(opts), } - if opts.ReplicationOptions == nil { - o.ReplicationOptions = &ReplicationOptions{ - NumPartitions: 6, - ReplicationFactor: 3, - } - } - return o -} - -func configureKafkaChannelAsDefault(opts KafkaChannelAsDefaultOptions) { - c := opts.UpgradeCtx - systemNs := "knative-eventing" - configmaps := opts.Client.Kube.CoreV1().ConfigMaps(systemNs) - cm := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: systemNs, - Name: "config-br-default-channel", - }, - Data: map[string]string{ - "channelTemplateSpec": fmt.Sprintf(`apiVersion: %s -kind: %s -spec: - numPartitions: %d - replicationFactor: %d - delivery: - retry: %d - backoffPolicy: %s - backoffDelay: %s`, - defaultChannelType.APIVersion, defaultChannelType.Kind, - opts.NumPartitions, opts.ReplicationFactor, - opts.RetryCount, opts.BackoffPolicy, opts.BackoffDelay, - ), - }, - } - cm, err := configmaps.Update(opts.Ctx, cm, metav1.UpdateOptions{}) - - if !assert.NoError(c.T, err) { - c.T.FailNow() - } - - c.Log.Info("Updated config-br-default-channel in ns knative-eventing"+ - " to eq: ", cm.Data) } diff --git a/test/upgrade/continual/channel-config.toml b/test/upgrade/continual/channel-config.toml new file mode 100644 index 0000000000..99f04dcf15 --- /dev/null +++ b/test/upgrade/continual/channel-config.toml @@ -0,0 +1,7 @@ +# logLevel = 'DEBUG' +[sender] +address = '{{- .Endpoint -}}' +interval = {{ .Config.Interval.Nanoseconds }} + +[forwarder] +target = 'http://wathola-receiver.{{- .Namespace -}}.svc.cluster.local' diff --git a/test/upgrade/continual/channel.go b/test/upgrade/continual/channel.go new file mode 100644 index 0000000000..63210340fa --- /dev/null +++ b/test/upgrade/continual/channel.go @@ -0,0 +1,289 @@ +/* +Copyright 2021 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package continual + +import ( + "fmt" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/eventing-kafka/pkg/apis/messaging/v1beta1" + reconcilertesting "knative.dev/eventing-kafka/pkg/channel/consolidated/reconciler/testing" + testlib "knative.dev/eventing-kafka/test/lib" + eventingduckv1 "knative.dev/eventing/pkg/apis/duck/v1" + eventingduckv1beta1 "knative.dev/eventing/pkg/apis/duck/v1beta1" + messagingv1beta1 "knative.dev/eventing/pkg/apis/messaging/v1beta1" + "knative.dev/eventing/test/lib/duck" + "knative.dev/eventing/test/lib/resources" + "knative.dev/eventing/test/upgrade/prober/sut" + "knative.dev/pkg/apis" + duckv1 "knative.dev/pkg/apis/duck/v1" + pkgupgrade "knative.dev/pkg/test/upgrade" +) + +const ( + channelConfigTemplatePath = "test/upgrade/continual/channel-config.toml" + defaultRetryCount = 12 + defaultBackoffPolicy = eventingduckv1beta1.BackoffPolicyExponential + defaultBackoffDelay = "PT1S" +) + +var ( + defaultChannelType = metav1.TypeMeta{ + APIVersion: "messaging.knative.dev/v1beta1", + Kind: "KafkaChannel", + } +) + +// ChannelTestOptions holds test options for KafkaChannel tests. +type ChannelTestOptions struct { + *TestOptions + *ReplicationOptions + *RetryOptions + *metav1.TypeMeta +} + +// ChannelTest tests channel operation in continual manner during the +// whole upgrade and downgrade process asserting that all event are propagated +// well. +func ChannelTest(opts ChannelTestOptions) pkgupgrade.BackgroundOperation { + opts = opts.withDefaults() + return continualVerification( + "ChannelContinualTest", + opts.TestOptions, + channelSut(opts), + channelConfigTemplatePath, + ) +} + +// BrokerBackedByChannelTest tests a broker backed by KafkaChannel operation in +// continual manner during the whole upgrade and downgrade process asserting +// that all event are propagated well. +func BrokerBackedByChannelTest(opts ChannelTestOptions) pkgupgrade.BackgroundOperation { + opts = opts.withDefaults() + return continualVerification( + "BrokerBackedByChannelContinualTest", + opts.TestOptions, + brokerBackedByChannelSut(opts), + channelConfigTemplatePath, + ) +} + +func (o ChannelTestOptions) withDefaults() ChannelTestOptions { + cto := o + if cto.TestOptions == nil { + cto.TestOptions = &TestOptions{} + } + if cto.TypeMeta == nil { + cto.TypeMeta = &defaultChannelType + } + if cto.RetryOptions == nil { + cto.RetryOptions = defaultRetryOptions() + } + if cto.ReplicationOptions == nil { + cto.ReplicationOptions = defaultReplicationOptions() + } + return cto +} + +func channelSut(opts ChannelTestOptions) sut.SystemUnderTest { + return &kafkaChannelSut{ + channelTypeMeta: opts.TypeMeta, + ReplicationOptions: opts.ReplicationOptions, + RetryOptions: opts.RetryOptions, + } +} + +func defaultReplicationOptions() *ReplicationOptions { + return &ReplicationOptions{ + NumPartitions: 6, + ReplicationFactor: 3, + } +} + +func defaultRetryOptions() *RetryOptions { + return &RetryOptions{ + RetryCount: defaultRetryCount, + BackoffPolicy: defaultBackoffPolicy, + BackoffDelay: defaultBackoffDelay, + } +} + +func brokerBackedByChannelSut(opts ChannelTestOptions) sut.SystemUnderTest { + return &brokerBackedByKafkaChannelSut{ + channelTypeMeta: opts.TypeMeta, + ReplicationOptions: opts.ReplicationOptions, + RetryOptions: opts.RetryOptions, + } +} + +type kafkaChannelSut struct { + channelTypeMeta *metav1.TypeMeta + *ReplicationOptions + *RetryOptions +} + +func (k kafkaChannelSut) Deploy(ctx sut.Context, destination duckv1.Destination) interface{} { + c := ctx.Client + name := "sut" + ch := reconcilertesting.NewKafkaChannel( + name, + c.Namespace, + k.RetryOptions.channelOption(), + k.ReplicationOptions.channelOption(), + ) + testlib.CreateKafkaChannelV1Beta1OrFail(c, ch) + metaResource := resources.NewMetaResource(name, c.Namespace, k.channelTypeMeta) + if err := duck.WaitForResourceReady(c.Dynamic, metaResource); err != nil { + c.T.Fatal(err) + } + + var sutUrl *apis.URL + if u, err := c.GetAddressableURI(name, k.channelTypeMeta); err != nil { + c.T.Fatal(err) + } else { + sutUrl, err = apis.ParseURL(u) + if err != nil { + c.T.Fatal(err) + } + } + + c.CreateSubscriptionOrFail( + name, name, k.channelTypeMeta, + withDestinationForSubscription(&destination), + k.RetryOptions.subscriptionOption(), + ) + + return sutUrl +} + +func withDestinationForSubscription(destination *duckv1.Destination) resources.SubscriptionOptionV1Beta1 { + return func(subscription *messagingv1beta1.Subscription) { + subscription.Spec.Subscriber = destination + } +} + +func (ro RetryOptions) subscriptionOption() resources.SubscriptionOptionV1Beta1 { + return func(subscription *messagingv1beta1.Subscription) { + ensureSubscriptionHasDelivery(subscription) + r := int32(ro.RetryCount) + subscription.Spec.Delivery.Retry = &r + subscription.Spec.Delivery.BackoffPolicy = &ro.BackoffPolicy + subscription.Spec.Delivery.BackoffDelay = &ro.BackoffDelay + } +} + +func (ro RetryOptions) channelOption() reconcilertesting.KafkaChannelOption { + return func(channel *v1beta1.KafkaChannel) { + ensureChannelHasDelivery(channel) + r := int32(ro.RetryCount) + channel.Spec.Delivery.Retry = &r + policy := toV1BackoffPolicy(ro.BackoffPolicy) + channel.Spec.Delivery.BackoffPolicy = &policy + channel.Spec.Delivery.BackoffDelay = &ro.BackoffDelay + } +} + +// toV1BackoffPolicy is required as beta1 isn't used in subscription but is in +// other types. This will be removed. +func toV1BackoffPolicy(pt eventingduckv1beta1.BackoffPolicyType) eventingduckv1.BackoffPolicyType { + switch pt { + case eventingduckv1beta1.BackoffPolicyLinear: + return eventingduckv1.BackoffPolicyLinear + case eventingduckv1beta1.BackoffPolicyExponential: + return eventingduckv1.BackoffPolicyExponential + } + panic("can't get to here") +} + +func (ro ReplicationOptions) channelOption() reconcilertesting.KafkaChannelOption { + return func(channel *v1beta1.KafkaChannel) { + channel.Spec.ReplicationFactor = int16(ro.ReplicationFactor) + channel.Spec.NumPartitions = int32(ro.NumPartitions) + } +} + +func ensureChannelHasDelivery(channel *v1beta1.KafkaChannel) { + if channel.Spec.Delivery == nil { + channel.Spec.Delivery = &eventingduckv1.DeliverySpec{} + } +} + +func ensureSubscriptionHasDelivery(subscription *messagingv1beta1.Subscription) { + if subscription.Spec.Delivery == nil { + subscription.Spec.Delivery = &eventingduckv1beta1.DeliverySpec{} + } +} + +type brokerBackedByKafkaChannelSut struct { + *ReplicationOptions + *RetryOptions + channelTypeMeta *metav1.TypeMeta + defaultSut sut.SystemUnderTest +} + +func (b *brokerBackedByKafkaChannelSut) Deploy( + ctx sut.Context, + destination duckv1.Destination, +) interface{} { + b.setKafkaAsDefaultForBroker(ctx) + + b.defaultSut = sut.NewDefault() + return b.defaultSut.Deploy(ctx, destination) +} + +func (b *brokerBackedByKafkaChannelSut) Teardown(ctx sut.Context) { + if b.defaultSut == nil { + ctx.T.Fatal("default SUT isn't set!?!") + } + if tr, ok := b.defaultSut.(sut.HasTeardown); ok { + tr.Teardown(ctx) + } +} + +func (b *brokerBackedByKafkaChannelSut) setKafkaAsDefaultForBroker(ctx sut.Context) { + systemNs := "knative-eventing" + configmaps := ctx.Client.Kube.CoreV1().ConfigMaps(systemNs) + cm := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: systemNs, + Name: "config-br-default-channel", + }, + Data: map[string]string{ + "channelTemplateSpec": fmt.Sprintf(`apiVersion: %s +kind: %s +spec: + numPartitions: %d + replicationFactor: %d + delivery: + retry: %d + backoffPolicy: %s + backoffDelay: %s`, + b.channelTypeMeta.APIVersion, b.channelTypeMeta.Kind, + b.NumPartitions, b.ReplicationFactor, + b.RetryCount, b.BackoffPolicy, b.BackoffDelay, + ), + }, + } + cm, err := configmaps.Update(ctx.Ctx, cm, metav1.UpdateOptions{}) + if err != nil { + ctx.T.Fatal(err) + } + + ctx.Log.Info("Updated config-br-default-channel in ns knative-eventing"+ + " to eq: ", cm.Data) +} diff --git a/test/upgrade/continual/kafkasender.go b/test/upgrade/continual/kafkasender.go new file mode 100644 index 0000000000..dbf9b77257 --- /dev/null +++ b/test/upgrade/continual/kafkasender.go @@ -0,0 +1,133 @@ +/* +Copyright 2021 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package continual + +import ( + "context" + "errors" + "fmt" + "strings" + + "github.com/Shopify/sarama" + protocolkafka "github.com/cloudevents/sdk-go/protocol/kafka_sarama/v2" + cloudevents "github.com/cloudevents/sdk-go/v2" + "github.com/cloudevents/sdk-go/v2/binding" + "go.uber.org/zap" + "knative.dev/eventing-kafka/pkg/common/client" + "knative.dev/eventing/test/upgrade/prober/wathola/sender" +) + +var ( + // ErrIllegalEndpointFormat if given endpoint structure is illegal and can't + // be used. + ErrIllegalEndpointFormat = errors.New( + "illegal format for Kafka topic endpoint") + + // ErrCantConnectToKafka if connection to kafka can't be established. + ErrCantConnectToKafka = errors.New( + "unable to connect to Kafka bootstrap servers") + + // ErrCantSend if event can't be sent to given Kafka topic + ErrCantSend = errors.New("can't send event to kafka topic") +) + +// CreateKafkaSender will create a wathola sender that sends events to Kafka +// topic directly. +func CreateKafkaSender(ctx context.Context, log *zap.SugaredLogger) sender.EventSender { + return &kafkaSender{ + ctx: ctx, + log: log, + } +} + +func (k *kafkaSender) Supports(endpoint interface{}) bool { + switch endpoint.(type) { + case map[string]interface{}: + _, err := castAsTopicEndpoint(endpoint) + return err == nil + default: + return false + } +} + +func (k *kafkaSender) SendEvent(ce cloudevents.Event, rawEndpoint interface{}) error { + endpoint, err := castAsTopicEndpoint(rawEndpoint) + if err != nil { + // this should never happen, as Supports func should be called first. + return err + } + conf, err := client.NewConfigBuilder(). + WithClientId("continualtests-kafka-sender"). + WithDefaults(). + Build(k.ctx) + if err != nil { + return err + } + producer, err := sarama.NewSyncProducer(endpoint.bootstrapServersSlice(), conf) + if err != nil { + return fmt.Errorf("%w: %v", ErrCantConnectToKafka, err) + } + message := binding.ToMessage(&ce) + kafkaProducerMessage := &sarama.ProducerMessage{ + Topic: endpoint.TopicName, + } + transformers := make([]binding.Transformer, 0) + err = protocolkafka.WriteProducerMessage(k.ctx, message, kafkaProducerMessage, transformers...) + if err != nil { + return fmt.Errorf("%w: %v", ErrCantSend, err) + } + part, offset, err := producer.SendMessage(kafkaProducerMessage) + if err != nil { + return fmt.Errorf("%w: %v", ErrCantSend, err) + } + k.log.Infof("Event %s has been send to kafka topic %s (partition: %d, offset: %d)", + ce.ID(), endpoint.TopicName, part, offset) + return nil +} + +type kafkaSender struct { + log *zap.SugaredLogger + ctx context.Context +} + +type kafkaTopicEndpoint struct { + BootstrapServers string + TopicName string +} + +func (e kafkaTopicEndpoint) bootstrapServersSlice() []string { + return strings.Split(e.BootstrapServers, ",") +} + +func castAsTopicEndpoint(endpoint interface{}) (kafkaTopicEndpoint, error) { + m, ok := endpoint.(map[string]interface{}) + if !ok { + return kafkaTopicEndpoint{}, ErrIllegalEndpointFormat + } + servers := m["bootstrapServers"] + if servers == "" { + return kafkaTopicEndpoint{}, ErrIllegalEndpointFormat + } + topic := m["topicName"] + if topic == "" { + return kafkaTopicEndpoint{}, ErrIllegalEndpointFormat + } + return kafkaTopicEndpoint{ + BootstrapServers: fmt.Sprintf("%v", servers), + TopicName: fmt.Sprintf("%v", topic), + }, nil +} diff --git a/test/upgrade/continual/source-config.toml b/test/upgrade/continual/source-config.toml new file mode 100644 index 0000000000..0276bcd107 --- /dev/null +++ b/test/upgrade/continual/source-config.toml @@ -0,0 +1,9 @@ +# logLevel = 'DEBUG' +[sender] +interval = {{ .Config.Interval.Nanoseconds }} + [sender.address] + bootstrapServers = '{{- .Endpoint.BootstrapServers -}}' + topicName = '{{- .Endpoint.TopicName -}}' + +[forwarder] +target = 'http://wathola-receiver.{{- .Namespace -}}.svc.cluster.local' diff --git a/test/upgrade/continual/source.go b/test/upgrade/continual/source.go new file mode 100644 index 0000000000..ff771ee99c --- /dev/null +++ b/test/upgrade/continual/source.go @@ -0,0 +1,139 @@ +/* +Copyright 2021 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package continual + +import ( + "fmt" + "strings" + + "github.com/google/uuid" + corev1 "k8s.io/api/core/v1" + "knative.dev/eventing-kafka/test/e2e/helpers" + contribtestlib "knative.dev/eventing-kafka/test/lib" + contribresources "knative.dev/eventing-kafka/test/lib/resources" + "knative.dev/eventing/test/upgrade/prober" + "knative.dev/eventing/test/upgrade/prober/sut" + duckv1 "knative.dev/pkg/apis/duck/v1" + pkgTest "knative.dev/pkg/test" + pkgupgrade "knative.dev/pkg/test/upgrade" +) + +const ( + defaultKafkaBootstrapPort = 9092 + defaultKafkaClusterName = "my-cluster" + defaultKafkaClusterNamespace = "kafka" + sourceConfigTemplatePath = "test/upgrade/continual/source-config.toml" +) + +// SourceTestOptions holds test options for KafkaSource tests. +type SourceTestOptions struct { + *TestOptions + *KafkaCluster +} + +// SourceTest tests source operation in continual manner during the +// whole upgrade and downgrade process asserting that all event are propagated +// well. +func SourceTest(opts SourceTestOptions) pkgupgrade.BackgroundOperation { + opts = opts.withDefaults() + return continualVerification( + "SourceContinualTests", + opts.TestOptions, + &kafkaSourceSut{KafkaCluster: opts.KafkaCluster}, + sourceConfigTemplatePath, + ) +} + +func (o SourceTestOptions) withDefaults() SourceTestOptions { + sto := o + if sto.TestOptions == nil { + sto.TestOptions = &TestOptions{} + } + if sto.KafkaCluster == nil { + sto.KafkaCluster = &KafkaCluster{} + } + c := sto.KafkaCluster.withDefaults() + sto.KafkaCluster = &c + sto.Configurators = append([]prober.Configurator{ + func(config *prober.Config) error { + config.Wathola.ImageResolver = kafkaSourceSenderImageResolver + return nil + }, + }, sto.Configurators...) + return sto +} + +func (c KafkaCluster) withDefaults() KafkaCluster { + kc := c + if kc.Name == "" { + kc.Name = defaultKafkaClusterName + } + if kc.Namespace == "" { + kc.Namespace = defaultKafkaClusterNamespace + } + if len(kc.BootstrapServers) == 0 { + kc.BootstrapServers = []string{ + fmt.Sprintf("%s-kafka-bootstrap.%s.svc:%d", + kc.Name, + kc.Namespace, + defaultKafkaBootstrapPort, + ), + } + } + return kc +} + +func kafkaSourceSenderImageResolver(component string) string { + if component == "wathola-sender" { + // replacing the original image with modified one from this repo + component = "wathola-kafka-sender" + } + return pkgTest.ImagePath(component) +} + +type kafkaSourceSut struct { + *KafkaCluster +} + +func (k kafkaSourceSut) Deploy(ctx sut.Context, destination duckv1.Destination) interface{} { + topicName := uuid.NewString() + c := k.KafkaCluster + helpers.MustCreateTopic(ctx.Client, c.Name, c.Namespace, + topicName, 6) + contribtestlib.CreateKafkaSourceV1Beta1OrFail(ctx.Client, contribresources.KafkaSourceV1Beta1( + c.serversLine(), + topicName, + toObjectReference(destination), + )) + return kafkaTopicEndpoint{ + BootstrapServers: k.KafkaCluster.serversLine(), + TopicName: topicName, + } +} + +func (c KafkaCluster) serversLine() string { + return strings.Join(c.BootstrapServers, ",") +} + +func toObjectReference(destination duckv1.Destination) *corev1.ObjectReference { + return &corev1.ObjectReference{ + APIVersion: destination.Ref.APIVersion, + Kind: destination.Ref.Kind, + Namespace: destination.Ref.Namespace, + Name: destination.Ref.Name, + } +} diff --git a/test/upgrade/continual/types.go b/test/upgrade/continual/types.go new file mode 100644 index 0000000000..fe1c1f87e6 --- /dev/null +++ b/test/upgrade/continual/types.go @@ -0,0 +1,49 @@ +/* +Copyright 2021 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package continual + +import ( + eventingduckv1beta1 "knative.dev/eventing/pkg/apis/duck/v1beta1" + "knative.dev/eventing/test/upgrade/prober" + "knative.dev/eventing/test/upgrade/prober/sut" +) + +// ReplicationOptions hold options for replication. +type ReplicationOptions struct { + NumPartitions int + ReplicationFactor int +} + +// RetryOptions holds options for retries. +type RetryOptions struct { + RetryCount int + BackoffPolicy eventingduckv1beta1.BackoffPolicyType + BackoffDelay string +} + +// KafkaCluster represents Kafka cluster endpoint. +type KafkaCluster struct { + BootstrapServers []string + Name string + Namespace string +} + +// TestOptions holds options for EventingKafka continual tests. +type TestOptions struct { + prober.ContinualVerificationOptions + SUTs map[string]sut.SystemUnderTest +} diff --git a/test/upgrade/continual/verification.go b/test/upgrade/continual/verification.go new file mode 100644 index 0000000000..914ab41260 --- /dev/null +++ b/test/upgrade/continual/verification.go @@ -0,0 +1,84 @@ +/* +Copyright 2021 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package continual + +import ( + "fmt" + + "github.com/kelseyhightower/envconfig" + "knative.dev/eventing/test/upgrade/prober" + "knative.dev/eventing/test/upgrade/prober/sut" + pkgupgrade "knative.dev/pkg/test/upgrade" +) + +func continualVerification( + testName string, + opts *TestOptions, + defaultSut sut.SystemUnderTest, + configTemplate string, +) pkgupgrade.BackgroundOperation { + return prober.NewContinualVerification( + testName, + verificationOptions( + opts, + resolveSut(testName, opts, defaultSut), + configTemplate, + ), + ) +} + +func resolveSut( + testname string, + opts *TestOptions, + defaultSut sut.SystemUnderTest, +) sut.SystemUnderTest { + var resolved sut.SystemUnderTest + if opts.SUTs != nil { + resolved = opts.SUTs[testname] + } + if resolved == nil { + resolved = defaultSut + } + return resolved +} + +func verificationOptions( + opts *TestOptions, + theSut sut.SystemUnderTest, + configTemplate string, +) prober.ContinualVerificationOptions { + return prober.ContinualVerificationOptions{ + Configurators: append( + opts.Configurators, + configurator(theSut, configTemplate), + ), + ClientOptions: opts.ClientOptions, + } +} + +func configurator(theSut sut.SystemUnderTest, configTemplate string) prober.Configurator { + return func(config *prober.Config) error { + config.SystemUnderTest = theSut + // TODO: knative/eventing#5176 - this is cumbersome + config.ConfigTemplate = fmt.Sprintf("../../../../../../%s", + configTemplate) + // envconfig.Process invocation is repeated from within prober.NewConfig to + // make sure every knob is configurable, but using defaults from Eventing + // Kafka instead of Core. The prefix is also changed. + return envconfig.Process("eventing_kafka_upgrade_tests", config) + } +} diff --git a/test/upgrade/suite.go b/test/upgrade/suite.go index 7795957609..fd32b0655d 100644 --- a/test/upgrade/suite.go +++ b/test/upgrade/suite.go @@ -17,6 +17,7 @@ limitations under the License. package upgrade import ( + "knative.dev/eventing-kafka/test/upgrade/continual" "knative.dev/eventing-kafka/test/upgrade/installation" pkgupgrade "knative.dev/pkg/test/upgrade" ) @@ -37,10 +38,10 @@ func Suite() pkgupgrade.Suite { ChannelPostDowngradeTest(), SourcePostDowngradeTest(), }, - Continual: []pkgupgrade.BackgroundOperation{ - ChannelContinualTest(ContinualTestOptions{}), - SourceContinualTest(ContinualTestOptions{}), - }, + Continual: merge( + ChannelContinualTests(continual.ChannelTestOptions{}), + SourceContinualTests(continual.SourceTestOptions{}), + ), }, Installations: pkgupgrade.Installations{ Base: []pkgupgrade.Operation{ @@ -56,3 +57,15 @@ func Suite() pkgupgrade.Suite { }, } } + +func merge(slices ...[]pkgupgrade.BackgroundOperation) []pkgupgrade.BackgroundOperation { + l := 0 + for _, slice := range slices { + l += len(slice) + } + result := make([]pkgupgrade.BackgroundOperation, 0, l) + for _, slice := range slices { + result = append(result, slice...) + } + return result +} diff --git a/vendor/github.com/pelletier/go-toml/LICENSE b/third_party/VENDOR-LICENSE/github.com/pelletier/go-toml/v2/LICENSE similarity index 94% rename from vendor/github.com/pelletier/go-toml/LICENSE rename to third_party/VENDOR-LICENSE/github.com/pelletier/go-toml/v2/LICENSE index 583bdae628..3a38ac28ba 100644 --- a/vendor/github.com/pelletier/go-toml/LICENSE +++ b/third_party/VENDOR-LICENSE/github.com/pelletier/go-toml/v2/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2013 - 2017 Thomas Pelletier, Eric Anderton +Copyright (c) 2013 - 2021 Thomas Pelletier, Eric Anderton Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendor/github.com/pelletier/go-toml/CONTRIBUTING.md b/vendor/github.com/pelletier/go-toml/CONTRIBUTING.md deleted file mode 100644 index 405c911c90..0000000000 --- a/vendor/github.com/pelletier/go-toml/CONTRIBUTING.md +++ /dev/null @@ -1,132 +0,0 @@ -## Contributing - -Thank you for your interest in go-toml! We appreciate you considering -contributing to go-toml! - -The main goal is the project is to provide an easy-to-use TOML -implementation for Go that gets the job done and gets out of your way – -dealing with TOML is probably not the central piece of your project. - -As the single maintainer of go-toml, time is scarce. All help, big or -small, is more than welcomed! - -### Ask questions - -Any question you may have, somebody else might have it too. Always feel -free to ask them on the [issues tracker][issues-tracker]. We will try to -answer them as clearly and quickly as possible, time permitting. - -Asking questions also helps us identify areas where the documentation needs -improvement, or new features that weren't envisioned before. Sometimes, a -seemingly innocent question leads to the fix of a bug. Don't hesitate and -ask away! - -### Improve the documentation - -The best way to share your knowledge and experience with go-toml is to -improve the documentation. Fix a typo, clarify an interface, add an -example, anything goes! - -The documentation is present in the [README][readme] and thorough the -source code. On release, it gets updated on [GoDoc][godoc]. To make a -change to the documentation, create a pull request with your proposed -changes. For simple changes like that, the easiest way to go is probably -the "Fork this project and edit the file" button on Github, displayed at -the top right of the file. Unless it's a trivial change (for example a -typo), provide a little bit of context in your pull request description or -commit message. - -### Report a bug - -Found a bug! Sorry to hear that :(. Help us and other track them down and -fix by reporting it. [File a new bug report][bug-report] on the [issues -tracker][issues-tracker]. The template should provide enough guidance on -what to include. When in doubt: add more details! By reducing ambiguity and -providing more information, it decreases back and forth and saves everyone -time. - -### Code changes - -Want to contribute a patch? Very happy to hear that! - -First, some high-level rules: - -* A short proposal with some POC code is better than a lengthy piece of - text with no code. Code speaks louder than words. -* No backward-incompatible patch will be accepted unless discussed. - Sometimes it's hard, and Go's lack of versioning by default does not - help, but we try not to break people's programs unless we absolutely have - to. -* If you are writing a new feature or extending an existing one, make sure - to write some documentation. -* Bug fixes need to be accompanied with regression tests. -* New code needs to be tested. -* Your commit messages need to explain why the change is needed, even if - already included in the PR description. - -It does sound like a lot, but those best practices are here to save time -overall and continuously improve the quality of the project, which is -something everyone benefits from. - -#### Get started - -The fairly standard code contribution process looks like that: - -1. [Fork the project][fork]. -2. Make your changes, commit on any branch you like. -3. [Open up a pull request][pull-request] -4. Review, potential ask for changes. -5. Merge. You're in! - -Feel free to ask for help! You can create draft pull requests to gather -some early feedback! - -#### Run the tests - -You can run tests for go-toml using Go's test tool: `go test ./...`. -When creating a pull requests, all tests will be ran on Linux on a few Go -versions (Travis CI), and on Windows using the latest Go version -(AppVeyor). - -#### Style - -Try to look around and follow the same format and structure as the rest of -the code. We enforce using `go fmt` on the whole code base. - ---- - -### Maintainers-only - -#### Merge pull request - -Checklist: - -* Passing CI. -* Does not introduce backward-incompatible changes (unless discussed). -* Has relevant doc changes. -* Has relevant unit tests. - -1. Merge using "squash and merge". -2. Make sure to edit the commit message to keep all the useful information - nice and clean. -3. Make sure the commit title is clear and contains the PR number (#123). - -#### New release - -1. Go to [releases][releases]. Click on "X commits to master since this - release". -2. Make note of all the changes. Look for backward incompatible changes, - new features, and bug fixes. -3. Pick the new version using the above and semver. -4. Create a [new release][new-release]. -5. Follow the same format as [1.1.0][release-110]. - -[issues-tracker]: https://github.com/pelletier/go-toml/issues -[bug-report]: https://github.com/pelletier/go-toml/issues/new?template=bug_report.md -[godoc]: https://godoc.org/github.com/pelletier/go-toml -[readme]: ./README.md -[fork]: https://help.github.com/articles/fork-a-repo -[pull-request]: https://help.github.com/en/articles/creating-a-pull-request -[releases]: https://github.com/pelletier/go-toml/releases -[new-release]: https://github.com/pelletier/go-toml/releases/new -[release-110]: https://github.com/pelletier/go-toml/releases/tag/v1.1.0 diff --git a/vendor/github.com/pelletier/go-toml/Dockerfile b/vendor/github.com/pelletier/go-toml/Dockerfile deleted file mode 100644 index fffdb01666..0000000000 --- a/vendor/github.com/pelletier/go-toml/Dockerfile +++ /dev/null @@ -1,11 +0,0 @@ -FROM golang:1.12-alpine3.9 as builder -WORKDIR /go/src/github.com/pelletier/go-toml -COPY . . -ENV CGO_ENABLED=0 -ENV GOOS=linux -RUN go install ./... - -FROM scratch -COPY --from=builder /go/bin/tomll /usr/bin/tomll -COPY --from=builder /go/bin/tomljson /usr/bin/tomljson -COPY --from=builder /go/bin/jsontoml /usr/bin/jsontoml diff --git a/vendor/github.com/pelletier/go-toml/Makefile b/vendor/github.com/pelletier/go-toml/Makefile deleted file mode 100644 index 9e4503aea6..0000000000 --- a/vendor/github.com/pelletier/go-toml/Makefile +++ /dev/null @@ -1,29 +0,0 @@ -export CGO_ENABLED=0 -go := go -go.goos ?= $(shell echo `go version`|cut -f4 -d ' '|cut -d '/' -f1) -go.goarch ?= $(shell echo `go version`|cut -f4 -d ' '|cut -d '/' -f2) - -out.tools := tomll tomljson jsontoml -out.dist := $(out.tools:=_$(go.goos)_$(go.goarch).tar.xz) -sources := $(wildcard **/*.go) - - -.PHONY: -tools: $(out.tools) - -$(out.tools): $(sources) - GOOS=$(go.goos) GOARCH=$(go.goarch) $(go) build ./cmd/$@ - -.PHONY: -dist: $(out.dist) - -$(out.dist):%_$(go.goos)_$(go.goarch).tar.xz: % - if [ "$(go.goos)" = "windows" ]; then \ - tar -cJf $@ $^.exe; \ - else \ - tar -cJf $@ $^; \ - fi - -.PHONY: -clean: - rm -rf $(out.tools) $(out.dist) diff --git a/vendor/github.com/pelletier/go-toml/PULL_REQUEST_TEMPLATE.md b/vendor/github.com/pelletier/go-toml/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 041cdc4a2f..0000000000 --- a/vendor/github.com/pelletier/go-toml/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,5 +0,0 @@ -**Issue:** add link to pelletier/go-toml issue here - -Explanation of what this pull request does. - -More detailed description of the decisions being made and the reasons why (if the patch is non-trivial). diff --git a/vendor/github.com/pelletier/go-toml/README.md b/vendor/github.com/pelletier/go-toml/README.md deleted file mode 100644 index 6831deb5bd..0000000000 --- a/vendor/github.com/pelletier/go-toml/README.md +++ /dev/null @@ -1,151 +0,0 @@ -# go-toml - -Go library for the [TOML](https://github.com/mojombo/toml) format. - -This library supports TOML version -[v1.0.0-rc.1](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v1.0.0-rc.1.md) - -[![GoDoc](https://godoc.org/github.com/pelletier/go-toml?status.svg)](http://godoc.org/github.com/pelletier/go-toml) -[![license](https://img.shields.io/github/license/pelletier/go-toml.svg)](https://github.com/pelletier/go-toml/blob/master/LICENSE) -[![Build Status](https://dev.azure.com/pelletierthomas/go-toml-ci/_apis/build/status/pelletier.go-toml?branchName=master)](https://dev.azure.com/pelletierthomas/go-toml-ci/_build/latest?definitionId=1&branchName=master) -[![codecov](https://codecov.io/gh/pelletier/go-toml/branch/master/graph/badge.svg)](https://codecov.io/gh/pelletier/go-toml) -[![Go Report Card](https://goreportcard.com/badge/github.com/pelletier/go-toml)](https://goreportcard.com/report/github.com/pelletier/go-toml) -[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fpelletier%2Fgo-toml.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fpelletier%2Fgo-toml?ref=badge_shield) - -## Features - -Go-toml provides the following features for using data parsed from TOML documents: - -* Load TOML documents from files and string data -* Easily navigate TOML structure using Tree -* Marshaling and unmarshaling to and from data structures -* Line & column position data for all parsed elements -* [Query support similar to JSON-Path](query/) -* Syntax errors contain line and column numbers - -## Import - -```go -import "github.com/pelletier/go-toml" -``` - -## Usage example - -Read a TOML document: - -```go -config, _ := toml.Load(` -[postgres] -user = "pelletier" -password = "mypassword"`) -// retrieve data directly -user := config.Get("postgres.user").(string) - -// or using an intermediate object -postgresConfig := config.Get("postgres").(*toml.Tree) -password := postgresConfig.Get("password").(string) -``` - -Or use Unmarshal: - -```go -type Postgres struct { - User string - Password string -} -type Config struct { - Postgres Postgres -} - -doc := []byte(` -[Postgres] -User = "pelletier" -Password = "mypassword"`) - -config := Config{} -toml.Unmarshal(doc, &config) -fmt.Println("user=", config.Postgres.User) -``` - -Or use a query: - -```go -// use a query to gather elements without walking the tree -q, _ := query.Compile("$..[user,password]") -results := q.Execute(config) -for ii, item := range results.Values() { - fmt.Printf("Query result %d: %v\n", ii, item) -} -``` - -## Documentation - -The documentation and additional examples are available at -[godoc.org](http://godoc.org/github.com/pelletier/go-toml). - -## Tools - -Go-toml provides two handy command line tools: - -* `tomll`: Reads TOML files and lints them. - - ``` - go install github.com/pelletier/go-toml/cmd/tomll - tomll --help - ``` -* `tomljson`: Reads a TOML file and outputs its JSON representation. - - ``` - go install github.com/pelletier/go-toml/cmd/tomljson - tomljson --help - ``` - - * `jsontoml`: Reads a JSON file and outputs a TOML representation. - - ``` - go install github.com/pelletier/go-toml/cmd/jsontoml - jsontoml --help - ``` - -### Docker image - -Those tools are also availble as a Docker image from -[dockerhub](https://hub.docker.com/r/pelletier/go-toml). For example, to -use `tomljson`: - -``` -docker run -v $PWD:/workdir pelletier/go-toml tomljson /workdir/example.toml -``` - -Only master (`latest`) and tagged versions are published to dockerhub. You -can build your own image as usual: - -``` -docker build -t go-toml . -``` - -## Contribute - -Feel free to report bugs and patches using GitHub's pull requests system on -[pelletier/go-toml](https://github.com/pelletier/go-toml). Any feedback would be -much appreciated! - -### Run tests - -`go test ./...` - -### Fuzzing - -The script `./fuzz.sh` is available to -run [go-fuzz](https://github.com/dvyukov/go-fuzz) on go-toml. - -## Versioning - -Go-toml follows [Semantic Versioning](http://semver.org/). The supported version -of [TOML](https://github.com/toml-lang/toml) is indicated at the beginning of -this document. The last two major versions of Go are supported -(see [Go Release Policy](https://golang.org/doc/devel/release.html#policy)). - -## License - -The MIT License (MIT). Read [LICENSE](LICENSE). diff --git a/vendor/github.com/pelletier/go-toml/azure-pipelines.yml b/vendor/github.com/pelletier/go-toml/azure-pipelines.yml deleted file mode 100644 index 242b5b5403..0000000000 --- a/vendor/github.com/pelletier/go-toml/azure-pipelines.yml +++ /dev/null @@ -1,230 +0,0 @@ -trigger: -- master - -stages: -- stage: fuzzit - displayName: "Run Fuzzit" - dependsOn: [] - condition: and(succeeded(), eq(variables['Build.SourceBranchName'], 'master')) - jobs: - - job: submit - displayName: "Submit" - pool: - vmImage: ubuntu-latest - steps: - - task: GoTool@0 - displayName: "Install Go 1.14" - inputs: - version: "1.14" - - script: echo "##vso[task.setvariable variable=PATH]${PATH}:/home/vsts/go/bin/" - - script: mkdir -p ${HOME}/go/src/github.com/pelletier/go-toml - - script: cp -R . ${HOME}/go/src/github.com/pelletier/go-toml - - task: Bash@3 - inputs: - filePath: './fuzzit.sh' - env: - TYPE: fuzzing - FUZZIT_API_KEY: $(FUZZIT_API_KEY) - -- stage: run_checks - displayName: "Check" - dependsOn: [] - jobs: - - job: fmt - displayName: "fmt" - pool: - vmImage: ubuntu-latest - steps: - - task: GoTool@0 - displayName: "Install Go 1.14" - inputs: - version: "1.14" - - task: Go@0 - displayName: "go fmt ./..." - inputs: - command: 'custom' - customCommand: 'fmt' - arguments: './...' - - job: coverage - displayName: "coverage" - pool: - vmImage: ubuntu-latest - steps: - - task: GoTool@0 - displayName: "Install Go 1.14" - inputs: - version: "1.14" - - task: Go@0 - displayName: "Generate coverage" - inputs: - command: 'test' - arguments: "-race -coverprofile=coverage.txt -covermode=atomic" - - task: Bash@3 - inputs: - targetType: 'inline' - script: 'bash <(curl -s https://codecov.io/bash) -t ${CODECOV_TOKEN}' - env: - CODECOV_TOKEN: $(CODECOV_TOKEN) - - job: benchmark - displayName: "benchmark" - pool: - vmImage: ubuntu-latest - steps: - - task: GoTool@0 - displayName: "Install Go 1.14" - inputs: - version: "1.14" - - script: echo "##vso[task.setvariable variable=PATH]${PATH}:/home/vsts/go/bin/" - - task: Bash@3 - inputs: - filePath: './benchmark.sh' - arguments: "master $(Build.Repository.Uri)" - - - job: fuzzing - displayName: "fuzzing" - pool: - vmImage: ubuntu-latest - steps: - - task: GoTool@0 - displayName: "Install Go 1.14" - inputs: - version: "1.14" - - script: echo "##vso[task.setvariable variable=PATH]${PATH}:/home/vsts/go/bin/" - - script: mkdir -p ${HOME}/go/src/github.com/pelletier/go-toml - - script: cp -R . ${HOME}/go/src/github.com/pelletier/go-toml - - task: Bash@3 - inputs: - filePath: './fuzzit.sh' - env: - TYPE: local-regression - - - job: go_unit_tests - displayName: "unit tests" - strategy: - matrix: - linux 1.14: - goVersion: '1.14' - imageName: 'ubuntu-latest' - mac 1.14: - goVersion: '1.14' - imageName: 'macOS-latest' - windows 1.14: - goVersion: '1.14' - imageName: 'windows-latest' - linux 1.13: - goVersion: '1.13' - imageName: 'ubuntu-latest' - mac 1.13: - goVersion: '1.13' - imageName: 'macOS-latest' - windows 1.13: - goVersion: '1.13' - imageName: 'windows-latest' - pool: - vmImage: $(imageName) - steps: - - task: GoTool@0 - displayName: "Install Go $(goVersion)" - inputs: - version: $(goVersion) - - task: Go@0 - displayName: "go test ./..." - inputs: - command: 'test' - arguments: './...' -- stage: build_binaries - displayName: "Build binaries" - dependsOn: run_checks - jobs: - - job: build_binary - displayName: "Build binary" - strategy: - matrix: - linux_amd64: - GOOS: linux - GOARCH: amd64 - darwin_amd64: - GOOS: darwin - GOARCH: amd64 - windows_amd64: - GOOS: windows - GOARCH: amd64 - pool: - vmImage: ubuntu-latest - steps: - - task: GoTool@0 - displayName: "Install Go" - inputs: - version: 1.14 - - task: Bash@3 - inputs: - targetType: inline - script: "make dist" - env: - go.goos: $(GOOS) - go.goarch: $(GOARCH) - - task: CopyFiles@2 - inputs: - sourceFolder: '$(Build.SourcesDirectory)' - contents: '*.tar.xz' - TargetFolder: '$(Build.ArtifactStagingDirectory)' - - task: PublishBuildArtifacts@1 - inputs: - pathtoPublish: '$(Build.ArtifactStagingDirectory)' - artifactName: binaries -- stage: build_binaries_manifest - displayName: "Build binaries manifest" - dependsOn: build_binaries - jobs: - - job: build_manifest - displayName: "Build binaries manifest" - steps: - - task: DownloadBuildArtifacts@0 - inputs: - buildType: 'current' - downloadType: 'single' - artifactName: 'binaries' - downloadPath: '$(Build.SourcesDirectory)' - - task: Bash@3 - inputs: - targetType: inline - script: "cd binaries && sha256sum --binary *.tar.xz | tee $(Build.ArtifactStagingDirectory)/sha256sums.txt" - - task: PublishBuildArtifacts@1 - inputs: - pathtoPublish: '$(Build.ArtifactStagingDirectory)' - artifactName: manifest - -- stage: build_docker_image - displayName: "Build Docker image" - dependsOn: run_checks - jobs: - - job: build - displayName: "Build" - pool: - vmImage: ubuntu-latest - steps: - - task: Docker@2 - inputs: - command: 'build' - Dockerfile: 'Dockerfile' - buildContext: '.' - addPipelineData: false - -- stage: publish_docker_image - displayName: "Publish Docker image" - dependsOn: build_docker_image - condition: and(succeeded(), eq(variables['Build.SourceBranchName'], 'master')) - jobs: - - job: publish - displayName: "Publish" - pool: - vmImage: ubuntu-latest - steps: - - task: Docker@2 - inputs: - containerRegistry: 'DockerHub' - repository: 'pelletier/go-toml' - command: 'buildAndPush' - Dockerfile: 'Dockerfile' - buildContext: '.' - tags: 'latest' diff --git a/vendor/github.com/pelletier/go-toml/benchmark.json b/vendor/github.com/pelletier/go-toml/benchmark.json deleted file mode 100644 index 86f99c6a87..0000000000 --- a/vendor/github.com/pelletier/go-toml/benchmark.json +++ /dev/null @@ -1,164 +0,0 @@ -{ - "array": { - "key1": [ - 1, - 2, - 3 - ], - "key2": [ - "red", - "yellow", - "green" - ], - "key3": [ - [ - 1, - 2 - ], - [ - 3, - 4, - 5 - ] - ], - "key4": [ - [ - 1, - 2 - ], - [ - "a", - "b", - "c" - ] - ], - "key5": [ - 1, - 2, - 3 - ], - "key6": [ - 1, - 2 - ] - }, - "boolean": { - "False": false, - "True": true - }, - "datetime": { - "key1": "1979-05-27T07:32:00Z", - "key2": "1979-05-27T00:32:00-07:00", - "key3": "1979-05-27T00:32:00.999999-07:00" - }, - "float": { - "both": { - "key": 6.626e-34 - }, - "exponent": { - "key1": 5e+22, - "key2": 1000000, - "key3": -0.02 - }, - "fractional": { - "key1": 1, - "key2": 3.1415, - "key3": -0.01 - }, - "underscores": { - "key1": 9224617.445991227, - "key2": 1e+100 - } - }, - "fruit": [{ - "name": "apple", - "physical": { - "color": "red", - "shape": "round" - }, - "variety": [{ - "name": "red delicious" - }, - { - "name": "granny smith" - } - ] - }, - { - "name": "banana", - "variety": [{ - "name": "plantain" - }] - } - ], - "integer": { - "key1": 99, - "key2": 42, - "key3": 0, - "key4": -17, - "underscores": { - "key1": 1000, - "key2": 5349221, - "key3": 12345 - } - }, - "products": [{ - "name": "Hammer", - "sku": 738594937 - }, - {}, - { - "color": "gray", - "name": "Nail", - "sku": 284758393 - } - ], - "string": { - "basic": { - "basic": "I'm a string. \"You can quote me\". Name\tJosé\nLocation\tSF." - }, - "literal": { - "multiline": { - "lines": "The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n", - "regex2": "I [dw]on't need \\d{2} apples" - }, - "quoted": "Tom \"Dubs\" Preston-Werner", - "regex": "\u003c\\i\\c*\\s*\u003e", - "winpath": "C:\\Users\\nodejs\\templates", - "winpath2": "\\\\ServerX\\admin$\\system32\\" - }, - "multiline": { - "continued": { - "key1": "The quick brown fox jumps over the lazy dog.", - "key2": "The quick brown fox jumps over the lazy dog.", - "key3": "The quick brown fox jumps over the lazy dog." - }, - "key1": "One\nTwo", - "key2": "One\nTwo", - "key3": "One\nTwo" - } - }, - "table": { - "inline": { - "name": { - "first": "Tom", - "last": "Preston-Werner" - }, - "point": { - "x": 1, - "y": 2 - } - }, - "key": "value", - "subtable": { - "key": "another value" - } - }, - "x": { - "y": { - "z": { - "w": {} - } - } - } -} diff --git a/vendor/github.com/pelletier/go-toml/benchmark.sh b/vendor/github.com/pelletier/go-toml/benchmark.sh deleted file mode 100644 index 7914fff49c..0000000000 --- a/vendor/github.com/pelletier/go-toml/benchmark.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash - -set -ex - -reference_ref=${1:-master} -reference_git=${2:-.} - -if ! `hash benchstat 2>/dev/null`; then - echo "Installing benchstat" - go get golang.org/x/perf/cmd/benchstat -fi - -tempdir=`mktemp -d /tmp/go-toml-benchmark-XXXXXX` -ref_tempdir="${tempdir}/ref" -ref_benchmark="${ref_tempdir}/benchmark-`echo -n ${reference_ref}|tr -s '/' '-'`.txt" -local_benchmark="`pwd`/benchmark-local.txt" - -echo "=== ${reference_ref} (${ref_tempdir})" -git clone ${reference_git} ${ref_tempdir} >/dev/null 2>/dev/null -pushd ${ref_tempdir} >/dev/null -git checkout ${reference_ref} >/dev/null 2>/dev/null -go test -bench=. -benchmem | tee ${ref_benchmark} -popd >/dev/null - -echo "" -echo "=== local" -go test -bench=. -benchmem | tee ${local_benchmark} - -echo "" -echo "=== diff" -benchstat -delta-test=none ${ref_benchmark} ${local_benchmark} diff --git a/vendor/github.com/pelletier/go-toml/benchmark.toml b/vendor/github.com/pelletier/go-toml/benchmark.toml deleted file mode 100644 index dfd77e0962..0000000000 --- a/vendor/github.com/pelletier/go-toml/benchmark.toml +++ /dev/null @@ -1,244 +0,0 @@ -################################################################################ -## Comment - -# Speak your mind with the hash symbol. They go from the symbol to the end of -# the line. - - -################################################################################ -## Table - -# Tables (also known as hash tables or dictionaries) are collections of -# key/value pairs. They appear in square brackets on a line by themselves. - -[table] - -key = "value" # Yeah, you can do this. - -# Nested tables are denoted by table names with dots in them. Name your tables -# whatever crap you please, just don't use #, ., [ or ]. - -[table.subtable] - -key = "another value" - -# You don't need to specify all the super-tables if you don't want to. TOML -# knows how to do it for you. - -# [x] you -# [x.y] don't -# [x.y.z] need these -[x.y.z.w] # for this to work - - -################################################################################ -## Inline Table - -# Inline tables provide a more compact syntax for expressing tables. They are -# especially useful for grouped data that can otherwise quickly become verbose. -# Inline tables are enclosed in curly braces `{` and `}`. No newlines are -# allowed between the curly braces unless they are valid within a value. - -[table.inline] - -name = { first = "Tom", last = "Preston-Werner" } -point = { x = 1, y = 2 } - - -################################################################################ -## String - -# There are four ways to express strings: basic, multi-line basic, literal, and -# multi-line literal. All strings must contain only valid UTF-8 characters. - -[string.basic] - -basic = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF." - -[string.multiline] - -# The following strings are byte-for-byte equivalent: -key1 = "One\nTwo" -key2 = """One\nTwo""" -key3 = """ -One -Two""" - -[string.multiline.continued] - -# The following strings are byte-for-byte equivalent: -key1 = "The quick brown fox jumps over the lazy dog." - -key2 = """ -The quick brown \ - - - fox jumps over \ - the lazy dog.""" - -key3 = """\ - The quick brown \ - fox jumps over \ - the lazy dog.\ - """ - -[string.literal] - -# What you see is what you get. -winpath = 'C:\Users\nodejs\templates' -winpath2 = '\\ServerX\admin$\system32\' -quoted = 'Tom "Dubs" Preston-Werner' -regex = '<\i\c*\s*>' - - -[string.literal.multiline] - -regex2 = '''I [dw]on't need \d{2} apples''' -lines = ''' -The first newline is -trimmed in raw strings. - All other whitespace - is preserved. -''' - - -################################################################################ -## Integer - -# Integers are whole numbers. Positive numbers may be prefixed with a plus sign. -# Negative numbers are prefixed with a minus sign. - -[integer] - -key1 = +99 -key2 = 42 -key3 = 0 -key4 = -17 - -[integer.underscores] - -# For large numbers, you may use underscores to enhance readability. Each -# underscore must be surrounded by at least one digit. -key1 = 1_000 -key2 = 5_349_221 -key3 = 1_2_3_4_5 # valid but inadvisable - - -################################################################################ -## Float - -# A float consists of an integer part (which may be prefixed with a plus or -# minus sign) followed by a fractional part and/or an exponent part. - -[float.fractional] - -key1 = +1.0 -key2 = 3.1415 -key3 = -0.01 - -[float.exponent] - -key1 = 5e+22 -key2 = 1e6 -key3 = -2E-2 - -[float.both] - -key = 6.626e-34 - -[float.underscores] - -key1 = 9_224_617.445_991_228_313 -key2 = 1e1_00 - - -################################################################################ -## Boolean - -# Booleans are just the tokens you're used to. Always lowercase. - -[boolean] - -True = true -False = false - - -################################################################################ -## Datetime - -# Datetimes are RFC 3339 dates. - -[datetime] - -key1 = 1979-05-27T07:32:00Z -key2 = 1979-05-27T00:32:00-07:00 -key3 = 1979-05-27T00:32:00.999999-07:00 - - -################################################################################ -## Array - -# Arrays are square brackets with other primitives inside. Whitespace is -# ignored. Elements are separated by commas. Data types may not be mixed. - -[array] - -key1 = [ 1, 2, 3 ] -key2 = [ "red", "yellow", "green" ] -key3 = [ [ 1, 2 ], [3, 4, 5] ] -#key4 = [ [ 1, 2 ], ["a", "b", "c"] ] # this is ok - -# Arrays can also be multiline. So in addition to ignoring whitespace, arrays -# also ignore newlines between the brackets. Terminating commas are ok before -# the closing bracket. - -key5 = [ - 1, 2, 3 -] -key6 = [ - 1, - 2, # this is ok -] - - -################################################################################ -## Array of Tables - -# These can be expressed by using a table name in double brackets. Each table -# with the same double bracketed name will be an element in the array. The -# tables are inserted in the order encountered. - -[[products]] - -name = "Hammer" -sku = 738594937 - -[[products]] - -[[products]] - -name = "Nail" -sku = 284758393 -color = "gray" - - -# You can create nested arrays of tables as well. - -[[fruit]] - name = "apple" - - [fruit.physical] - color = "red" - shape = "round" - - [[fruit.variety]] - name = "red delicious" - - [[fruit.variety]] - name = "granny smith" - -[[fruit]] - name = "banana" - - [[fruit.variety]] - name = "plantain" diff --git a/vendor/github.com/pelletier/go-toml/benchmark.yml b/vendor/github.com/pelletier/go-toml/benchmark.yml deleted file mode 100644 index 0bd19f08a6..0000000000 --- a/vendor/github.com/pelletier/go-toml/benchmark.yml +++ /dev/null @@ -1,121 +0,0 @@ ---- -array: - key1: - - 1 - - 2 - - 3 - key2: - - red - - yellow - - green - key3: - - - 1 - - 2 - - - 3 - - 4 - - 5 - key4: - - - 1 - - 2 - - - a - - b - - c - key5: - - 1 - - 2 - - 3 - key6: - - 1 - - 2 -boolean: - 'False': false - 'True': true -datetime: - key1: '1979-05-27T07:32:00Z' - key2: '1979-05-27T00:32:00-07:00' - key3: '1979-05-27T00:32:00.999999-07:00' -float: - both: - key: 6.626e-34 - exponent: - key1: 5.0e+22 - key2: 1000000 - key3: -0.02 - fractional: - key1: 1 - key2: 3.1415 - key3: -0.01 - underscores: - key1: 9224617.445991227 - key2: 1.0e+100 -fruit: -- name: apple - physical: - color: red - shape: round - variety: - - name: red delicious - - name: granny smith -- name: banana - variety: - - name: plantain -integer: - key1: 99 - key2: 42 - key3: 0 - key4: -17 - underscores: - key1: 1000 - key2: 5349221 - key3: 12345 -products: -- name: Hammer - sku: 738594937 -- {} -- color: gray - name: Nail - sku: 284758393 -string: - basic: - basic: "I'm a string. \"You can quote me\". Name\tJosé\nLocation\tSF." - literal: - multiline: - lines: | - The first newline is - trimmed in raw strings. - All other whitespace - is preserved. - regex2: I [dw]on't need \d{2} apples - quoted: Tom "Dubs" Preston-Werner - regex: "<\\i\\c*\\s*>" - winpath: C:\Users\nodejs\templates - winpath2: "\\\\ServerX\\admin$\\system32\\" - multiline: - continued: - key1: The quick brown fox jumps over the lazy dog. - key2: The quick brown fox jumps over the lazy dog. - key3: The quick brown fox jumps over the lazy dog. - key1: |- - One - Two - key2: |- - One - Two - key3: |- - One - Two -table: - inline: - name: - first: Tom - last: Preston-Werner - point: - x: 1 - y: 2 - key: value - subtable: - key: another value -x: - y: - z: - w: {} diff --git a/vendor/github.com/pelletier/go-toml/doc.go b/vendor/github.com/pelletier/go-toml/doc.go deleted file mode 100644 index a1406a32b3..0000000000 --- a/vendor/github.com/pelletier/go-toml/doc.go +++ /dev/null @@ -1,23 +0,0 @@ -// Package toml is a TOML parser and manipulation library. -// -// This version supports the specification as described in -// https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.5.0.md -// -// Marshaling -// -// Go-toml can marshal and unmarshal TOML documents from and to data -// structures. -// -// TOML document as a tree -// -// Go-toml can operate on a TOML document as a tree. Use one of the Load* -// functions to parse TOML data and obtain a Tree instance, then one of its -// methods to manipulate the tree. -// -// JSONPath-like queries -// -// The package github.com/pelletier/go-toml/query implements a system -// similar to JSONPath to quickly retrieve elements of a TOML document using a -// single expression. See the package documentation for more information. -// -package toml diff --git a/vendor/github.com/pelletier/go-toml/example-crlf.toml b/vendor/github.com/pelletier/go-toml/example-crlf.toml deleted file mode 100644 index 780d9c68f2..0000000000 --- a/vendor/github.com/pelletier/go-toml/example-crlf.toml +++ /dev/null @@ -1,30 +0,0 @@ -# This is a TOML document. Boom. - -title = "TOML Example" - -[owner] -name = "Tom Preston-Werner" -organization = "GitHub" -bio = "GitHub Cofounder & CEO\nLikes tater tots and beer." -dob = 1979-05-27T07:32:00Z # First class dates? Why not? - -[database] -server = "192.168.1.1" -ports = [ 8001, 8001, 8002 ] -connection_max = 5000 -enabled = true - -[servers] - - # You can indent as you please. Tabs or spaces. TOML don't care. - [servers.alpha] - ip = "10.0.0.1" - dc = "eqdc10" - - [servers.beta] - ip = "10.0.0.2" - dc = "eqdc10" - -[clients] -data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it -score = 4e-08 # to make sure leading zeroes in exponent parts of floats are supported \ No newline at end of file diff --git a/vendor/github.com/pelletier/go-toml/example.toml b/vendor/github.com/pelletier/go-toml/example.toml deleted file mode 100644 index f45bf88b8f..0000000000 --- a/vendor/github.com/pelletier/go-toml/example.toml +++ /dev/null @@ -1,30 +0,0 @@ -# This is a TOML document. Boom. - -title = "TOML Example" - -[owner] -name = "Tom Preston-Werner" -organization = "GitHub" -bio = "GitHub Cofounder & CEO\nLikes tater tots and beer." -dob = 1979-05-27T07:32:00Z # First class dates? Why not? - -[database] -server = "192.168.1.1" -ports = [ 8001, 8001, 8002 ] -connection_max = 5000 -enabled = true - -[servers] - - # You can indent as you please. Tabs or spaces. TOML don't care. - [servers.alpha] - ip = "10.0.0.1" - dc = "eqdc10" - - [servers.beta] - ip = "10.0.0.2" - dc = "eqdc10" - -[clients] -data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it -score = 4e-08 # to make sure leading zeroes in exponent parts of floats are supported \ No newline at end of file diff --git a/vendor/github.com/pelletier/go-toml/fuzz.go b/vendor/github.com/pelletier/go-toml/fuzz.go deleted file mode 100644 index 14570c8d35..0000000000 --- a/vendor/github.com/pelletier/go-toml/fuzz.go +++ /dev/null @@ -1,31 +0,0 @@ -// +build gofuzz - -package toml - -func Fuzz(data []byte) int { - tree, err := LoadBytes(data) - if err != nil { - if tree != nil { - panic("tree must be nil if there is an error") - } - return 0 - } - - str, err := tree.ToTomlString() - if err != nil { - if str != "" { - panic(`str must be "" if there is an error`) - } - panic(err) - } - - tree, err = Load(str) - if err != nil { - if tree != nil { - panic("tree must be nil if there is an error") - } - return 0 - } - - return 1 -} diff --git a/vendor/github.com/pelletier/go-toml/fuzz.sh b/vendor/github.com/pelletier/go-toml/fuzz.sh deleted file mode 100644 index 3204b4c446..0000000000 --- a/vendor/github.com/pelletier/go-toml/fuzz.sh +++ /dev/null @@ -1,15 +0,0 @@ -#! /bin/sh -set -eu - -go get github.com/dvyukov/go-fuzz/go-fuzz -go get github.com/dvyukov/go-fuzz/go-fuzz-build - -if [ ! -e toml-fuzz.zip ]; then - go-fuzz-build github.com/pelletier/go-toml -fi - -rm -fr fuzz -mkdir -p fuzz/corpus -cp *.toml fuzz/corpus - -go-fuzz -bin=toml-fuzz.zip -workdir=fuzz diff --git a/vendor/github.com/pelletier/go-toml/fuzzit.sh b/vendor/github.com/pelletier/go-toml/fuzzit.sh deleted file mode 100644 index b575a6081f..0000000000 --- a/vendor/github.com/pelletier/go-toml/fuzzit.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash -set -xe - -# go-fuzz doesn't support modules yet, so ensure we do everything -# in the old style GOPATH way -export GO111MODULE="off" - -# install go-fuzz -go get -u github.com/dvyukov/go-fuzz/go-fuzz github.com/dvyukov/go-fuzz/go-fuzz-build - -# target name can only contain lower-case letters (a-z), digits (0-9) and a dash (-) -# to add another target, make sure to create it with `fuzzit create target` -# before using `fuzzit create job` -TARGET=toml-fuzzer - -go-fuzz-build -libfuzzer -o ${TARGET}.a github.com/pelletier/go-toml -clang -fsanitize=fuzzer ${TARGET}.a -o ${TARGET} - -# install fuzzit for talking to fuzzit.dev service -# or latest version: -# https://github.com/fuzzitdev/fuzzit/releases/latest/download/fuzzit_Linux_x86_64 -wget -q -O fuzzit https://github.com/fuzzitdev/fuzzit/releases/download/v2.4.52/fuzzit_Linux_x86_64 -chmod a+x fuzzit - -# TODO: change kkowalczyk to go-toml and create toml-fuzzer target there -./fuzzit create job --type $TYPE go-toml/${TARGET} ${TARGET} diff --git a/vendor/github.com/pelletier/go-toml/go.mod b/vendor/github.com/pelletier/go-toml/go.mod deleted file mode 100644 index c7faa6b3e1..0000000000 --- a/vendor/github.com/pelletier/go-toml/go.mod +++ /dev/null @@ -1,9 +0,0 @@ -module github.com/pelletier/go-toml - -go 1.12 - -require ( - github.com/BurntSushi/toml v0.3.1 - github.com/davecgh/go-spew v1.1.1 - gopkg.in/yaml.v2 v2.3.0 -) diff --git a/vendor/github.com/pelletier/go-toml/go.sum b/vendor/github.com/pelletier/go-toml/go.sum deleted file mode 100644 index 6f356470d7..0000000000 --- a/vendor/github.com/pelletier/go-toml/go.sum +++ /dev/null @@ -1,19 +0,0 @@ -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3 h1:fvjTMHxHEw/mxHbtzPi3JCcKXQRAnQTBRo6YCJSVHKI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/pelletier/go-toml/keysparsing.go b/vendor/github.com/pelletier/go-toml/keysparsing.go deleted file mode 100644 index e091500b24..0000000000 --- a/vendor/github.com/pelletier/go-toml/keysparsing.go +++ /dev/null @@ -1,112 +0,0 @@ -// Parsing keys handling both bare and quoted keys. - -package toml - -import ( - "errors" - "fmt" -) - -// Convert the bare key group string to an array. -// The input supports double quotation and single quotation, -// but escape sequences are not supported. Lexers must unescape them beforehand. -func parseKey(key string) ([]string, error) { - runes := []rune(key) - var groups []string - - if len(key) == 0 { - return nil, errors.New("empty key") - } - - idx := 0 - for idx < len(runes) { - for ; idx < len(runes) && isSpace(runes[idx]); idx++ { - // skip leading whitespace - } - if idx >= len(runes) { - break - } - r := runes[idx] - if isValidBareChar(r) { - // parse bare key - startIdx := idx - endIdx := -1 - idx++ - for idx < len(runes) { - r = runes[idx] - if isValidBareChar(r) { - idx++ - } else if r == '.' { - endIdx = idx - break - } else if isSpace(r) { - endIdx = idx - for ; idx < len(runes) && isSpace(runes[idx]); idx++ { - // skip trailing whitespace - } - if idx < len(runes) && runes[idx] != '.' { - return nil, fmt.Errorf("invalid key character after whitespace: %c", runes[idx]) - } - break - } else { - return nil, fmt.Errorf("invalid bare key character: %c", r) - } - } - if endIdx == -1 { - endIdx = idx - } - groups = append(groups, string(runes[startIdx:endIdx])) - } else if r == '\'' { - // parse single quoted key - idx++ - startIdx := idx - for { - if idx >= len(runes) { - return nil, fmt.Errorf("unclosed single-quoted key") - } - r = runes[idx] - if r == '\'' { - groups = append(groups, string(runes[startIdx:idx])) - idx++ - break - } - idx++ - } - } else if r == '"' { - // parse double quoted key - idx++ - startIdx := idx - for { - if idx >= len(runes) { - return nil, fmt.Errorf("unclosed double-quoted key") - } - r = runes[idx] - if r == '"' { - groups = append(groups, string(runes[startIdx:idx])) - idx++ - break - } - idx++ - } - } else if r == '.' { - idx++ - if idx >= len(runes) { - return nil, fmt.Errorf("unexpected end of key") - } - r = runes[idx] - if !isValidBareChar(r) && r != '\'' && r != '"' && r != ' ' { - return nil, fmt.Errorf("expecting key part after dot") - } - } else { - return nil, fmt.Errorf("invalid key character: %c", r) - } - } - if len(groups) == 0 { - return nil, fmt.Errorf("empty key") - } - return groups, nil -} - -func isValidBareChar(r rune) bool { - return isAlphanumeric(r) || r == '-' || isDigit(r) -} diff --git a/vendor/github.com/pelletier/go-toml/lexer.go b/vendor/github.com/pelletier/go-toml/lexer.go deleted file mode 100644 index 425e847a7a..0000000000 --- a/vendor/github.com/pelletier/go-toml/lexer.go +++ /dev/null @@ -1,801 +0,0 @@ -// TOML lexer. -// -// Written using the principles developed by Rob Pike in -// http://www.youtube.com/watch?v=HxaD_trXwRE - -package toml - -import ( - "bytes" - "errors" - "fmt" - "regexp" - "strconv" - "strings" -) - -var dateRegexp *regexp.Regexp - -// Define state functions -type tomlLexStateFn func() tomlLexStateFn - -// Define lexer -type tomlLexer struct { - inputIdx int - input []rune // Textual source - currentTokenStart int - currentTokenStop int - tokens []token - brackets []rune - line int - col int - endbufferLine int - endbufferCol int -} - -// Basic read operations on input - -func (l *tomlLexer) read() rune { - r := l.peek() - if r == '\n' { - l.endbufferLine++ - l.endbufferCol = 1 - } else { - l.endbufferCol++ - } - l.inputIdx++ - return r -} - -func (l *tomlLexer) next() rune { - r := l.read() - - if r != eof { - l.currentTokenStop++ - } - return r -} - -func (l *tomlLexer) ignore() { - l.currentTokenStart = l.currentTokenStop - l.line = l.endbufferLine - l.col = l.endbufferCol -} - -func (l *tomlLexer) skip() { - l.next() - l.ignore() -} - -func (l *tomlLexer) fastForward(n int) { - for i := 0; i < n; i++ { - l.next() - } -} - -func (l *tomlLexer) emitWithValue(t tokenType, value string) { - l.tokens = append(l.tokens, token{ - Position: Position{l.line, l.col}, - typ: t, - val: value, - }) - l.ignore() -} - -func (l *tomlLexer) emit(t tokenType) { - l.emitWithValue(t, string(l.input[l.currentTokenStart:l.currentTokenStop])) -} - -func (l *tomlLexer) peek() rune { - if l.inputIdx >= len(l.input) { - return eof - } - return l.input[l.inputIdx] -} - -func (l *tomlLexer) peekString(size int) string { - maxIdx := len(l.input) - upperIdx := l.inputIdx + size // FIXME: potential overflow - if upperIdx > maxIdx { - upperIdx = maxIdx - } - return string(l.input[l.inputIdx:upperIdx]) -} - -func (l *tomlLexer) follow(next string) bool { - return next == l.peekString(len(next)) -} - -// Error management - -func (l *tomlLexer) errorf(format string, args ...interface{}) tomlLexStateFn { - l.tokens = append(l.tokens, token{ - Position: Position{l.line, l.col}, - typ: tokenError, - val: fmt.Sprintf(format, args...), - }) - return nil -} - -// State functions - -func (l *tomlLexer) lexVoid() tomlLexStateFn { - for { - next := l.peek() - switch next { - case '}': // after '{' - return l.lexRightCurlyBrace - case '[': - return l.lexTableKey - case '#': - return l.lexComment(l.lexVoid) - case '=': - return l.lexEqual - case '\r': - fallthrough - case '\n': - l.skip() - continue - } - - if isSpace(next) { - l.skip() - } - - if isKeyStartChar(next) { - return l.lexKey - } - - if next == eof { - l.next() - break - } - } - - l.emit(tokenEOF) - return nil -} - -func (l *tomlLexer) lexRvalue() tomlLexStateFn { - for { - next := l.peek() - switch next { - case '.': - return l.errorf("cannot start float with a dot") - case '=': - return l.lexEqual - case '[': - return l.lexLeftBracket - case ']': - return l.lexRightBracket - case '{': - return l.lexLeftCurlyBrace - case '}': - return l.lexRightCurlyBrace - case '#': - return l.lexComment(l.lexRvalue) - case '"': - return l.lexString - case '\'': - return l.lexLiteralString - case ',': - return l.lexComma - case '\r': - fallthrough - case '\n': - l.skip() - if len(l.brackets) > 0 && l.brackets[len(l.brackets)-1] == '[' { - return l.lexRvalue - } - return l.lexVoid - } - - if l.follow("true") { - return l.lexTrue - } - - if l.follow("false") { - return l.lexFalse - } - - if l.follow("inf") { - return l.lexInf - } - - if l.follow("nan") { - return l.lexNan - } - - if isSpace(next) { - l.skip() - continue - } - - if next == eof { - l.next() - break - } - - possibleDate := l.peekString(35) - dateSubmatches := dateRegexp.FindStringSubmatch(possibleDate) - if dateSubmatches != nil && dateSubmatches[0] != "" { - l.fastForward(len(dateSubmatches[0])) - if dateSubmatches[2] == "" { // no timezone information => local date - return l.lexLocalDate - } - return l.lexDate - } - - if next == '+' || next == '-' || isDigit(next) { - return l.lexNumber - } - - return l.errorf("no value can start with %c", next) - } - - l.emit(tokenEOF) - return nil -} - -func (l *tomlLexer) lexLeftCurlyBrace() tomlLexStateFn { - l.next() - l.emit(tokenLeftCurlyBrace) - l.brackets = append(l.brackets, '{') - return l.lexVoid -} - -func (l *tomlLexer) lexRightCurlyBrace() tomlLexStateFn { - l.next() - l.emit(tokenRightCurlyBrace) - if len(l.brackets) == 0 || l.brackets[len(l.brackets)-1] != '{' { - return l.errorf("cannot have '}' here") - } - l.brackets = l.brackets[:len(l.brackets)-1] - return l.lexRvalue -} - -func (l *tomlLexer) lexDate() tomlLexStateFn { - l.emit(tokenDate) - return l.lexRvalue -} - -func (l *tomlLexer) lexLocalDate() tomlLexStateFn { - l.emit(tokenLocalDate) - return l.lexRvalue -} - -func (l *tomlLexer) lexTrue() tomlLexStateFn { - l.fastForward(4) - l.emit(tokenTrue) - return l.lexRvalue -} - -func (l *tomlLexer) lexFalse() tomlLexStateFn { - l.fastForward(5) - l.emit(tokenFalse) - return l.lexRvalue -} - -func (l *tomlLexer) lexInf() tomlLexStateFn { - l.fastForward(3) - l.emit(tokenInf) - return l.lexRvalue -} - -func (l *tomlLexer) lexNan() tomlLexStateFn { - l.fastForward(3) - l.emit(tokenNan) - return l.lexRvalue -} - -func (l *tomlLexer) lexEqual() tomlLexStateFn { - l.next() - l.emit(tokenEqual) - return l.lexRvalue -} - -func (l *tomlLexer) lexComma() tomlLexStateFn { - l.next() - l.emit(tokenComma) - if len(l.brackets) > 0 && l.brackets[len(l.brackets)-1] == '{' { - return l.lexVoid - } - return l.lexRvalue -} - -// Parse the key and emits its value without escape sequences. -// bare keys, basic string keys and literal string keys are supported. -func (l *tomlLexer) lexKey() tomlLexStateFn { - growingString := "" - - for r := l.peek(); isKeyChar(r) || r == '\n' || r == '\r'; r = l.peek() { - if r == '"' { - l.next() - str, err := l.lexStringAsString(`"`, false, true) - if err != nil { - return l.errorf(err.Error()) - } - growingString += "\"" + str + "\"" - l.next() - continue - } else if r == '\'' { - l.next() - str, err := l.lexLiteralStringAsString(`'`, false) - if err != nil { - return l.errorf(err.Error()) - } - growingString += "'" + str + "'" - l.next() - continue - } else if r == '\n' { - return l.errorf("keys cannot contain new lines") - } else if isSpace(r) { - str := " " - // skip trailing whitespace - l.next() - for r = l.peek(); isSpace(r); r = l.peek() { - str += string(r) - l.next() - } - // break loop if not a dot - if r != '.' { - break - } - str += "." - // skip trailing whitespace after dot - l.next() - for r = l.peek(); isSpace(r); r = l.peek() { - str += string(r) - l.next() - } - growingString += str - continue - } else if r == '.' { - // skip - } else if !isValidBareChar(r) { - return l.errorf("keys cannot contain %c character", r) - } - growingString += string(r) - l.next() - } - l.emitWithValue(tokenKey, growingString) - return l.lexVoid -} - -func (l *tomlLexer) lexComment(previousState tomlLexStateFn) tomlLexStateFn { - return func() tomlLexStateFn { - for next := l.peek(); next != '\n' && next != eof; next = l.peek() { - if next == '\r' && l.follow("\r\n") { - break - } - l.next() - } - l.ignore() - return previousState - } -} - -func (l *tomlLexer) lexLeftBracket() tomlLexStateFn { - l.next() - l.emit(tokenLeftBracket) - l.brackets = append(l.brackets, '[') - return l.lexRvalue -} - -func (l *tomlLexer) lexLiteralStringAsString(terminator string, discardLeadingNewLine bool) (string, error) { - growingString := "" - - if discardLeadingNewLine { - if l.follow("\r\n") { - l.skip() - l.skip() - } else if l.peek() == '\n' { - l.skip() - } - } - - // find end of string - for { - if l.follow(terminator) { - return growingString, nil - } - - next := l.peek() - if next == eof { - break - } - growingString += string(l.next()) - } - - return "", errors.New("unclosed string") -} - -func (l *tomlLexer) lexLiteralString() tomlLexStateFn { - l.skip() - - // handle special case for triple-quote - terminator := "'" - discardLeadingNewLine := false - if l.follow("''") { - l.skip() - l.skip() - terminator = "'''" - discardLeadingNewLine = true - } - - str, err := l.lexLiteralStringAsString(terminator, discardLeadingNewLine) - if err != nil { - return l.errorf(err.Error()) - } - - l.emitWithValue(tokenString, str) - l.fastForward(len(terminator)) - l.ignore() - return l.lexRvalue -} - -// Lex a string and return the results as a string. -// Terminator is the substring indicating the end of the token. -// The resulting string does not include the terminator. -func (l *tomlLexer) lexStringAsString(terminator string, discardLeadingNewLine, acceptNewLines bool) (string, error) { - growingString := "" - - if discardLeadingNewLine { - if l.follow("\r\n") { - l.skip() - l.skip() - } else if l.peek() == '\n' { - l.skip() - } - } - - for { - if l.follow(terminator) { - return growingString, nil - } - - if l.follow("\\") { - l.next() - switch l.peek() { - case '\r': - fallthrough - case '\n': - fallthrough - case '\t': - fallthrough - case ' ': - // skip all whitespace chars following backslash - for strings.ContainsRune("\r\n\t ", l.peek()) { - l.next() - } - case '"': - growingString += "\"" - l.next() - case 'n': - growingString += "\n" - l.next() - case 'b': - growingString += "\b" - l.next() - case 'f': - growingString += "\f" - l.next() - case '/': - growingString += "/" - l.next() - case 't': - growingString += "\t" - l.next() - case 'r': - growingString += "\r" - l.next() - case '\\': - growingString += "\\" - l.next() - case 'u': - l.next() - code := "" - for i := 0; i < 4; i++ { - c := l.peek() - if !isHexDigit(c) { - return "", errors.New("unfinished unicode escape") - } - l.next() - code = code + string(c) - } - intcode, err := strconv.ParseInt(code, 16, 32) - if err != nil { - return "", errors.New("invalid unicode escape: \\u" + code) - } - growingString += string(rune(intcode)) - case 'U': - l.next() - code := "" - for i := 0; i < 8; i++ { - c := l.peek() - if !isHexDigit(c) { - return "", errors.New("unfinished unicode escape") - } - l.next() - code = code + string(c) - } - intcode, err := strconv.ParseInt(code, 16, 64) - if err != nil { - return "", errors.New("invalid unicode escape: \\U" + code) - } - growingString += string(rune(intcode)) - default: - return "", errors.New("invalid escape sequence: \\" + string(l.peek())) - } - } else { - r := l.peek() - - if 0x00 <= r && r <= 0x1F && r != '\t' && !(acceptNewLines && (r == '\n' || r == '\r')) { - return "", fmt.Errorf("unescaped control character %U", r) - } - l.next() - growingString += string(r) - } - - if l.peek() == eof { - break - } - } - - return "", errors.New("unclosed string") -} - -func (l *tomlLexer) lexString() tomlLexStateFn { - l.skip() - - // handle special case for triple-quote - terminator := `"` - discardLeadingNewLine := false - acceptNewLines := false - if l.follow(`""`) { - l.skip() - l.skip() - terminator = `"""` - discardLeadingNewLine = true - acceptNewLines = true - } - - str, err := l.lexStringAsString(terminator, discardLeadingNewLine, acceptNewLines) - if err != nil { - return l.errorf(err.Error()) - } - - l.emitWithValue(tokenString, str) - l.fastForward(len(terminator)) - l.ignore() - return l.lexRvalue -} - -func (l *tomlLexer) lexTableKey() tomlLexStateFn { - l.next() - - if l.peek() == '[' { - // token '[[' signifies an array of tables - l.next() - l.emit(tokenDoubleLeftBracket) - return l.lexInsideTableArrayKey - } - // vanilla table key - l.emit(tokenLeftBracket) - return l.lexInsideTableKey -} - -// Parse the key till "]]", but only bare keys are supported -func (l *tomlLexer) lexInsideTableArrayKey() tomlLexStateFn { - for r := l.peek(); r != eof; r = l.peek() { - switch r { - case ']': - if l.currentTokenStop > l.currentTokenStart { - l.emit(tokenKeyGroupArray) - } - l.next() - if l.peek() != ']' { - break - } - l.next() - l.emit(tokenDoubleRightBracket) - return l.lexVoid - case '[': - return l.errorf("table array key cannot contain ']'") - default: - l.next() - } - } - return l.errorf("unclosed table array key") -} - -// Parse the key till "]" but only bare keys are supported -func (l *tomlLexer) lexInsideTableKey() tomlLexStateFn { - for r := l.peek(); r != eof; r = l.peek() { - switch r { - case ']': - if l.currentTokenStop > l.currentTokenStart { - l.emit(tokenKeyGroup) - } - l.next() - l.emit(tokenRightBracket) - return l.lexVoid - case '[': - return l.errorf("table key cannot contain ']'") - default: - l.next() - } - } - return l.errorf("unclosed table key") -} - -func (l *tomlLexer) lexRightBracket() tomlLexStateFn { - l.next() - l.emit(tokenRightBracket) - if len(l.brackets) == 0 || l.brackets[len(l.brackets)-1] != '[' { - return l.errorf("cannot have ']' here") - } - l.brackets = l.brackets[:len(l.brackets)-1] - return l.lexRvalue -} - -type validRuneFn func(r rune) bool - -func isValidHexRune(r rune) bool { - return r >= 'a' && r <= 'f' || - r >= 'A' && r <= 'F' || - r >= '0' && r <= '9' || - r == '_' -} - -func isValidOctalRune(r rune) bool { - return r >= '0' && r <= '7' || r == '_' -} - -func isValidBinaryRune(r rune) bool { - return r == '0' || r == '1' || r == '_' -} - -func (l *tomlLexer) lexNumber() tomlLexStateFn { - r := l.peek() - - if r == '0' { - follow := l.peekString(2) - if len(follow) == 2 { - var isValidRune validRuneFn - switch follow[1] { - case 'x': - isValidRune = isValidHexRune - case 'o': - isValidRune = isValidOctalRune - case 'b': - isValidRune = isValidBinaryRune - default: - if follow[1] >= 'a' && follow[1] <= 'z' || follow[1] >= 'A' && follow[1] <= 'Z' { - return l.errorf("unknown number base: %s. possible options are x (hex) o (octal) b (binary)", string(follow[1])) - } - } - - if isValidRune != nil { - l.next() - l.next() - digitSeen := false - for { - next := l.peek() - if !isValidRune(next) { - break - } - digitSeen = true - l.next() - } - - if !digitSeen { - return l.errorf("number needs at least one digit") - } - - l.emit(tokenInteger) - - return l.lexRvalue - } - } - } - - if r == '+' || r == '-' { - l.next() - if l.follow("inf") { - return l.lexInf - } - if l.follow("nan") { - return l.lexNan - } - } - - pointSeen := false - expSeen := false - digitSeen := false - for { - next := l.peek() - if next == '.' { - if pointSeen { - return l.errorf("cannot have two dots in one float") - } - l.next() - if !isDigit(l.peek()) { - return l.errorf("float cannot end with a dot") - } - pointSeen = true - } else if next == 'e' || next == 'E' { - expSeen = true - l.next() - r := l.peek() - if r == '+' || r == '-' { - l.next() - } - } else if isDigit(next) { - digitSeen = true - l.next() - } else if next == '_' { - l.next() - } else { - break - } - if pointSeen && !digitSeen { - return l.errorf("cannot start float with a dot") - } - } - - if !digitSeen { - return l.errorf("no digit in that number") - } - if pointSeen || expSeen { - l.emit(tokenFloat) - } else { - l.emit(tokenInteger) - } - return l.lexRvalue -} - -func (l *tomlLexer) run() { - for state := l.lexVoid; state != nil; { - state = state() - } -} - -func init() { - // Regexp for all date/time formats supported by TOML. - // Group 1: nano precision - // Group 2: timezone - // - // /!\ also matches the empty string - // - // Example matches: - //1979-05-27T07:32:00Z - //1979-05-27T00:32:00-07:00 - //1979-05-27T00:32:00.999999-07:00 - //1979-05-27 07:32:00Z - //1979-05-27 00:32:00-07:00 - //1979-05-27 00:32:00.999999-07:00 - //1979-05-27T07:32:00 - //1979-05-27T00:32:00.999999 - //1979-05-27 07:32:00 - //1979-05-27 00:32:00.999999 - //1979-05-27 - //07:32:00 - //00:32:00.999999 - dateRegexp = regexp.MustCompile(`^(?:\d{1,4}-\d{2}-\d{2})?(?:[T ]?\d{2}:\d{2}:\d{2}(\.\d{1,9})?(Z|[+-]\d{2}:\d{2})?)?`) -} - -// Entry point -func lexToml(inputBytes []byte) []token { - runes := bytes.Runes(inputBytes) - l := &tomlLexer{ - input: runes, - tokens: make([]token, 0, 256), - line: 1, - col: 1, - endbufferLine: 1, - endbufferCol: 1, - } - l.run() - return l.tokens -} diff --git a/vendor/github.com/pelletier/go-toml/marshal.go b/vendor/github.com/pelletier/go-toml/marshal.go deleted file mode 100644 index db5a7b4f09..0000000000 --- a/vendor/github.com/pelletier/go-toml/marshal.go +++ /dev/null @@ -1,1240 +0,0 @@ -package toml - -import ( - "bytes" - "encoding" - "errors" - "fmt" - "io" - "reflect" - "sort" - "strconv" - "strings" - "time" -) - -const ( - tagFieldName = "toml" - tagFieldComment = "comment" - tagCommented = "commented" - tagMultiline = "multiline" - tagDefault = "default" -) - -type tomlOpts struct { - name string - nameFromTag bool - comment string - commented bool - multiline bool - include bool - omitempty bool - defaultValue string -} - -type encOpts struct { - quoteMapKeys bool - arraysOneElementPerLine bool -} - -var encOptsDefaults = encOpts{ - quoteMapKeys: false, -} - -type annotation struct { - tag string - comment string - commented string - multiline string - defaultValue string -} - -var annotationDefault = annotation{ - tag: tagFieldName, - comment: tagFieldComment, - commented: tagCommented, - multiline: tagMultiline, - defaultValue: tagDefault, -} - -type marshalOrder int - -// Orders the Encoder can write the fields to the output stream. -const ( - // Sort fields alphabetically. - OrderAlphabetical marshalOrder = iota + 1 - // Preserve the order the fields are encountered. For example, the order of fields in - // a struct. - OrderPreserve -) - -var timeType = reflect.TypeOf(time.Time{}) -var marshalerType = reflect.TypeOf(new(Marshaler)).Elem() -var unmarshalerType = reflect.TypeOf(new(Unmarshaler)).Elem() -var textMarshalerType = reflect.TypeOf(new(encoding.TextMarshaler)).Elem() -var textUnmarshalerType = reflect.TypeOf(new(encoding.TextUnmarshaler)).Elem() -var localDateType = reflect.TypeOf(LocalDate{}) -var localTimeType = reflect.TypeOf(LocalTime{}) -var localDateTimeType = reflect.TypeOf(LocalDateTime{}) - -// Check if the given marshal type maps to a Tree primitive -func isPrimitive(mtype reflect.Type) bool { - switch mtype.Kind() { - case reflect.Ptr: - return isPrimitive(mtype.Elem()) - case reflect.Bool: - return true - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return true - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - return true - case reflect.Float32, reflect.Float64: - return true - case reflect.String: - return true - case reflect.Struct: - return isTimeType(mtype) - default: - return false - } -} - -func isTimeType(mtype reflect.Type) bool { - return mtype == timeType || mtype == localDateType || mtype == localDateTimeType || mtype == localTimeType -} - -// Check if the given marshal type maps to a Tree slice or array -func isTreeSequence(mtype reflect.Type) bool { - switch mtype.Kind() { - case reflect.Ptr: - return isTreeSequence(mtype.Elem()) - case reflect.Slice, reflect.Array: - return isTree(mtype.Elem()) - default: - return false - } -} - -// Check if the given marshal type maps to a slice or array of a custom marshaler type -func isCustomMarshalerSequence(mtype reflect.Type) bool { - switch mtype.Kind() { - case reflect.Ptr: - return isCustomMarshalerSequence(mtype.Elem()) - case reflect.Slice, reflect.Array: - return isCustomMarshaler(mtype.Elem()) || isCustomMarshaler(reflect.New(mtype.Elem()).Type()) - default: - return false - } -} - -// Check if the given marshal type maps to a slice or array of a text marshaler type -func isTextMarshalerSequence(mtype reflect.Type) bool { - switch mtype.Kind() { - case reflect.Ptr: - return isTextMarshalerSequence(mtype.Elem()) - case reflect.Slice, reflect.Array: - return isTextMarshaler(mtype.Elem()) || isTextMarshaler(reflect.New(mtype.Elem()).Type()) - default: - return false - } -} - -// Check if the given marshal type maps to a non-Tree slice or array -func isOtherSequence(mtype reflect.Type) bool { - switch mtype.Kind() { - case reflect.Ptr: - return isOtherSequence(mtype.Elem()) - case reflect.Slice, reflect.Array: - return !isTreeSequence(mtype) - default: - return false - } -} - -// Check if the given marshal type maps to a Tree -func isTree(mtype reflect.Type) bool { - switch mtype.Kind() { - case reflect.Ptr: - return isTree(mtype.Elem()) - case reflect.Map: - return true - case reflect.Struct: - return !isPrimitive(mtype) - default: - return false - } -} - -func isCustomMarshaler(mtype reflect.Type) bool { - return mtype.Implements(marshalerType) -} - -func callCustomMarshaler(mval reflect.Value) ([]byte, error) { - return mval.Interface().(Marshaler).MarshalTOML() -} - -func isTextMarshaler(mtype reflect.Type) bool { - return mtype.Implements(textMarshalerType) && !isTimeType(mtype) -} - -func callTextMarshaler(mval reflect.Value) ([]byte, error) { - return mval.Interface().(encoding.TextMarshaler).MarshalText() -} - -func isCustomUnmarshaler(mtype reflect.Type) bool { - return mtype.Implements(unmarshalerType) -} - -func callCustomUnmarshaler(mval reflect.Value, tval interface{}) error { - return mval.Interface().(Unmarshaler).UnmarshalTOML(tval) -} - -func isTextUnmarshaler(mtype reflect.Type) bool { - return mtype.Implements(textUnmarshalerType) -} - -func callTextUnmarshaler(mval reflect.Value, text []byte) error { - return mval.Interface().(encoding.TextUnmarshaler).UnmarshalText(text) -} - -// Marshaler is the interface implemented by types that -// can marshal themselves into valid TOML. -type Marshaler interface { - MarshalTOML() ([]byte, error) -} - -// Unmarshaler is the interface implemented by types that -// can unmarshal a TOML description of themselves. -type Unmarshaler interface { - UnmarshalTOML(interface{}) error -} - -/* -Marshal returns the TOML encoding of v. Behavior is similar to the Go json -encoder, except that there is no concept of a Marshaler interface or MarshalTOML -function for sub-structs, and currently only definite types can be marshaled -(i.e. no `interface{}`). - -The following struct annotations are supported: - - toml:"Field" Overrides the field's name to output. - omitempty When set, empty values and groups are not emitted. - comment:"comment" Emits a # comment on the same line. This supports new lines. - commented:"true" Emits the value as commented. - -Note that pointers are automatically assigned the "omitempty" option, as TOML -explicitly does not handle null values (saying instead the label should be -dropped). - -Tree structural types and corresponding marshal types: - - *Tree (*)struct, (*)map[string]interface{} - []*Tree (*)[](*)struct, (*)[](*)map[string]interface{} - []interface{} (as interface{}) (*)[]primitive, (*)[]([]interface{}) - interface{} (*)primitive - -Tree primitive types and corresponding marshal types: - - uint64 uint, uint8-uint64, pointers to same - int64 int, int8-uint64, pointers to same - float64 float32, float64, pointers to same - string string, pointers to same - bool bool, pointers to same - time.LocalTime time.LocalTime{}, pointers to same - -For additional flexibility, use the Encoder API. -*/ -func Marshal(v interface{}) ([]byte, error) { - return NewEncoder(nil).marshal(v) -} - -// Encoder writes TOML values to an output stream. -type Encoder struct { - w io.Writer - encOpts - annotation - line int - col int - order marshalOrder - promoteAnon bool - indentation string -} - -// NewEncoder returns a new encoder that writes to w. -func NewEncoder(w io.Writer) *Encoder { - return &Encoder{ - w: w, - encOpts: encOptsDefaults, - annotation: annotationDefault, - line: 0, - col: 1, - order: OrderAlphabetical, - indentation: " ", - } -} - -// Encode writes the TOML encoding of v to the stream. -// -// See the documentation for Marshal for details. -func (e *Encoder) Encode(v interface{}) error { - b, err := e.marshal(v) - if err != nil { - return err - } - if _, err := e.w.Write(b); err != nil { - return err - } - return nil -} - -// QuoteMapKeys sets up the encoder to encode -// maps with string type keys with quoted TOML keys. -// -// This relieves the character limitations on map keys. -func (e *Encoder) QuoteMapKeys(v bool) *Encoder { - e.quoteMapKeys = v - return e -} - -// ArraysWithOneElementPerLine sets up the encoder to encode arrays -// with more than one element on multiple lines instead of one. -// -// For example: -// -// A = [1,2,3] -// -// Becomes -// -// A = [ -// 1, -// 2, -// 3, -// ] -func (e *Encoder) ArraysWithOneElementPerLine(v bool) *Encoder { - e.arraysOneElementPerLine = v - return e -} - -// Order allows to change in which order fields will be written to the output stream. -func (e *Encoder) Order(ord marshalOrder) *Encoder { - e.order = ord - return e -} - -// Indentation allows to change indentation when marshalling. -func (e *Encoder) Indentation(indent string) *Encoder { - e.indentation = indent - return e -} - -// SetTagName allows changing default tag "toml" -func (e *Encoder) SetTagName(v string) *Encoder { - e.tag = v - return e -} - -// SetTagComment allows changing default tag "comment" -func (e *Encoder) SetTagComment(v string) *Encoder { - e.comment = v - return e -} - -// SetTagCommented allows changing default tag "commented" -func (e *Encoder) SetTagCommented(v string) *Encoder { - e.commented = v - return e -} - -// SetTagMultiline allows changing default tag "multiline" -func (e *Encoder) SetTagMultiline(v string) *Encoder { - e.multiline = v - return e -} - -// PromoteAnonymous allows to change how anonymous struct fields are marshaled. -// Usually, they are marshaled as if the inner exported fields were fields in -// the outer struct. However, if an anonymous struct field is given a name in -// its TOML tag, it is treated like a regular struct field with that name. -// rather than being anonymous. -// -// In case anonymous promotion is enabled, all anonymous structs are promoted -// and treated like regular struct fields. -func (e *Encoder) PromoteAnonymous(promote bool) *Encoder { - e.promoteAnon = promote - return e -} - -func (e *Encoder) marshal(v interface{}) ([]byte, error) { - // Check if indentation is valid - for _, char := range e.indentation { - if !isSpace(char) { - return []byte{}, fmt.Errorf("invalid indentation: must only contains space or tab characters") - } - } - - mtype := reflect.TypeOf(v) - if mtype == nil { - return []byte{}, errors.New("nil cannot be marshaled to TOML") - } - - switch mtype.Kind() { - case reflect.Struct, reflect.Map: - case reflect.Ptr: - if mtype.Elem().Kind() != reflect.Struct { - return []byte{}, errors.New("Only pointer to struct can be marshaled to TOML") - } - if reflect.ValueOf(v).IsNil() { - return []byte{}, errors.New("nil pointer cannot be marshaled to TOML") - } - default: - return []byte{}, errors.New("Only a struct or map can be marshaled to TOML") - } - - sval := reflect.ValueOf(v) - if isCustomMarshaler(mtype) { - return callCustomMarshaler(sval) - } - if isTextMarshaler(mtype) { - return callTextMarshaler(sval) - } - t, err := e.valueToTree(mtype, sval) - if err != nil { - return []byte{}, err - } - - var buf bytes.Buffer - _, err = t.writeToOrdered(&buf, "", "", 0, e.arraysOneElementPerLine, e.order, e.indentation, false) - - return buf.Bytes(), err -} - -// Create next tree with a position based on Encoder.line -func (e *Encoder) nextTree() *Tree { - return newTreeWithPosition(Position{Line: e.line, Col: 1}) -} - -// Convert given marshal struct or map value to toml tree -func (e *Encoder) valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, error) { - if mtype.Kind() == reflect.Ptr { - return e.valueToTree(mtype.Elem(), mval.Elem()) - } - tval := e.nextTree() - switch mtype.Kind() { - case reflect.Struct: - switch mval.Interface().(type) { - case Tree: - reflect.ValueOf(tval).Elem().Set(mval) - default: - for i := 0; i < mtype.NumField(); i++ { - mtypef, mvalf := mtype.Field(i), mval.Field(i) - opts := tomlOptions(mtypef, e.annotation) - if opts.include && ((mtypef.Type.Kind() != reflect.Interface && !opts.omitempty) || !isZero(mvalf)) { - val, err := e.valueToToml(mtypef.Type, mvalf) - if err != nil { - return nil, err - } - if tree, ok := val.(*Tree); ok && mtypef.Anonymous && !opts.nameFromTag && !e.promoteAnon { - e.appendTree(tval, tree) - } else { - tval.SetPathWithOptions([]string{opts.name}, SetOptions{ - Comment: opts.comment, - Commented: opts.commented, - Multiline: opts.multiline, - }, val) - } - } - } - } - case reflect.Map: - keys := mval.MapKeys() - if e.order == OrderPreserve && len(keys) > 0 { - // Sorting []reflect.Value is not straight forward. - // - // OrderPreserve will support deterministic results when string is used - // as the key to maps. - typ := keys[0].Type() - kind := keys[0].Kind() - if kind == reflect.String { - ikeys := make([]string, len(keys)) - for i := range keys { - ikeys[i] = keys[i].Interface().(string) - } - sort.Strings(ikeys) - for i := range ikeys { - keys[i] = reflect.ValueOf(ikeys[i]).Convert(typ) - } - } - } - for _, key := range keys { - mvalf := mval.MapIndex(key) - if (mtype.Elem().Kind() == reflect.Ptr || mtype.Elem().Kind() == reflect.Interface) && mvalf.IsNil() { - continue - } - val, err := e.valueToToml(mtype.Elem(), mvalf) - if err != nil { - return nil, err - } - if e.quoteMapKeys { - keyStr, err := tomlValueStringRepresentation(key.String(), "", "", e.order, e.arraysOneElementPerLine) - if err != nil { - return nil, err - } - tval.SetPath([]string{keyStr}, val) - } else { - tval.SetPath([]string{key.String()}, val) - } - } - } - return tval, nil -} - -// Convert given marshal slice to slice of Toml trees -func (e *Encoder) valueToTreeSlice(mtype reflect.Type, mval reflect.Value) ([]*Tree, error) { - tval := make([]*Tree, mval.Len(), mval.Len()) - for i := 0; i < mval.Len(); i++ { - val, err := e.valueToTree(mtype.Elem(), mval.Index(i)) - if err != nil { - return nil, err - } - tval[i] = val - } - return tval, nil -} - -// Convert given marshal slice to slice of toml values -func (e *Encoder) valueToOtherSlice(mtype reflect.Type, mval reflect.Value) (interface{}, error) { - tval := make([]interface{}, mval.Len(), mval.Len()) - for i := 0; i < mval.Len(); i++ { - val, err := e.valueToToml(mtype.Elem(), mval.Index(i)) - if err != nil { - return nil, err - } - tval[i] = val - } - return tval, nil -} - -// Convert given marshal value to toml value -func (e *Encoder) valueToToml(mtype reflect.Type, mval reflect.Value) (interface{}, error) { - e.line++ - if mtype.Kind() == reflect.Ptr { - switch { - case isCustomMarshaler(mtype): - return callCustomMarshaler(mval) - case isTextMarshaler(mtype): - return callTextMarshaler(mval) - default: - return e.valueToToml(mtype.Elem(), mval.Elem()) - } - } - if mtype.Kind() == reflect.Interface { - return e.valueToToml(mval.Elem().Type(), mval.Elem()) - } - switch { - case isCustomMarshaler(mtype): - return callCustomMarshaler(mval) - case isTextMarshaler(mtype): - return callTextMarshaler(mval) - case isTree(mtype): - return e.valueToTree(mtype, mval) - case isOtherSequence(mtype), isCustomMarshalerSequence(mtype), isTextMarshalerSequence(mtype): - return e.valueToOtherSlice(mtype, mval) - case isTreeSequence(mtype): - return e.valueToTreeSlice(mtype, mval) - default: - switch mtype.Kind() { - case reflect.Bool: - return mval.Bool(), nil - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - if mtype.Kind() == reflect.Int64 && mtype == reflect.TypeOf(time.Duration(1)) { - return fmt.Sprint(mval), nil - } - return mval.Int(), nil - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - return mval.Uint(), nil - case reflect.Float32, reflect.Float64: - return mval.Float(), nil - case reflect.String: - return mval.String(), nil - case reflect.Struct: - return mval.Interface(), nil - default: - return nil, fmt.Errorf("Marshal can't handle %v(%v)", mtype, mtype.Kind()) - } - } -} - -func (e *Encoder) appendTree(t, o *Tree) error { - for key, value := range o.values { - if _, ok := t.values[key]; ok { - continue - } - if tomlValue, ok := value.(*tomlValue); ok { - tomlValue.position.Col = t.position.Col - } - t.values[key] = value - } - return nil -} - -// Unmarshal attempts to unmarshal the Tree into a Go struct pointed by v. -// Neither Unmarshaler interfaces nor UnmarshalTOML functions are supported for -// sub-structs, and only definite types can be unmarshaled. -func (t *Tree) Unmarshal(v interface{}) error { - d := Decoder{tval: t, tagName: tagFieldName} - return d.unmarshal(v) -} - -// Marshal returns the TOML encoding of Tree. -// See Marshal() documentation for types mapping table. -func (t *Tree) Marshal() ([]byte, error) { - var buf bytes.Buffer - _, err := t.WriteTo(&buf) - if err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -// Unmarshal parses the TOML-encoded data and stores the result in the value -// pointed to by v. Behavior is similar to the Go json encoder, except that there -// is no concept of an Unmarshaler interface or UnmarshalTOML function for -// sub-structs, and currently only definite types can be unmarshaled to (i.e. no -// `interface{}`). -// -// The following struct annotations are supported: -// -// toml:"Field" Overrides the field's name to map to. -// default:"foo" Provides a default value. -// -// For default values, only fields of the following types are supported: -// * string -// * bool -// * int -// * int64 -// * float64 -// -// See Marshal() documentation for types mapping table. -func Unmarshal(data []byte, v interface{}) error { - t, err := LoadReader(bytes.NewReader(data)) - if err != nil { - return err - } - return t.Unmarshal(v) -} - -// Decoder reads and decodes TOML values from an input stream. -type Decoder struct { - r io.Reader - tval *Tree - encOpts - tagName string - strict bool - visitor visitorState -} - -// NewDecoder returns a new decoder that reads from r. -func NewDecoder(r io.Reader) *Decoder { - return &Decoder{ - r: r, - encOpts: encOptsDefaults, - tagName: tagFieldName, - } -} - -// Decode reads a TOML-encoded value from it's input -// and unmarshals it in the value pointed at by v. -// -// See the documentation for Marshal for details. -func (d *Decoder) Decode(v interface{}) error { - var err error - d.tval, err = LoadReader(d.r) - if err != nil { - return err - } - return d.unmarshal(v) -} - -// SetTagName allows changing default tag "toml" -func (d *Decoder) SetTagName(v string) *Decoder { - d.tagName = v - return d -} - -// Strict allows changing to strict decoding. Any fields that are found in the -// input data and do not have a corresponding struct member cause an error. -func (d *Decoder) Strict(strict bool) *Decoder { - d.strict = strict - return d -} - -func (d *Decoder) unmarshal(v interface{}) error { - mtype := reflect.TypeOf(v) - if mtype == nil { - return errors.New("nil cannot be unmarshaled from TOML") - } - if mtype.Kind() != reflect.Ptr { - return errors.New("only a pointer to struct or map can be unmarshaled from TOML") - } - - elem := mtype.Elem() - - switch elem.Kind() { - case reflect.Struct, reflect.Map: - default: - return errors.New("only a pointer to struct or map can be unmarshaled from TOML") - } - - if reflect.ValueOf(v).IsNil() { - return errors.New("nil pointer cannot be unmarshaled from TOML") - } - - vv := reflect.ValueOf(v).Elem() - - if d.strict { - d.visitor = newVisitorState(d.tval) - } - - sval, err := d.valueFromTree(elem, d.tval, &vv) - if err != nil { - return err - } - if err := d.visitor.validate(); err != nil { - return err - } - reflect.ValueOf(v).Elem().Set(sval) - return nil -} - -// Convert toml tree to marshal struct or map, using marshal type. When mval1 -// is non-nil, merge fields into the given value instead of allocating a new one. -func (d *Decoder) valueFromTree(mtype reflect.Type, tval *Tree, mval1 *reflect.Value) (reflect.Value, error) { - if mtype.Kind() == reflect.Ptr { - return d.unwrapPointer(mtype, tval, mval1) - } - - // Check if pointer to value implements the Unmarshaler interface. - if mvalPtr := reflect.New(mtype); isCustomUnmarshaler(mvalPtr.Type()) { - d.visitor.visitAll() - - if err := callCustomUnmarshaler(mvalPtr, tval.ToMap()); err != nil { - return reflect.ValueOf(nil), fmt.Errorf("unmarshal toml: %v", err) - } - return mvalPtr.Elem(), nil - } - - var mval reflect.Value - switch mtype.Kind() { - case reflect.Struct: - if mval1 != nil { - mval = *mval1 - } else { - mval = reflect.New(mtype).Elem() - } - - switch mval.Interface().(type) { - case Tree: - mval.Set(reflect.ValueOf(tval).Elem()) - default: - for i := 0; i < mtype.NumField(); i++ { - mtypef := mtype.Field(i) - an := annotation{tag: d.tagName} - opts := tomlOptions(mtypef, an) - if !opts.include { - continue - } - baseKey := opts.name - keysToTry := []string{ - baseKey, - strings.ToLower(baseKey), - strings.ToTitle(baseKey), - strings.ToLower(string(baseKey[0])) + baseKey[1:], - } - - found := false - if tval != nil { - for _, key := range keysToTry { - exists := tval.HasPath([]string{key}) - if !exists { - continue - } - - d.visitor.push(key) - val := tval.GetPath([]string{key}) - fval := mval.Field(i) - mvalf, err := d.valueFromToml(mtypef.Type, val, &fval) - if err != nil { - return mval, formatError(err, tval.GetPositionPath([]string{key})) - } - mval.Field(i).Set(mvalf) - found = true - d.visitor.pop() - break - } - } - - if !found && opts.defaultValue != "" { - mvalf := mval.Field(i) - var val interface{} - var err error - switch mvalf.Kind() { - case reflect.String: - val = opts.defaultValue - case reflect.Bool: - val, err = strconv.ParseBool(opts.defaultValue) - case reflect.Uint: - val, err = strconv.ParseUint(opts.defaultValue, 10, 0) - case reflect.Uint8: - val, err = strconv.ParseUint(opts.defaultValue, 10, 8) - case reflect.Uint16: - val, err = strconv.ParseUint(opts.defaultValue, 10, 16) - case reflect.Uint32: - val, err = strconv.ParseUint(opts.defaultValue, 10, 32) - case reflect.Uint64: - val, err = strconv.ParseUint(opts.defaultValue, 10, 64) - case reflect.Int: - val, err = strconv.ParseInt(opts.defaultValue, 10, 0) - case reflect.Int8: - val, err = strconv.ParseInt(opts.defaultValue, 10, 8) - case reflect.Int16: - val, err = strconv.ParseInt(opts.defaultValue, 10, 16) - case reflect.Int32: - val, err = strconv.ParseInt(opts.defaultValue, 10, 32) - case reflect.Int64: - val, err = strconv.ParseInt(opts.defaultValue, 10, 64) - case reflect.Float32: - val, err = strconv.ParseFloat(opts.defaultValue, 32) - case reflect.Float64: - val, err = strconv.ParseFloat(opts.defaultValue, 64) - default: - return mvalf, fmt.Errorf("unsupported field type for default option") - } - - if err != nil { - return mvalf, err - } - mvalf.Set(reflect.ValueOf(val).Convert(mvalf.Type())) - } - - // save the old behavior above and try to check structs - if !found && opts.defaultValue == "" && mtypef.Type.Kind() == reflect.Struct { - tmpTval := tval - if !mtypef.Anonymous { - tmpTval = nil - } - fval := mval.Field(i) - v, err := d.valueFromTree(mtypef.Type, tmpTval, &fval) - if err != nil { - return v, err - } - mval.Field(i).Set(v) - } - } - } - case reflect.Map: - mval = reflect.MakeMap(mtype) - for _, key := range tval.Keys() { - d.visitor.push(key) - // TODO: path splits key - val := tval.GetPath([]string{key}) - mvalf, err := d.valueFromToml(mtype.Elem(), val, nil) - if err != nil { - return mval, formatError(err, tval.GetPositionPath([]string{key})) - } - mval.SetMapIndex(reflect.ValueOf(key).Convert(mtype.Key()), mvalf) - d.visitor.pop() - } - } - return mval, nil -} - -// Convert toml value to marshal struct/map slice, using marshal type -func (d *Decoder) valueFromTreeSlice(mtype reflect.Type, tval []*Tree) (reflect.Value, error) { - mval, err := makeSliceOrArray(mtype, len(tval)) - if err != nil { - return mval, err - } - - for i := 0; i < len(tval); i++ { - d.visitor.push(strconv.Itoa(i)) - val, err := d.valueFromTree(mtype.Elem(), tval[i], nil) - if err != nil { - return mval, err - } - mval.Index(i).Set(val) - d.visitor.pop() - } - return mval, nil -} - -// Convert toml value to marshal primitive slice, using marshal type -func (d *Decoder) valueFromOtherSlice(mtype reflect.Type, tval []interface{}) (reflect.Value, error) { - mval, err := makeSliceOrArray(mtype, len(tval)) - if err != nil { - return mval, err - } - - for i := 0; i < len(tval); i++ { - val, err := d.valueFromToml(mtype.Elem(), tval[i], nil) - if err != nil { - return mval, err - } - mval.Index(i).Set(val) - } - return mval, nil -} - -// Convert toml value to marshal primitive slice, using marshal type -func (d *Decoder) valueFromOtherSliceI(mtype reflect.Type, tval interface{}) (reflect.Value, error) { - val := reflect.ValueOf(tval) - length := val.Len() - - mval, err := makeSliceOrArray(mtype, length) - if err != nil { - return mval, err - } - - for i := 0; i < length; i++ { - val, err := d.valueFromToml(mtype.Elem(), val.Index(i).Interface(), nil) - if err != nil { - return mval, err - } - mval.Index(i).Set(val) - } - return mval, nil -} - -// Create a new slice or a new array with specified length -func makeSliceOrArray(mtype reflect.Type, tLength int) (reflect.Value, error) { - var mval reflect.Value - switch mtype.Kind() { - case reflect.Slice: - mval = reflect.MakeSlice(mtype, tLength, tLength) - case reflect.Array: - mval = reflect.New(reflect.ArrayOf(mtype.Len(), mtype.Elem())).Elem() - if tLength > mtype.Len() { - return mval, fmt.Errorf("unmarshal: TOML array length (%v) exceeds destination array length (%v)", tLength, mtype.Len()) - } - } - return mval, nil -} - -// Convert toml value to marshal value, using marshal type. When mval1 is non-nil -// and the given type is a struct value, merge fields into it. -func (d *Decoder) valueFromToml(mtype reflect.Type, tval interface{}, mval1 *reflect.Value) (reflect.Value, error) { - if mtype.Kind() == reflect.Ptr { - return d.unwrapPointer(mtype, tval, mval1) - } - - switch t := tval.(type) { - case *Tree: - var mval11 *reflect.Value - if mtype.Kind() == reflect.Struct { - mval11 = mval1 - } - - if isTree(mtype) { - return d.valueFromTree(mtype, t, mval11) - } - - if mtype.Kind() == reflect.Interface { - if mval1 == nil || mval1.IsNil() { - return d.valueFromTree(reflect.TypeOf(map[string]interface{}{}), t, nil) - } else { - return d.valueFromToml(mval1.Elem().Type(), t, nil) - } - } - - return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to a tree", tval, tval) - case []*Tree: - if isTreeSequence(mtype) { - return d.valueFromTreeSlice(mtype, t) - } - if mtype.Kind() == reflect.Interface { - if mval1 == nil || mval1.IsNil() { - return d.valueFromTreeSlice(reflect.TypeOf([]map[string]interface{}{}), t) - } else { - ival := mval1.Elem() - return d.valueFromToml(mval1.Elem().Type(), t, &ival) - } - } - return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to trees", tval, tval) - case []interface{}: - d.visitor.visit() - if isOtherSequence(mtype) { - return d.valueFromOtherSlice(mtype, t) - } - if mtype.Kind() == reflect.Interface { - if mval1 == nil || mval1.IsNil() { - return d.valueFromOtherSlice(reflect.TypeOf([]interface{}{}), t) - } else { - ival := mval1.Elem() - return d.valueFromToml(mval1.Elem().Type(), t, &ival) - } - } - return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to a slice", tval, tval) - default: - d.visitor.visit() - // Check if pointer to value implements the encoding.TextUnmarshaler. - if mvalPtr := reflect.New(mtype); isTextUnmarshaler(mvalPtr.Type()) && !isTimeType(mtype) { - if err := d.unmarshalText(tval, mvalPtr); err != nil { - return reflect.ValueOf(nil), fmt.Errorf("unmarshal text: %v", err) - } - return mvalPtr.Elem(), nil - } - - switch mtype.Kind() { - case reflect.Bool, reflect.Struct: - val := reflect.ValueOf(tval) - - switch val.Type() { - case localDateType: - localDate := val.Interface().(LocalDate) - switch mtype { - case timeType: - return reflect.ValueOf(time.Date(localDate.Year, localDate.Month, localDate.Day, 0, 0, 0, 0, time.Local)), nil - } - case localDateTimeType: - localDateTime := val.Interface().(LocalDateTime) - switch mtype { - case timeType: - return reflect.ValueOf(time.Date( - localDateTime.Date.Year, - localDateTime.Date.Month, - localDateTime.Date.Day, - localDateTime.Time.Hour, - localDateTime.Time.Minute, - localDateTime.Time.Second, - localDateTime.Time.Nanosecond, - time.Local)), nil - } - } - - // if this passes for when mtype is reflect.Struct, tval is a time.LocalTime - if !val.Type().ConvertibleTo(mtype) { - return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v", tval, tval, mtype.String()) - } - - return val.Convert(mtype), nil - case reflect.String: - val := reflect.ValueOf(tval) - // stupidly, int64 is convertible to string. So special case this. - if !val.Type().ConvertibleTo(mtype) || val.Kind() == reflect.Int64 { - return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v", tval, tval, mtype.String()) - } - - return val.Convert(mtype), nil - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - val := reflect.ValueOf(tval) - if mtype.Kind() == reflect.Int64 && mtype == reflect.TypeOf(time.Duration(1)) && val.Kind() == reflect.String { - d, err := time.ParseDuration(val.String()) - if err != nil { - return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v. %s", tval, tval, mtype.String(), err) - } - return reflect.ValueOf(d), nil - } - if !val.Type().ConvertibleTo(mtype) || val.Kind() == reflect.Float64 { - return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v", tval, tval, mtype.String()) - } - if reflect.Indirect(reflect.New(mtype)).OverflowInt(val.Convert(reflect.TypeOf(int64(0))).Int()) { - return reflect.ValueOf(nil), fmt.Errorf("%v(%T) would overflow %v", tval, tval, mtype.String()) - } - - return val.Convert(mtype), nil - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - val := reflect.ValueOf(tval) - if !val.Type().ConvertibleTo(mtype) || val.Kind() == reflect.Float64 { - return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v", tval, tval, mtype.String()) - } - - if val.Convert(reflect.TypeOf(int(1))).Int() < 0 { - return reflect.ValueOf(nil), fmt.Errorf("%v(%T) is negative so does not fit in %v", tval, tval, mtype.String()) - } - if reflect.Indirect(reflect.New(mtype)).OverflowUint(val.Convert(reflect.TypeOf(uint64(0))).Uint()) { - return reflect.ValueOf(nil), fmt.Errorf("%v(%T) would overflow %v", tval, tval, mtype.String()) - } - - return val.Convert(mtype), nil - case reflect.Float32, reflect.Float64: - val := reflect.ValueOf(tval) - if !val.Type().ConvertibleTo(mtype) || val.Kind() == reflect.Int64 { - return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v", tval, tval, mtype.String()) - } - if reflect.Indirect(reflect.New(mtype)).OverflowFloat(val.Convert(reflect.TypeOf(float64(0))).Float()) { - return reflect.ValueOf(nil), fmt.Errorf("%v(%T) would overflow %v", tval, tval, mtype.String()) - } - - return val.Convert(mtype), nil - case reflect.Interface: - if mval1 == nil || mval1.IsNil() { - return reflect.ValueOf(tval), nil - } else { - ival := mval1.Elem() - return d.valueFromToml(mval1.Elem().Type(), t, &ival) - } - case reflect.Slice, reflect.Array: - if isOtherSequence(mtype) && isOtherSequence(reflect.TypeOf(t)) { - return d.valueFromOtherSliceI(mtype, t) - } - return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v(%v)", tval, tval, mtype, mtype.Kind()) - default: - return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v(%v)", tval, tval, mtype, mtype.Kind()) - } - } -} - -func (d *Decoder) unwrapPointer(mtype reflect.Type, tval interface{}, mval1 *reflect.Value) (reflect.Value, error) { - var melem *reflect.Value - - if mval1 != nil && !mval1.IsNil() && (mtype.Elem().Kind() == reflect.Struct || mtype.Elem().Kind() == reflect.Interface) { - elem := mval1.Elem() - melem = &elem - } - - val, err := d.valueFromToml(mtype.Elem(), tval, melem) - if err != nil { - return reflect.ValueOf(nil), err - } - mval := reflect.New(mtype.Elem()) - mval.Elem().Set(val) - return mval, nil -} - -func (d *Decoder) unmarshalText(tval interface{}, mval reflect.Value) error { - var buf bytes.Buffer - fmt.Fprint(&buf, tval) - return callTextUnmarshaler(mval, buf.Bytes()) -} - -func tomlOptions(vf reflect.StructField, an annotation) tomlOpts { - tag := vf.Tag.Get(an.tag) - parse := strings.Split(tag, ",") - var comment string - if c := vf.Tag.Get(an.comment); c != "" { - comment = c - } - commented, _ := strconv.ParseBool(vf.Tag.Get(an.commented)) - multiline, _ := strconv.ParseBool(vf.Tag.Get(an.multiline)) - defaultValue := vf.Tag.Get(tagDefault) - result := tomlOpts{ - name: vf.Name, - nameFromTag: false, - comment: comment, - commented: commented, - multiline: multiline, - include: true, - omitempty: false, - defaultValue: defaultValue, - } - if parse[0] != "" { - if parse[0] == "-" && len(parse) == 1 { - result.include = false - } else { - result.name = strings.Trim(parse[0], " ") - result.nameFromTag = true - } - } - if vf.PkgPath != "" { - result.include = false - } - if len(parse) > 1 && strings.Trim(parse[1], " ") == "omitempty" { - result.omitempty = true - } - if vf.Type.Kind() == reflect.Ptr { - result.omitempty = true - } - return result -} - -func isZero(val reflect.Value) bool { - switch val.Type().Kind() { - case reflect.Slice, reflect.Array, reflect.Map: - return val.Len() == 0 - default: - return reflect.DeepEqual(val.Interface(), reflect.Zero(val.Type()).Interface()) - } -} - -func formatError(err error, pos Position) error { - if err.Error()[0] == '(' { // Error already contains position information - return err - } - return fmt.Errorf("%s: %s", pos, err) -} - -// visitorState keeps track of which keys were unmarshaled. -type visitorState struct { - tree *Tree - path []string - keys map[string]struct{} - active bool -} - -func newVisitorState(tree *Tree) visitorState { - path, result := []string{}, map[string]struct{}{} - insertKeys(path, result, tree) - return visitorState{ - tree: tree, - path: path[:0], - keys: result, - active: true, - } -} - -func (s *visitorState) push(key string) { - if s.active { - s.path = append(s.path, key) - } -} - -func (s *visitorState) pop() { - if s.active { - s.path = s.path[:len(s.path)-1] - } -} - -func (s *visitorState) visit() { - if s.active { - delete(s.keys, strings.Join(s.path, ".")) - } -} - -func (s *visitorState) visitAll() { - if s.active { - for k := range s.keys { - if strings.HasPrefix(k, strings.Join(s.path, ".")) { - delete(s.keys, k) - } - } - } -} - -func (s *visitorState) validate() error { - if !s.active { - return nil - } - undecoded := make([]string, 0, len(s.keys)) - for key := range s.keys { - undecoded = append(undecoded, key) - } - sort.Strings(undecoded) - if len(undecoded) > 0 { - return fmt.Errorf("undecoded keys: %q", undecoded) - } - return nil -} - -func insertKeys(path []string, m map[string]struct{}, tree *Tree) { - for k, v := range tree.values { - switch node := v.(type) { - case []*Tree: - for i, item := range node { - insertKeys(append(path, k, strconv.Itoa(i)), m, item) - } - case *Tree: - insertKeys(append(path, k), m, node) - case *tomlValue: - m[strings.Join(append(path, k), ".")] = struct{}{} - } - } -} diff --git a/vendor/github.com/pelletier/go-toml/marshal_OrderPreserve_test.toml b/vendor/github.com/pelletier/go-toml/marshal_OrderPreserve_test.toml deleted file mode 100644 index 792b72ed72..0000000000 --- a/vendor/github.com/pelletier/go-toml/marshal_OrderPreserve_test.toml +++ /dev/null @@ -1,39 +0,0 @@ -title = "TOML Marshal Testing" - -[basic_lists] - floats = [12.3,45.6,78.9] - bools = [true,false,true] - dates = [1979-05-27T07:32:00Z,1980-05-27T07:32:00Z] - ints = [8001,8001,8002] - uints = [5002,5003] - strings = ["One","Two","Three"] - -[[subdocptrs]] - name = "Second" - -[basic_map] - one = "one" - two = "two" - -[subdoc] - - [subdoc.second] - name = "Second" - - [subdoc.first] - name = "First" - -[basic] - uint = 5001 - bool = true - float = 123.4 - float64 = 123.456782132399 - int = 5000 - string = "Bite me" - date = 1979-05-27T07:32:00Z - -[[subdoclist]] - name = "List.First" - -[[subdoclist]] - name = "List.Second" diff --git a/vendor/github.com/pelletier/go-toml/marshal_test.toml b/vendor/github.com/pelletier/go-toml/marshal_test.toml deleted file mode 100644 index ba5e110bf0..0000000000 --- a/vendor/github.com/pelletier/go-toml/marshal_test.toml +++ /dev/null @@ -1,39 +0,0 @@ -title = "TOML Marshal Testing" - -[basic] - bool = true - date = 1979-05-27T07:32:00Z - float = 123.4 - float64 = 123.456782132399 - int = 5000 - string = "Bite me" - uint = 5001 - -[basic_lists] - bools = [true,false,true] - dates = [1979-05-27T07:32:00Z,1980-05-27T07:32:00Z] - floats = [12.3,45.6,78.9] - ints = [8001,8001,8002] - strings = ["One","Two","Three"] - uints = [5002,5003] - -[basic_map] - one = "one" - two = "two" - -[subdoc] - - [subdoc.first] - name = "First" - - [subdoc.second] - name = "Second" - -[[subdoclist]] - name = "List.First" - -[[subdoclist]] - name = "List.Second" - -[[subdocptrs]] - name = "Second" diff --git a/vendor/github.com/pelletier/go-toml/parser.go b/vendor/github.com/pelletier/go-toml/parser.go deleted file mode 100644 index 7bf40bbdc7..0000000000 --- a/vendor/github.com/pelletier/go-toml/parser.go +++ /dev/null @@ -1,493 +0,0 @@ -// TOML Parser. - -package toml - -import ( - "errors" - "fmt" - "math" - "reflect" - "regexp" - "strconv" - "strings" - "time" -) - -type tomlParser struct { - flowIdx int - flow []token - tree *Tree - currentTable []string - seenTableKeys []string -} - -type tomlParserStateFn func() tomlParserStateFn - -// Formats and panics an error message based on a token -func (p *tomlParser) raiseError(tok *token, msg string, args ...interface{}) { - panic(tok.Position.String() + ": " + fmt.Sprintf(msg, args...)) -} - -func (p *tomlParser) run() { - for state := p.parseStart; state != nil; { - state = state() - } -} - -func (p *tomlParser) peek() *token { - if p.flowIdx >= len(p.flow) { - return nil - } - return &p.flow[p.flowIdx] -} - -func (p *tomlParser) assume(typ tokenType) { - tok := p.getToken() - if tok == nil { - p.raiseError(tok, "was expecting token %s, but token stream is empty", tok) - } - if tok.typ != typ { - p.raiseError(tok, "was expecting token %s, but got %s instead", typ, tok) - } -} - -func (p *tomlParser) getToken() *token { - tok := p.peek() - if tok == nil { - return nil - } - p.flowIdx++ - return tok -} - -func (p *tomlParser) parseStart() tomlParserStateFn { - tok := p.peek() - - // end of stream, parsing is finished - if tok == nil { - return nil - } - - switch tok.typ { - case tokenDoubleLeftBracket: - return p.parseGroupArray - case tokenLeftBracket: - return p.parseGroup - case tokenKey: - return p.parseAssign - case tokenEOF: - return nil - case tokenError: - p.raiseError(tok, "parsing error: %s", tok.String()) - default: - p.raiseError(tok, "unexpected token %s", tok.typ) - } - return nil -} - -func (p *tomlParser) parseGroupArray() tomlParserStateFn { - startToken := p.getToken() // discard the [[ - key := p.getToken() - if key.typ != tokenKeyGroupArray { - p.raiseError(key, "unexpected token %s, was expecting a table array key", key) - } - - // get or create table array element at the indicated part in the path - keys, err := parseKey(key.val) - if err != nil { - p.raiseError(key, "invalid table array key: %s", err) - } - p.tree.createSubTree(keys[:len(keys)-1], startToken.Position) // create parent entries - destTree := p.tree.GetPath(keys) - var array []*Tree - if destTree == nil { - array = make([]*Tree, 0) - } else if target, ok := destTree.([]*Tree); ok && target != nil { - array = destTree.([]*Tree) - } else { - p.raiseError(key, "key %s is already assigned and not of type table array", key) - } - p.currentTable = keys - - // add a new tree to the end of the table array - newTree := newTree() - newTree.position = startToken.Position - array = append(array, newTree) - p.tree.SetPath(p.currentTable, array) - - // remove all keys that were children of this table array - prefix := key.val + "." - found := false - for ii := 0; ii < len(p.seenTableKeys); { - tableKey := p.seenTableKeys[ii] - if strings.HasPrefix(tableKey, prefix) { - p.seenTableKeys = append(p.seenTableKeys[:ii], p.seenTableKeys[ii+1:]...) - } else { - found = (tableKey == key.val) - ii++ - } - } - - // keep this key name from use by other kinds of assignments - if !found { - p.seenTableKeys = append(p.seenTableKeys, key.val) - } - - // move to next parser state - p.assume(tokenDoubleRightBracket) - return p.parseStart -} - -func (p *tomlParser) parseGroup() tomlParserStateFn { - startToken := p.getToken() // discard the [ - key := p.getToken() - if key.typ != tokenKeyGroup { - p.raiseError(key, "unexpected token %s, was expecting a table key", key) - } - for _, item := range p.seenTableKeys { - if item == key.val { - p.raiseError(key, "duplicated tables") - } - } - - p.seenTableKeys = append(p.seenTableKeys, key.val) - keys, err := parseKey(key.val) - if err != nil { - p.raiseError(key, "invalid table array key: %s", err) - } - if err := p.tree.createSubTree(keys, startToken.Position); err != nil { - p.raiseError(key, "%s", err) - } - destTree := p.tree.GetPath(keys) - if target, ok := destTree.(*Tree); ok && target != nil && target.inline { - p.raiseError(key, "could not re-define exist inline table or its sub-table : %s", - strings.Join(keys, ".")) - } - p.assume(tokenRightBracket) - p.currentTable = keys - return p.parseStart -} - -func (p *tomlParser) parseAssign() tomlParserStateFn { - key := p.getToken() - p.assume(tokenEqual) - - parsedKey, err := parseKey(key.val) - if err != nil { - p.raiseError(key, "invalid key: %s", err.Error()) - } - - value := p.parseRvalue() - var tableKey []string - if len(p.currentTable) > 0 { - tableKey = p.currentTable - } else { - tableKey = []string{} - } - - prefixKey := parsedKey[0 : len(parsedKey)-1] - tableKey = append(tableKey, prefixKey...) - - // find the table to assign, looking out for arrays of tables - var targetNode *Tree - switch node := p.tree.GetPath(tableKey).(type) { - case []*Tree: - targetNode = node[len(node)-1] - case *Tree: - targetNode = node - case nil: - // create intermediate - if err := p.tree.createSubTree(tableKey, key.Position); err != nil { - p.raiseError(key, "could not create intermediate group: %s", err) - } - targetNode = p.tree.GetPath(tableKey).(*Tree) - default: - p.raiseError(key, "Unknown table type for path: %s", - strings.Join(tableKey, ".")) - } - - if targetNode.inline { - p.raiseError(key, "could not add key or sub-table to exist inline table or its sub-table : %s", - strings.Join(tableKey, ".")) - } - - // assign value to the found table - keyVal := parsedKey[len(parsedKey)-1] - localKey := []string{keyVal} - finalKey := append(tableKey, keyVal) - if targetNode.GetPath(localKey) != nil { - p.raiseError(key, "The following key was defined twice: %s", - strings.Join(finalKey, ".")) - } - var toInsert interface{} - - switch value.(type) { - case *Tree, []*Tree: - toInsert = value - default: - toInsert = &tomlValue{value: value, position: key.Position} - } - targetNode.values[keyVal] = toInsert - return p.parseStart -} - -var numberUnderscoreInvalidRegexp *regexp.Regexp -var hexNumberUnderscoreInvalidRegexp *regexp.Regexp - -func numberContainsInvalidUnderscore(value string) error { - if numberUnderscoreInvalidRegexp.MatchString(value) { - return errors.New("invalid use of _ in number") - } - return nil -} - -func hexNumberContainsInvalidUnderscore(value string) error { - if hexNumberUnderscoreInvalidRegexp.MatchString(value) { - return errors.New("invalid use of _ in hex number") - } - return nil -} - -func cleanupNumberToken(value string) string { - cleanedVal := strings.Replace(value, "_", "", -1) - return cleanedVal -} - -func (p *tomlParser) parseRvalue() interface{} { - tok := p.getToken() - if tok == nil || tok.typ == tokenEOF { - p.raiseError(tok, "expecting a value") - } - - switch tok.typ { - case tokenString: - return tok.val - case tokenTrue: - return true - case tokenFalse: - return false - case tokenInf: - if tok.val[0] == '-' { - return math.Inf(-1) - } - return math.Inf(1) - case tokenNan: - return math.NaN() - case tokenInteger: - cleanedVal := cleanupNumberToken(tok.val) - var err error - var val int64 - if len(cleanedVal) >= 3 && cleanedVal[0] == '0' { - switch cleanedVal[1] { - case 'x': - err = hexNumberContainsInvalidUnderscore(tok.val) - if err != nil { - p.raiseError(tok, "%s", err) - } - val, err = strconv.ParseInt(cleanedVal[2:], 16, 64) - case 'o': - err = numberContainsInvalidUnderscore(tok.val) - if err != nil { - p.raiseError(tok, "%s", err) - } - val, err = strconv.ParseInt(cleanedVal[2:], 8, 64) - case 'b': - err = numberContainsInvalidUnderscore(tok.val) - if err != nil { - p.raiseError(tok, "%s", err) - } - val, err = strconv.ParseInt(cleanedVal[2:], 2, 64) - default: - panic("invalid base") // the lexer should catch this first - } - } else { - err = numberContainsInvalidUnderscore(tok.val) - if err != nil { - p.raiseError(tok, "%s", err) - } - val, err = strconv.ParseInt(cleanedVal, 10, 64) - } - if err != nil { - p.raiseError(tok, "%s", err) - } - return val - case tokenFloat: - err := numberContainsInvalidUnderscore(tok.val) - if err != nil { - p.raiseError(tok, "%s", err) - } - cleanedVal := cleanupNumberToken(tok.val) - val, err := strconv.ParseFloat(cleanedVal, 64) - if err != nil { - p.raiseError(tok, "%s", err) - } - return val - case tokenDate: - layout := time.RFC3339Nano - if !strings.Contains(tok.val, "T") { - layout = strings.Replace(layout, "T", " ", 1) - } - val, err := time.ParseInLocation(layout, tok.val, time.UTC) - if err != nil { - p.raiseError(tok, "%s", err) - } - return val - case tokenLocalDate: - v := strings.Replace(tok.val, " ", "T", -1) - isDateTime := false - isTime := false - for _, c := range v { - if c == 'T' || c == 't' { - isDateTime = true - break - } - if c == ':' { - isTime = true - break - } - } - - var val interface{} - var err error - - if isDateTime { - val, err = ParseLocalDateTime(v) - } else if isTime { - val, err = ParseLocalTime(v) - } else { - val, err = ParseLocalDate(v) - } - - if err != nil { - p.raiseError(tok, "%s", err) - } - return val - case tokenLeftBracket: - return p.parseArray() - case tokenLeftCurlyBrace: - return p.parseInlineTable() - case tokenEqual: - p.raiseError(tok, "cannot have multiple equals for the same key") - case tokenError: - p.raiseError(tok, "%s", tok) - } - - p.raiseError(tok, "never reached") - - return nil -} - -func tokenIsComma(t *token) bool { - return t != nil && t.typ == tokenComma -} - -func (p *tomlParser) parseInlineTable() *Tree { - tree := newTree() - var previous *token -Loop: - for { - follow := p.peek() - if follow == nil || follow.typ == tokenEOF { - p.raiseError(follow, "unterminated inline table") - } - switch follow.typ { - case tokenRightCurlyBrace: - p.getToken() - break Loop - case tokenKey, tokenInteger, tokenString: - if !tokenIsComma(previous) && previous != nil { - p.raiseError(follow, "comma expected between fields in inline table") - } - key := p.getToken() - p.assume(tokenEqual) - - parsedKey, err := parseKey(key.val) - if err != nil { - p.raiseError(key, "invalid key: %s", err) - } - - value := p.parseRvalue() - tree.SetPath(parsedKey, value) - case tokenComma: - if tokenIsComma(previous) { - p.raiseError(follow, "need field between two commas in inline table") - } - p.getToken() - default: - p.raiseError(follow, "unexpected token type in inline table: %s", follow.String()) - } - previous = follow - } - if tokenIsComma(previous) { - p.raiseError(previous, "trailing comma at the end of inline table") - } - tree.inline = true - return tree -} - -func (p *tomlParser) parseArray() interface{} { - var array []interface{} - arrayType := reflect.TypeOf(newTree()) - for { - follow := p.peek() - if follow == nil || follow.typ == tokenEOF { - p.raiseError(follow, "unterminated array") - } - if follow.typ == tokenRightBracket { - p.getToken() - break - } - val := p.parseRvalue() - if reflect.TypeOf(val) != arrayType { - arrayType = nil - } - array = append(array, val) - follow = p.peek() - if follow == nil || follow.typ == tokenEOF { - p.raiseError(follow, "unterminated array") - } - if follow.typ != tokenRightBracket && follow.typ != tokenComma { - p.raiseError(follow, "missing comma") - } - if follow.typ == tokenComma { - p.getToken() - } - } - - // if the array is a mixed-type array or its length is 0, - // don't convert it to a table array - if len(array) <= 0 { - arrayType = nil - } - // An array of Trees is actually an array of inline - // tables, which is a shorthand for a table array. If the - // array was not converted from []interface{} to []*Tree, - // the two notations would not be equivalent. - if arrayType == reflect.TypeOf(newTree()) { - tomlArray := make([]*Tree, len(array)) - for i, v := range array { - tomlArray[i] = v.(*Tree) - } - return tomlArray - } - return array -} - -func parseToml(flow []token) *Tree { - result := newTree() - result.position = Position{1, 1} - parser := &tomlParser{ - flowIdx: 0, - flow: flow, - tree: result, - currentTable: make([]string, 0), - seenTableKeys: make([]string, 0), - } - parser.run() - return result -} - -func init() { - numberUnderscoreInvalidRegexp = regexp.MustCompile(`([^\d]_|_[^\d])|_$|^_`) - hexNumberUnderscoreInvalidRegexp = regexp.MustCompile(`(^0x_)|([^\da-f]_|_[^\da-f])|_$|^_`) -} diff --git a/vendor/github.com/pelletier/go-toml/position.go b/vendor/github.com/pelletier/go-toml/position.go deleted file mode 100644 index c17bff87ba..0000000000 --- a/vendor/github.com/pelletier/go-toml/position.go +++ /dev/null @@ -1,29 +0,0 @@ -// Position support for go-toml - -package toml - -import ( - "fmt" -) - -// Position of a document element within a TOML document. -// -// Line and Col are both 1-indexed positions for the element's line number and -// column number, respectively. Values of zero or less will cause Invalid(), -// to return true. -type Position struct { - Line int // line within the document - Col int // column within the line -} - -// String representation of the position. -// Displays 1-indexed line and column numbers. -func (p Position) String() string { - return fmt.Sprintf("(%d, %d)", p.Line, p.Col) -} - -// Invalid returns whether or not the position is valid (i.e. with negative or -// null values) -func (p Position) Invalid() bool { - return p.Line <= 0 || p.Col <= 0 -} diff --git a/vendor/github.com/pelletier/go-toml/token.go b/vendor/github.com/pelletier/go-toml/token.go deleted file mode 100644 index 6af4ec46bc..0000000000 --- a/vendor/github.com/pelletier/go-toml/token.go +++ /dev/null @@ -1,134 +0,0 @@ -package toml - -import "fmt" - -// Define tokens -type tokenType int - -const ( - eof = -(iota + 1) -) - -const ( - tokenError tokenType = iota - tokenEOF - tokenComment - tokenKey - tokenString - tokenInteger - tokenTrue - tokenFalse - tokenFloat - tokenInf - tokenNan - tokenEqual - tokenLeftBracket - tokenRightBracket - tokenLeftCurlyBrace - tokenRightCurlyBrace - tokenLeftParen - tokenRightParen - tokenDoubleLeftBracket - tokenDoubleRightBracket - tokenDate - tokenLocalDate - tokenKeyGroup - tokenKeyGroupArray - tokenComma - tokenColon - tokenDollar - tokenStar - tokenQuestion - tokenDot - tokenDotDot - tokenEOL -) - -var tokenTypeNames = []string{ - "Error", - "EOF", - "Comment", - "Key", - "String", - "Integer", - "True", - "False", - "Float", - "Inf", - "NaN", - "=", - "[", - "]", - "{", - "}", - "(", - ")", - "]]", - "[[", - "LocalDate", - "LocalDate", - "KeyGroup", - "KeyGroupArray", - ",", - ":", - "$", - "*", - "?", - ".", - "..", - "EOL", -} - -type token struct { - Position - typ tokenType - val string -} - -func (tt tokenType) String() string { - idx := int(tt) - if idx < len(tokenTypeNames) { - return tokenTypeNames[idx] - } - return "Unknown" -} - -func (t token) String() string { - switch t.typ { - case tokenEOF: - return "EOF" - case tokenError: - return t.val - } - - return fmt.Sprintf("%q", t.val) -} - -func isSpace(r rune) bool { - return r == ' ' || r == '\t' -} - -func isAlphanumeric(r rune) bool { - return 'a' <= r && r <= 'z' || 'A' <= r && r <= 'Z' || r == '_' -} - -func isKeyChar(r rune) bool { - // Keys start with the first character that isn't whitespace or [ and end - // with the last non-whitespace character before the equals sign. Keys - // cannot contain a # character." - return !(r == '\r' || r == '\n' || r == eof || r == '=') -} - -func isKeyStartChar(r rune) bool { - return !(isSpace(r) || r == '\r' || r == '\n' || r == eof || r == '[') -} - -func isDigit(r rune) bool { - return '0' <= r && r <= '9' -} - -func isHexDigit(r rune) bool { - return isDigit(r) || - (r >= 'a' && r <= 'f') || - (r >= 'A' && r <= 'F') -} diff --git a/vendor/github.com/pelletier/go-toml/toml.go b/vendor/github.com/pelletier/go-toml/toml.go deleted file mode 100644 index d323c39bce..0000000000 --- a/vendor/github.com/pelletier/go-toml/toml.go +++ /dev/null @@ -1,399 +0,0 @@ -package toml - -import ( - "errors" - "fmt" - "io" - "io/ioutil" - "os" - "runtime" - "strings" -) - -type tomlValue struct { - value interface{} // string, int64, uint64, float64, bool, time.Time, [] of any of this list - comment string - commented bool - multiline bool - position Position -} - -// Tree is the result of the parsing of a TOML file. -type Tree struct { - values map[string]interface{} // string -> *tomlValue, *Tree, []*Tree - comment string - commented bool - inline bool - position Position -} - -func newTree() *Tree { - return newTreeWithPosition(Position{}) -} - -func newTreeWithPosition(pos Position) *Tree { - return &Tree{ - values: make(map[string]interface{}), - position: pos, - } -} - -// TreeFromMap initializes a new Tree object using the given map. -func TreeFromMap(m map[string]interface{}) (*Tree, error) { - result, err := toTree(m) - if err != nil { - return nil, err - } - return result.(*Tree), nil -} - -// Position returns the position of the tree. -func (t *Tree) Position() Position { - return t.position -} - -// Has returns a boolean indicating if the given key exists. -func (t *Tree) Has(key string) bool { - if key == "" { - return false - } - return t.HasPath(strings.Split(key, ".")) -} - -// HasPath returns true if the given path of keys exists, false otherwise. -func (t *Tree) HasPath(keys []string) bool { - return t.GetPath(keys) != nil -} - -// Keys returns the keys of the toplevel tree (does not recurse). -func (t *Tree) Keys() []string { - keys := make([]string, len(t.values)) - i := 0 - for k := range t.values { - keys[i] = k - i++ - } - return keys -} - -// Get the value at key in the Tree. -// Key is a dot-separated path (e.g. a.b.c) without single/double quoted strings. -// If you need to retrieve non-bare keys, use GetPath. -// Returns nil if the path does not exist in the tree. -// If keys is of length zero, the current tree is returned. -func (t *Tree) Get(key string) interface{} { - if key == "" { - return t - } - return t.GetPath(strings.Split(key, ".")) -} - -// GetPath returns the element in the tree indicated by 'keys'. -// If keys is of length zero, the current tree is returned. -func (t *Tree) GetPath(keys []string) interface{} { - if len(keys) == 0 { - return t - } - subtree := t - for _, intermediateKey := range keys[:len(keys)-1] { - value, exists := subtree.values[intermediateKey] - if !exists { - return nil - } - switch node := value.(type) { - case *Tree: - subtree = node - case []*Tree: - // go to most recent element - if len(node) == 0 { - return nil - } - subtree = node[len(node)-1] - default: - return nil // cannot navigate through other node types - } - } - // branch based on final node type - switch node := subtree.values[keys[len(keys)-1]].(type) { - case *tomlValue: - return node.value - default: - return node - } -} - -// GetPosition returns the position of the given key. -func (t *Tree) GetPosition(key string) Position { - if key == "" { - return t.position - } - return t.GetPositionPath(strings.Split(key, ".")) -} - -// GetPositionPath returns the element in the tree indicated by 'keys'. -// If keys is of length zero, the current tree is returned. -func (t *Tree) GetPositionPath(keys []string) Position { - if len(keys) == 0 { - return t.position - } - subtree := t - for _, intermediateKey := range keys[:len(keys)-1] { - value, exists := subtree.values[intermediateKey] - if !exists { - return Position{0, 0} - } - switch node := value.(type) { - case *Tree: - subtree = node - case []*Tree: - // go to most recent element - if len(node) == 0 { - return Position{0, 0} - } - subtree = node[len(node)-1] - default: - return Position{0, 0} - } - } - // branch based on final node type - switch node := subtree.values[keys[len(keys)-1]].(type) { - case *tomlValue: - return node.position - case *Tree: - return node.position - case []*Tree: - // go to most recent element - if len(node) == 0 { - return Position{0, 0} - } - return node[len(node)-1].position - default: - return Position{0, 0} - } -} - -// GetDefault works like Get but with a default value -func (t *Tree) GetDefault(key string, def interface{}) interface{} { - val := t.Get(key) - if val == nil { - return def - } - return val -} - -// SetOptions arguments are supplied to the SetWithOptions and SetPathWithOptions functions to modify marshalling behaviour. -// The default values within the struct are valid default options. -type SetOptions struct { - Comment string - Commented bool - Multiline bool -} - -// SetWithOptions is the same as Set, but allows you to provide formatting -// instructions to the key, that will be used by Marshal(). -func (t *Tree) SetWithOptions(key string, opts SetOptions, value interface{}) { - t.SetPathWithOptions(strings.Split(key, "."), opts, value) -} - -// SetPathWithOptions is the same as SetPath, but allows you to provide -// formatting instructions to the key, that will be reused by Marshal(). -func (t *Tree) SetPathWithOptions(keys []string, opts SetOptions, value interface{}) { - subtree := t - for i, intermediateKey := range keys[:len(keys)-1] { - nextTree, exists := subtree.values[intermediateKey] - if !exists { - nextTree = newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col}) - subtree.values[intermediateKey] = nextTree // add new element here - } - switch node := nextTree.(type) { - case *Tree: - subtree = node - case []*Tree: - // go to most recent element - if len(node) == 0 { - // create element if it does not exist - subtree.values[intermediateKey] = append(node, newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col})) - } - subtree = node[len(node)-1] - } - } - - var toInsert interface{} - - switch v := value.(type) { - case *Tree: - v.comment = opts.Comment - v.commented = opts.Commented - toInsert = value - case []*Tree: - for i := range v { - v[i].commented = opts.Commented - } - toInsert = value - case *tomlValue: - v.comment = opts.Comment - toInsert = v - default: - toInsert = &tomlValue{value: value, - comment: opts.Comment, - commented: opts.Commented, - multiline: opts.Multiline, - position: Position{Line: subtree.position.Line + len(subtree.values) + 1, Col: subtree.position.Col}} - } - - subtree.values[keys[len(keys)-1]] = toInsert -} - -// Set an element in the tree. -// Key is a dot-separated path (e.g. a.b.c). -// Creates all necessary intermediate trees, if needed. -func (t *Tree) Set(key string, value interface{}) { - t.SetWithComment(key, "", false, value) -} - -// SetWithComment is the same as Set, but allows you to provide comment -// information to the key, that will be reused by Marshal(). -func (t *Tree) SetWithComment(key string, comment string, commented bool, value interface{}) { - t.SetPathWithComment(strings.Split(key, "."), comment, commented, value) -} - -// SetPath sets an element in the tree. -// Keys is an array of path elements (e.g. {"a","b","c"}). -// Creates all necessary intermediate trees, if needed. -func (t *Tree) SetPath(keys []string, value interface{}) { - t.SetPathWithComment(keys, "", false, value) -} - -// SetPathWithComment is the same as SetPath, but allows you to provide comment -// information to the key, that will be reused by Marshal(). -func (t *Tree) SetPathWithComment(keys []string, comment string, commented bool, value interface{}) { - t.SetPathWithOptions(keys, SetOptions{Comment: comment, Commented: commented}, value) -} - -// Delete removes a key from the tree. -// Key is a dot-separated path (e.g. a.b.c). -func (t *Tree) Delete(key string) error { - keys, err := parseKey(key) - if err != nil { - return err - } - return t.DeletePath(keys) -} - -// DeletePath removes a key from the tree. -// Keys is an array of path elements (e.g. {"a","b","c"}). -func (t *Tree) DeletePath(keys []string) error { - keyLen := len(keys) - if keyLen == 1 { - delete(t.values, keys[0]) - return nil - } - tree := t.GetPath(keys[:keyLen-1]) - item := keys[keyLen-1] - switch node := tree.(type) { - case *Tree: - delete(node.values, item) - return nil - } - return errors.New("no such key to delete") -} - -// createSubTree takes a tree and a key and create the necessary intermediate -// subtrees to create a subtree at that point. In-place. -// -// e.g. passing a.b.c will create (assuming tree is empty) tree[a], tree[a][b] -// and tree[a][b][c] -// -// Returns nil on success, error object on failure -func (t *Tree) createSubTree(keys []string, pos Position) error { - subtree := t - for i, intermediateKey := range keys { - nextTree, exists := subtree.values[intermediateKey] - if !exists { - tree := newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col}) - tree.position = pos - tree.inline = subtree.inline - subtree.values[intermediateKey] = tree - nextTree = tree - } - - switch node := nextTree.(type) { - case []*Tree: - subtree = node[len(node)-1] - case *Tree: - subtree = node - default: - return fmt.Errorf("unknown type for path %s (%s): %T (%#v)", - strings.Join(keys, "."), intermediateKey, nextTree, nextTree) - } - } - return nil -} - -// LoadBytes creates a Tree from a []byte. -func LoadBytes(b []byte) (tree *Tree, err error) { - defer func() { - if r := recover(); r != nil { - if _, ok := r.(runtime.Error); ok { - panic(r) - } - err = errors.New(r.(string)) - } - }() - - if len(b) >= 4 && (hasUTF32BigEndianBOM4(b) || hasUTF32LittleEndianBOM4(b)) { - b = b[4:] - } else if len(b) >= 3 && hasUTF8BOM3(b) { - b = b[3:] - } else if len(b) >= 2 && (hasUTF16BigEndianBOM2(b) || hasUTF16LittleEndianBOM2(b)) { - b = b[2:] - } - - tree = parseToml(lexToml(b)) - return -} - -func hasUTF16BigEndianBOM2(b []byte) bool { - return b[0] == 0xFE && b[1] == 0xFF -} - -func hasUTF16LittleEndianBOM2(b []byte) bool { - return b[0] == 0xFF && b[1] == 0xFE -} - -func hasUTF8BOM3(b []byte) bool { - return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF -} - -func hasUTF32BigEndianBOM4(b []byte) bool { - return b[0] == 0x00 && b[1] == 0x00 && b[2] == 0xFE && b[3] == 0xFF -} - -func hasUTF32LittleEndianBOM4(b []byte) bool { - return b[0] == 0xFF && b[1] == 0xFE && b[2] == 0x00 && b[3] == 0x00 -} - -// LoadReader creates a Tree from any io.Reader. -func LoadReader(reader io.Reader) (tree *Tree, err error) { - inputBytes, err := ioutil.ReadAll(reader) - if err != nil { - return - } - tree, err = LoadBytes(inputBytes) - return -} - -// Load creates a Tree from a string. -func Load(content string) (tree *Tree, err error) { - return LoadBytes([]byte(content)) -} - -// LoadFile creates a Tree from a file. -func LoadFile(path string) (tree *Tree, err error) { - file, err := os.Open(path) - if err != nil { - return nil, err - } - defer file.Close() - return LoadReader(file) -} diff --git a/vendor/github.com/pelletier/go-toml/tomltree_create.go b/vendor/github.com/pelletier/go-toml/tomltree_create.go deleted file mode 100644 index 79610e9b34..0000000000 --- a/vendor/github.com/pelletier/go-toml/tomltree_create.go +++ /dev/null @@ -1,142 +0,0 @@ -package toml - -import ( - "fmt" - "reflect" - "time" -) - -var kindToType = [reflect.String + 1]reflect.Type{ - reflect.Bool: reflect.TypeOf(true), - reflect.String: reflect.TypeOf(""), - reflect.Float32: reflect.TypeOf(float64(1)), - reflect.Float64: reflect.TypeOf(float64(1)), - reflect.Int: reflect.TypeOf(int64(1)), - reflect.Int8: reflect.TypeOf(int64(1)), - reflect.Int16: reflect.TypeOf(int64(1)), - reflect.Int32: reflect.TypeOf(int64(1)), - reflect.Int64: reflect.TypeOf(int64(1)), - reflect.Uint: reflect.TypeOf(uint64(1)), - reflect.Uint8: reflect.TypeOf(uint64(1)), - reflect.Uint16: reflect.TypeOf(uint64(1)), - reflect.Uint32: reflect.TypeOf(uint64(1)), - reflect.Uint64: reflect.TypeOf(uint64(1)), -} - -// typeFor returns a reflect.Type for a reflect.Kind, or nil if none is found. -// supported values: -// string, bool, int64, uint64, float64, time.Time, int, int8, int16, int32, uint, uint8, uint16, uint32, float32 -func typeFor(k reflect.Kind) reflect.Type { - if k > 0 && int(k) < len(kindToType) { - return kindToType[k] - } - return nil -} - -func simpleValueCoercion(object interface{}) (interface{}, error) { - switch original := object.(type) { - case string, bool, int64, uint64, float64, time.Time: - return original, nil - case int: - return int64(original), nil - case int8: - return int64(original), nil - case int16: - return int64(original), nil - case int32: - return int64(original), nil - case uint: - return uint64(original), nil - case uint8: - return uint64(original), nil - case uint16: - return uint64(original), nil - case uint32: - return uint64(original), nil - case float32: - return float64(original), nil - case fmt.Stringer: - return original.String(), nil - default: - return nil, fmt.Errorf("cannot convert type %T to Tree", object) - } -} - -func sliceToTree(object interface{}) (interface{}, error) { - // arrays are a bit tricky, since they can represent either a - // collection of simple values, which is represented by one - // *tomlValue, or an array of tables, which is represented by an - // array of *Tree. - - // holding the assumption that this function is called from toTree only when value.Kind() is Array or Slice - value := reflect.ValueOf(object) - insideType := value.Type().Elem() - length := value.Len() - if length > 0 { - insideType = reflect.ValueOf(value.Index(0).Interface()).Type() - } - if insideType.Kind() == reflect.Map { - // this is considered as an array of tables - tablesArray := make([]*Tree, 0, length) - for i := 0; i < length; i++ { - table := value.Index(i) - tree, err := toTree(table.Interface()) - if err != nil { - return nil, err - } - tablesArray = append(tablesArray, tree.(*Tree)) - } - return tablesArray, nil - } - - sliceType := typeFor(insideType.Kind()) - if sliceType == nil { - sliceType = insideType - } - - arrayValue := reflect.MakeSlice(reflect.SliceOf(sliceType), 0, length) - - for i := 0; i < length; i++ { - val := value.Index(i).Interface() - simpleValue, err := simpleValueCoercion(val) - if err != nil { - return nil, err - } - arrayValue = reflect.Append(arrayValue, reflect.ValueOf(simpleValue)) - } - return &tomlValue{value: arrayValue.Interface(), position: Position{}}, nil -} - -func toTree(object interface{}) (interface{}, error) { - value := reflect.ValueOf(object) - - if value.Kind() == reflect.Map { - values := map[string]interface{}{} - keys := value.MapKeys() - for _, key := range keys { - if key.Kind() != reflect.String { - if _, ok := key.Interface().(string); !ok { - return nil, fmt.Errorf("map key needs to be a string, not %T (%v)", key.Interface(), key.Kind()) - } - } - - v := value.MapIndex(key) - newValue, err := toTree(v.Interface()) - if err != nil { - return nil, err - } - values[key.String()] = newValue - } - return &Tree{values: values, position: Position{}}, nil - } - - if value.Kind() == reflect.Array || value.Kind() == reflect.Slice { - return sliceToTree(object) - } - - simpleValue, err := simpleValueCoercion(object) - if err != nil { - return nil, err - } - return &tomlValue{value: simpleValue, position: Position{}}, nil -} diff --git a/vendor/github.com/pelletier/go-toml/tomltree_write.go b/vendor/github.com/pelletier/go-toml/tomltree_write.go deleted file mode 100644 index 2d6487ede4..0000000000 --- a/vendor/github.com/pelletier/go-toml/tomltree_write.go +++ /dev/null @@ -1,517 +0,0 @@ -package toml - -import ( - "bytes" - "fmt" - "io" - "math" - "math/big" - "reflect" - "sort" - "strconv" - "strings" - "time" -) - -type valueComplexity int - -const ( - valueSimple valueComplexity = iota + 1 - valueComplex -) - -type sortNode struct { - key string - complexity valueComplexity -} - -// Encodes a string to a TOML-compliant multi-line string value -// This function is a clone of the existing encodeTomlString function, except that whitespace characters -// are preserved. Quotation marks and backslashes are also not escaped. -func encodeMultilineTomlString(value string, commented string) string { - var b bytes.Buffer - adjacentQuoteCount := 0 - - b.WriteString(commented) - for i, rr := range value { - if rr != '"' { - adjacentQuoteCount = 0 - } else { - adjacentQuoteCount++ - } - switch rr { - case '\b': - b.WriteString(`\b`) - case '\t': - b.WriteString("\t") - case '\n': - b.WriteString("\n" + commented) - case '\f': - b.WriteString(`\f`) - case '\r': - b.WriteString("\r") - case '"': - if adjacentQuoteCount >= 3 || i == len(value)-1 { - adjacentQuoteCount = 0 - b.WriteString(`\"`) - } else { - b.WriteString(`"`) - } - case '\\': - b.WriteString(`\`) - default: - intRr := uint16(rr) - if intRr < 0x001F { - b.WriteString(fmt.Sprintf("\\u%0.4X", intRr)) - } else { - b.WriteRune(rr) - } - } - } - return b.String() -} - -// Encodes a string to a TOML-compliant string value -func encodeTomlString(value string) string { - var b bytes.Buffer - - for _, rr := range value { - switch rr { - case '\b': - b.WriteString(`\b`) - case '\t': - b.WriteString(`\t`) - case '\n': - b.WriteString(`\n`) - case '\f': - b.WriteString(`\f`) - case '\r': - b.WriteString(`\r`) - case '"': - b.WriteString(`\"`) - case '\\': - b.WriteString(`\\`) - default: - intRr := uint16(rr) - if intRr < 0x001F { - b.WriteString(fmt.Sprintf("\\u%0.4X", intRr)) - } else { - b.WriteRune(rr) - } - } - } - return b.String() -} - -func tomlTreeStringRepresentation(t *Tree, ord marshalOrder) (string, error) { - var orderedVals []sortNode - switch ord { - case OrderPreserve: - orderedVals = sortByLines(t) - default: - orderedVals = sortAlphabetical(t) - } - - var values []string - for _, node := range orderedVals { - k := node.key - v := t.values[k] - - repr, err := tomlValueStringRepresentation(v, "", "", ord, false) - if err != nil { - return "", err - } - values = append(values, quoteKeyIfNeeded(k)+" = "+repr) - } - return "{ " + strings.Join(values, ", ") + " }", nil -} - -func tomlValueStringRepresentation(v interface{}, commented string, indent string, ord marshalOrder, arraysOneElementPerLine bool) (string, error) { - // this interface check is added to dereference the change made in the writeTo function. - // That change was made to allow this function to see formatting options. - tv, ok := v.(*tomlValue) - if ok { - v = tv.value - } else { - tv = &tomlValue{} - } - - switch value := v.(type) { - case uint64: - return strconv.FormatUint(value, 10), nil - case int64: - return strconv.FormatInt(value, 10), nil - case float64: - // Default bit length is full 64 - bits := 64 - // Float panics if nan is used - if !math.IsNaN(value) { - // if 32 bit accuracy is enough to exactly show, use 32 - _, acc := big.NewFloat(value).Float32() - if acc == big.Exact { - bits = 32 - } - } - if math.Trunc(value) == value { - return strings.ToLower(strconv.FormatFloat(value, 'f', 1, bits)), nil - } - return strings.ToLower(strconv.FormatFloat(value, 'f', -1, bits)), nil - case string: - if tv.multiline { - return "\"\"\"\n" + encodeMultilineTomlString(value, commented) + "\"\"\"", nil - } - return "\"" + encodeTomlString(value) + "\"", nil - case []byte: - b, _ := v.([]byte) - return tomlValueStringRepresentation(string(b), commented, indent, ord, arraysOneElementPerLine) - case bool: - if value { - return "true", nil - } - return "false", nil - case time.Time: - return value.Format(time.RFC3339), nil - case LocalDate: - return value.String(), nil - case LocalDateTime: - return value.String(), nil - case LocalTime: - return value.String(), nil - case *Tree: - return tomlTreeStringRepresentation(value, ord) - case nil: - return "", nil - } - - rv := reflect.ValueOf(v) - - if rv.Kind() == reflect.Slice { - var values []string - for i := 0; i < rv.Len(); i++ { - item := rv.Index(i).Interface() - itemRepr, err := tomlValueStringRepresentation(item, commented, indent, ord, arraysOneElementPerLine) - if err != nil { - return "", err - } - values = append(values, itemRepr) - } - if arraysOneElementPerLine && len(values) > 1 { - stringBuffer := bytes.Buffer{} - valueIndent := indent + ` ` // TODO: move that to a shared encoder state - - stringBuffer.WriteString("[\n") - - for _, value := range values { - stringBuffer.WriteString(valueIndent) - stringBuffer.WriteString(commented + value) - stringBuffer.WriteString(`,`) - stringBuffer.WriteString("\n") - } - - stringBuffer.WriteString(indent + commented + "]") - - return stringBuffer.String(), nil - } - return "[" + strings.Join(values, ", ") + "]", nil - } - return "", fmt.Errorf("unsupported value type %T: %v", v, v) -} - -func getTreeArrayLine(trees []*Tree) (line int) { - // get lowest line number that is not 0 - for _, tv := range trees { - if tv.position.Line < line || line == 0 { - line = tv.position.Line - } - } - return -} - -func sortByLines(t *Tree) (vals []sortNode) { - var ( - line int - lines []int - tv *Tree - tom *tomlValue - node sortNode - ) - vals = make([]sortNode, 0) - m := make(map[int]sortNode) - - for k := range t.values { - v := t.values[k] - switch v.(type) { - case *Tree: - tv = v.(*Tree) - line = tv.position.Line - node = sortNode{key: k, complexity: valueComplex} - case []*Tree: - line = getTreeArrayLine(v.([]*Tree)) - node = sortNode{key: k, complexity: valueComplex} - default: - tom = v.(*tomlValue) - line = tom.position.Line - node = sortNode{key: k, complexity: valueSimple} - } - lines = append(lines, line) - vals = append(vals, node) - m[line] = node - } - sort.Ints(lines) - - for i, line := range lines { - vals[i] = m[line] - } - - return vals -} - -func sortAlphabetical(t *Tree) (vals []sortNode) { - var ( - node sortNode - simpVals []string - compVals []string - ) - vals = make([]sortNode, 0) - m := make(map[string]sortNode) - - for k := range t.values { - v := t.values[k] - switch v.(type) { - case *Tree, []*Tree: - node = sortNode{key: k, complexity: valueComplex} - compVals = append(compVals, node.key) - default: - node = sortNode{key: k, complexity: valueSimple} - simpVals = append(simpVals, node.key) - } - vals = append(vals, node) - m[node.key] = node - } - - // Simples first to match previous implementation - sort.Strings(simpVals) - i := 0 - for _, key := range simpVals { - vals[i] = m[key] - i++ - } - - sort.Strings(compVals) - for _, key := range compVals { - vals[i] = m[key] - i++ - } - - return vals -} - -func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64, arraysOneElementPerLine bool) (int64, error) { - return t.writeToOrdered(w, indent, keyspace, bytesCount, arraysOneElementPerLine, OrderAlphabetical, " ", false) -} - -func (t *Tree) writeToOrdered(w io.Writer, indent, keyspace string, bytesCount int64, arraysOneElementPerLine bool, ord marshalOrder, indentString string, parentCommented bool) (int64, error) { - var orderedVals []sortNode - - switch ord { - case OrderPreserve: - orderedVals = sortByLines(t) - default: - orderedVals = sortAlphabetical(t) - } - - for _, node := range orderedVals { - switch node.complexity { - case valueComplex: - k := node.key - v := t.values[k] - - combinedKey := quoteKeyIfNeeded(k) - if keyspace != "" { - combinedKey = keyspace + "." + combinedKey - } - - switch node := v.(type) { - // node has to be of those two types given how keys are sorted above - case *Tree: - tv, ok := t.values[k].(*Tree) - if !ok { - return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k]) - } - if tv.comment != "" { - comment := strings.Replace(tv.comment, "\n", "\n"+indent+"#", -1) - start := "# " - if strings.HasPrefix(comment, "#") { - start = "" - } - writtenBytesCountComment, errc := writeStrings(w, "\n", indent, start, comment) - bytesCount += int64(writtenBytesCountComment) - if errc != nil { - return bytesCount, errc - } - } - - var commented string - if parentCommented || t.commented || tv.commented { - commented = "# " - } - writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[", combinedKey, "]\n") - bytesCount += int64(writtenBytesCount) - if err != nil { - return bytesCount, err - } - bytesCount, err = node.writeToOrdered(w, indent+indentString, combinedKey, bytesCount, arraysOneElementPerLine, ord, indentString, parentCommented || t.commented || tv.commented) - if err != nil { - return bytesCount, err - } - case []*Tree: - for _, subTree := range node { - var commented string - if parentCommented || t.commented || subTree.commented { - commented = "# " - } - writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[[", combinedKey, "]]\n") - bytesCount += int64(writtenBytesCount) - if err != nil { - return bytesCount, err - } - - bytesCount, err = subTree.writeToOrdered(w, indent+indentString, combinedKey, bytesCount, arraysOneElementPerLine, ord, indentString, parentCommented || t.commented || subTree.commented) - if err != nil { - return bytesCount, err - } - } - } - default: // Simple - k := node.key - v, ok := t.values[k].(*tomlValue) - if !ok { - return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k]) - } - - var commented string - if parentCommented || t.commented || v.commented { - commented = "# " - } - repr, err := tomlValueStringRepresentation(v, commented, indent, ord, arraysOneElementPerLine) - if err != nil { - return bytesCount, err - } - - if v.comment != "" { - comment := strings.Replace(v.comment, "\n", "\n"+indent+"#", -1) - start := "# " - if strings.HasPrefix(comment, "#") { - start = "" - } - writtenBytesCountComment, errc := writeStrings(w, "\n", indent, start, comment, "\n") - bytesCount += int64(writtenBytesCountComment) - if errc != nil { - return bytesCount, errc - } - } - - quotedKey := quoteKeyIfNeeded(k) - writtenBytesCount, err := writeStrings(w, indent, commented, quotedKey, " = ", repr, "\n") - bytesCount += int64(writtenBytesCount) - if err != nil { - return bytesCount, err - } - } - } - - return bytesCount, nil -} - -// quote a key if it does not fit the bare key format (A-Za-z0-9_-) -// quoted keys use the same rules as strings -func quoteKeyIfNeeded(k string) string { - // when encoding a map with the 'quoteMapKeys' option enabled, the tree will contain - // keys that have already been quoted. - // not an ideal situation, but good enough of a stop gap. - if len(k) >= 2 && k[0] == '"' && k[len(k)-1] == '"' { - return k - } - isBare := true - for _, r := range k { - if !isValidBareChar(r) { - isBare = false - break - } - } - if isBare { - return k - } - return quoteKey(k) -} - -func quoteKey(k string) string { - return "\"" + encodeTomlString(k) + "\"" -} - -func writeStrings(w io.Writer, s ...string) (int, error) { - var n int - for i := range s { - b, err := io.WriteString(w, s[i]) - n += b - if err != nil { - return n, err - } - } - return n, nil -} - -// WriteTo encode the Tree as Toml and writes it to the writer w. -// Returns the number of bytes written in case of success, or an error if anything happened. -func (t *Tree) WriteTo(w io.Writer) (int64, error) { - return t.writeTo(w, "", "", 0, false) -} - -// ToTomlString generates a human-readable representation of the current tree. -// Output spans multiple lines, and is suitable for ingest by a TOML parser. -// If the conversion cannot be performed, ToString returns a non-nil error. -func (t *Tree) ToTomlString() (string, error) { - b, err := t.Marshal() - if err != nil { - return "", err - } - return string(b), nil -} - -// String generates a human-readable representation of the current tree. -// Alias of ToString. Present to implement the fmt.Stringer interface. -func (t *Tree) String() string { - result, _ := t.ToTomlString() - return result -} - -// ToMap recursively generates a representation of the tree using Go built-in structures. -// The following types are used: -// -// * bool -// * float64 -// * int64 -// * string -// * uint64 -// * time.Time -// * map[string]interface{} (where interface{} is any of this list) -// * []interface{} (where interface{} is any of this list) -func (t *Tree) ToMap() map[string]interface{} { - result := map[string]interface{}{} - - for k, v := range t.values { - switch node := v.(type) { - case []*Tree: - var array []interface{} - for _, item := range node { - array = append(array, item.ToMap()) - } - result[k] = array - case *Tree: - result[k] = node.ToMap() - case *tomlValue: - result[k] = node.value - } - } - return result -} diff --git a/vendor/github.com/pelletier/go-toml/.dockerignore b/vendor/github.com/pelletier/go-toml/v2/.dockerignore similarity index 100% rename from vendor/github.com/pelletier/go-toml/.dockerignore rename to vendor/github.com/pelletier/go-toml/v2/.dockerignore diff --git a/vendor/github.com/pelletier/go-toml/.gitignore b/vendor/github.com/pelletier/go-toml/v2/.gitignore similarity index 100% rename from vendor/github.com/pelletier/go-toml/.gitignore rename to vendor/github.com/pelletier/go-toml/v2/.gitignore diff --git a/vendor/github.com/pelletier/go-toml/v2/.golangci.toml b/vendor/github.com/pelletier/go-toml/v2/.golangci.toml new file mode 100644 index 0000000000..fdf167b4ab --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/.golangci.toml @@ -0,0 +1,84 @@ +[service] +golangci-lint-version = "1.39.0" + +[linters-settings.wsl] +allow-assign-and-anything = true + +[linters-settings.exhaustive] +default-signifies-exhaustive = true + +[linters] +disable-all = true +enable = [ + "asciicheck", + "bodyclose", + "cyclop", + "deadcode", + "depguard", + "dogsled", + "dupl", + "durationcheck", + "errcheck", + "errorlint", + "exhaustive", + # "exhaustivestruct", + "exportloopref", + "forbidigo", + # "forcetypeassert", + "funlen", + "gci", + # "gochecknoglobals", + "gochecknoinits", + "gocognit", + "goconst", + "gocritic", + "gocyclo", + "godot", + "godox", + # "goerr113", + "gofmt", + "gofumpt", + "goheader", + "goimports", + "golint", + "gomnd", + # "gomoddirectives", + "gomodguard", + "goprintffuncname", + "gosec", + "gosimple", + "govet", + # "ifshort", + "importas", + "ineffassign", + "lll", + "makezero", + "misspell", + "nakedret", + "nestif", + "nilerr", + # "nlreturn", + "noctx", + "nolintlint", + "paralleltest", + "prealloc", + "predeclared", + "revive", + "rowserrcheck", + "sqlclosecheck", + "staticcheck", + "structcheck", + "stylecheck", + # "testpackage", + "thelper", + "tparallel", + "typecheck", + "unconvert", + "unparam", + "unused", + "varcheck", + "wastedassign", + "whitespace", + # "wrapcheck", + # "wsl" +] diff --git a/vendor/github.com/pelletier/go-toml/v2/CONTRIBUTING.md b/vendor/github.com/pelletier/go-toml/v2/CONTRIBUTING.md new file mode 100644 index 0000000000..59658d39d0 --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/CONTRIBUTING.md @@ -0,0 +1,182 @@ +# Contributing + +Thank you for your interest in go-toml! We appreciate you considering +contributing to go-toml! + +The main goal is the project is to provide an easy-to-use and efficient TOML +implementation for Go that gets the job done and gets out of your way – dealing +with TOML is probably not the central piece of your project. + +As the single maintainer of go-toml, time is scarce. All help, big or small, is +more than welcomed! + +## Ask questions + +Any question you may have, somebody else might have it too. Always feel free to +ask them on the [discussion board][discussions]. We will try to answer them as +clearly and quickly as possible, time permitting. + +Asking questions also helps us identify areas where the documentation needs +improvement, or new features that weren't envisioned before. Sometimes, a +seemingly innocent question leads to the fix of a bug. Don't hesitate and ask +away! + +[discussions]: https://github.com/pelletier/go-toml/discussions + +## Improve the documentation + +The best way to share your knowledge and experience with go-toml is to improve +the documentation. Fix a typo, clarify an interface, add an example, anything +goes! + +The documentation is present in the [README][readme] and thorough the source +code. On release, it gets updated on [pkg.go.dev][pkg.go.dev]. To make a change +to the documentation, create a pull request with your proposed changes. For +simple changes like that, the easiest way to go is probably the "Fork this +project and edit the file" button on Github, displayed at the top right of the +file. Unless it's a trivial change (for example a typo), provide a little bit of +context in your pull request description or commit message. + +## Report a bug + +Found a bug! Sorry to hear that :(. Help us and other track them down and fix by +reporting it. [File a new bug report][bug-report] on the [issues +tracker][issues-tracker]. The template should provide enough guidance on what to +include. When in doubt: add more details! By reducing ambiguity and providing +more information, it decreases back and forth and saves everyone time. + +## Code changes + +Want to contribute a patch? Very happy to hear that! + +First, some high-level rules: + +- A short proposal with some POC code is better than a lengthy piece of text + with no code. Code speaks louder than words. That being said, bigger changes + should probably start with a [discussion][discussions]. +- No backward-incompatible patch will be accepted unless discussed. Sometimes + it's hard, but we try not to break people's programs unless we absolutely have + to. +- If you are writing a new feature or extending an existing one, make sure to + write some documentation. +- Bug fixes need to be accompanied with regression tests. +- New code needs to be tested. +- Your commit messages need to explain why the change is needed, even if already + included in the PR description. + +It does sound like a lot, but those best practices are here to save time overall +and continuously improve the quality of the project, which is something everyone +benefits from. + +### Get started + +The fairly standard code contribution process looks like that: + +1. [Fork the project][fork]. +2. Make your changes, commit on any branch you like. +3. [Open up a pull request][pull-request] +4. Review, potential ask for changes. +5. Merge. + +Feel free to ask for help! You can create draft pull requests to gather +some early feedback! + +### Run the tests + +You can run tests for go-toml using Go's test tool: `go test -race ./...`. + +During the pull request process, all tests will be ran on Linux, Windows, and +MacOS on the last two versions of Go. + +However, given GitHub's new policy to _not_ run Actions on pull requests until a +maintainer clicks on button, it is highly recommended that you run them locally +as you make changes. + +### Check coverage + +We use `go tool cover` to compute test coverage. Most code editors have a way to +run and display code coverage, but at the end of the day, we do this: + +``` +go test -covermode=atomic -coverprofile=coverage.out +go tool cover -func=coverage.out +``` + +and verify that the overall percentage of tested code does not go down. This is +a requirement. As a rule of thumb, all lines of code touched by your changes +should be covered. On Unix you can use `./ci.sh coverage -d v2` to check if your +code lowers the coverage. + +### Verify performance + +Go-toml aims to stay efficient. We rely on a set of scenarios executed with Go's +builtin benchmark systems. Because of their noisy nature, containers provided by +Github Actions cannot be reliably used for benchmarking. As a result, you are +responsible for checking that your changes do not incur a performance penalty. +You can run their following to execute benchmarks: + +``` +go test ./... -bench=. -count=10 +``` + +Benchmark results should be compared against each other with +[benchstat][benchstat]. Typical flow looks like this: + +1. On the `v2` branch, run `go test ./... -bench=. -count 10` and save output to + a file (for example `old.txt`). +2. Make some code changes. +3. Run `go test ....` again, and save the output to an other file (for example + `new.txt`). +4. Run `benchstat old.txt new.txt` to check that time/op does not go up in any + test. + +On Unix you can use `./ci.sh benchmark -d v2` to verify how your code impacts +performance. + +It is highly encouraged to add the benchstat results to your pull request +description. Pull requests that lower performance will receive more scrutiny. + +[benchstat]: https://pkg.go.dev/golang.org/x/perf/cmd/benchstat + +### Style + +Try to look around and follow the same format and structure as the rest of the +code. We enforce using `go fmt` on the whole code base. + +--- + +## Maintainers-only + +### Merge pull request + +Checklist: + +- Passing CI. +- Does not introduce backward-incompatible changes (unless discussed). +- Has relevant doc changes. +- Benchstat does not show performance regression. + +1. Merge using "squash and merge". +2. Make sure to edit the commit message to keep all the useful information + nice and clean. +3. Make sure the commit title is clear and contains the PR number (#123). + +### New release + +1. Go to [releases][releases]. Click on "X commits to master since this + release". +2. Make note of all the changes. Look for backward incompatible changes, + new features, and bug fixes. +3. Pick the new version using the above and semver. +4. Create a [new release][new-release]. +5. Follow the same format as [1.1.0][release-110]. + +[issues-tracker]: https://github.com/pelletier/go-toml/issues +[bug-report]: https://github.com/pelletier/go-toml/issues/new?template=bug_report.md +[pkg.go.dev]: https://pkg.go.dev/github.com/pelletier/go-toml +[readme]: ./README.md +[fork]: https://help.github.com/articles/fork-a-repo +[pull-request]: https://help.github.com/en/articles/creating-a-pull-request +[releases]: https://github.com/pelletier/go-toml/releases +[new-release]: https://github.com/pelletier/go-toml/releases/new +[release-110]: https://github.com/pelletier/go-toml/releases/tag/v1.1.0 diff --git a/third_party/VENDOR-LICENSE/github.com/pelletier/go-toml/LICENSE b/vendor/github.com/pelletier/go-toml/v2/LICENSE similarity index 94% rename from third_party/VENDOR-LICENSE/github.com/pelletier/go-toml/LICENSE rename to vendor/github.com/pelletier/go-toml/v2/LICENSE index 583bdae628..3a38ac28ba 100644 --- a/third_party/VENDOR-LICENSE/github.com/pelletier/go-toml/LICENSE +++ b/vendor/github.com/pelletier/go-toml/v2/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2013 - 2017 Thomas Pelletier, Eric Anderton +Copyright (c) 2013 - 2021 Thomas Pelletier, Eric Anderton Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendor/github.com/pelletier/go-toml/v2/README.md b/vendor/github.com/pelletier/go-toml/v2/README.md new file mode 100644 index 0000000000..876f31e567 --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/README.md @@ -0,0 +1,352 @@ +# go-toml v2 + +Go library for the [TOML](https://toml.io/en/) format. + +This library supports [TOML v1.0.0](https://toml.io/en/v1.0.0). + + +## Development status + +This is the upcoming major version of go-toml. It is currently in active +development. As of release v2.0.0-beta.1, the library has reached feature parity +with v1, and fixes a lot known bugs and performance issues along the way. + +If you do not need the advanced document editing features of v1, you are +encouraged to try out this version. + +👉 [Roadmap for v2](https://github.com/pelletier/go-toml/discussions/506). + + +## Documentation + +Full API, examples, and implementation notes are available in the Go documentation. + +[![Go Reference](https://pkg.go.dev/badge/github.com/pelletier/go-toml/v2.svg)](https://pkg.go.dev/github.com/pelletier/go-toml/v2) + + +## Import + +```go +import "github.com/pelletier/go-toml/v2" +``` + +## Features + +### Stdlib behavior + +As much as possible, this library is designed to behave similarly as the +standard library's `encoding/json`. + +### Performance + +While go-toml favors usability, it is written with performance in mind. Most +operations should not be shockingly slow. + +### Strict mode + +`Decoder` can be set to "strict mode", which makes it error when some parts of +the TOML document was not prevent in the target structure. This is a great way +to check for typos. [See example in the documentation][strict]. + +[strict]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#example-Decoder.SetStrict + +### Contextualized errors + +When decoding errors occur, go-toml returns [`DecodeError`][decode-err]), which +contains a human readable contextualized version of the error. For example: + +``` +2| key1 = "value1" +3| key2 = "missing2" + | ~~~~ missing field +4| key3 = "missing3" +5| key4 = "value4" +``` + +[decode-err]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#DecodeError + +### Local date and time support + +TOML supports native [local date/times][ldt]. It allows to represent a given +date, time, or date-time without relation to a timezone or offset. To support +this use-case, go-toml provides [`LocalDate`][tld], [`LocalTime`][tlt], and +[`LocalDateTime`][tldt]. Those types can be transformed to and from `time.Time`, +making them convenient yet unambiguous structures for their respective TOML +representation. + +[ldt]: https://toml.io/en/v1.0.0#local-date-time +[tld]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#LocalDate +[tlt]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#LocalTime +[tldt]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#LocalDateTime + +## Getting started + +Given the following struct, let's see how to read it and write it as TOML: + +```go +type MyConfig struct { + Version int + Name string + Tags []string +} +``` + +### Unmarshaling + +[`Unmarshal`][unmarshal] reads a TOML document and fills a Go structure with its +content. For example: + +```go +doc := ` +version = 2 +name = "go-toml" +tags = ["go", "toml"] +` + +var cfg MyConfig +err := toml.Unmarshal([]byte(doc), &cfg) +if err != nil { + panic(err) +} +fmt.Println("version:", cfg.Version) +fmt.Println("name:", cfg.Name) +fmt.Println("tags:", cfg.Tags) + +// Output: +// version: 2 +// name: go-toml +// tags: [go toml] +``` + +[unmarshal]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#Unmarshal + +### Marshaling + +[`Marshal`][marshal] is the opposite of Unmarshal: it represents a Go structure +as a TOML document: + +```go +cfg := MyConfig{ + Version: 2, + Name: "go-toml", + Tags: []string{"go", "toml"}, +} + +b, err := toml.Marshal(cfg) +if err != nil { + panic(err) +} +fmt.Println(string(b)) + +// Output: +// Version = 2 +// Name = 'go-toml' +// Tags = ['go', 'toml'] +``` + +[marshal]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#Marshal + +## Migrating from v1 + +This section describes the differences between v1 and v2, with some pointers on +how to get the original behavior when possible. + +### Decoding / Unmarshal + +#### Automatic field name guessing + +When unmarshaling to a struct, if a key in the TOML document does not exactly +match the name of a struct field or any of the `toml`-tagged field, v1 tries +multiple variations of the key ([code][v1-keys]). + +V2 instead does a case-insensitive matching, like `encoding/json`. + +This could impact you if you are relying on casing to differentiate two fields, +and one of them is a not using the `toml` struct tag. The recommended solution +is to be specific about tag names for those fields using the `toml` struct tag. + +[v1-keys]: https://github.com/pelletier/go-toml/blob/a2e52561804c6cd9392ebf0048ca64fe4af67a43/marshal.go#L775-L781 + +#### Ignore preexisting value in interface + +When decoding into a non-nil `interface{}`, go-toml v1 uses the type of the +element in the interface to decode the object. For example: + +```go +type inner struct { + B interface{} +} +type doc struct { + A interface{} +} + +d := doc{ + A: inner{ + B: "Before", + }, +} + +data := ` +[A] +B = "After" +` + +toml.Unmarshal([]byte(data), &d) +fmt.Printf("toml v1: %#v\n", d) + +// toml v1: main.doc{A:main.inner{B:"After"}} +``` + +In this case, field `A` is of type `interface{}`, containing a `inner` struct. +V1 sees that type and uses it when decoding the object. + +When decoding an object into an `interface{}`, V2 instead disregards whatever +value the `interface{}` may contain and replaces it with a +`map[string]interface{}`. With the same data structure as above, here is what +the result looks like: + +```go +toml.Unmarshal([]byte(data), &d) +fmt.Printf("toml v2: %#v\n", d) + +// toml v2: main.doc{A:map[string]interface {}{"B":"After"}} +``` + +This is to match `encoding/json`'s behavior. There is no way to make the v2 +decoder behave like v1. + +#### Values out of array bounds ignored + +When decoding into an array, v1 returns an error when the number of elements +contained in the doc is superior to the capacity of the array. For example: + +```go +type doc struct { + A [2]string +} +d := doc{} +err := toml.Unmarshal([]byte(`A = ["one", "two", "many"]`), &d) +fmt.Println(err) + +// (1, 1): unmarshal: TOML array length (3) exceeds destination array length (2) +``` + +In the same situation, v2 ignores the last value: + +```go +err := toml.Unmarshal([]byte(`A = ["one", "two", "many"]`), &d) +fmt.Println("err:", err, "d:", d) +// err: d: {[one two]} +``` + +This is to match `encoding/json`'s behavior. There is no way to make the v2 +decoder behave like v1. + +#### Support for `toml.Unmarshaler` has been dropped + +This method was not widely used, poorly defined, and added a lot of complexity. +A similar effect can be achieved by implementing the `encoding.TextUnmarshaler` +interface and use strings. + +### Encoding / Marshal + +#### Default struct fields order + +V1 emits struct fields order alphabetically by default. V2 struct fields are +emitted in order they are defined. For example: + +```go +type S struct { + B string + A string +} + +data := S{ + B: "B", + A: "A", +} + +b, _ := tomlv1.Marshal(data) +fmt.Println("v1:\n" + string(b)) + +b, _ = tomlv2.Marshal(data) +fmt.Println("v2:\n" + string(b)) + +// Output: +// v1: +// A = "A" +// B = "B" + +// v2: +// B = 'B' +// A = 'A' +``` + +There is no way to make v2 encoder behave like v1. A workaround could be to +manually sort the fields alphabetically in the struct definition. + +#### No indentation by default + +V1 automatically indents content of tables by default. V2 does not. However the +same behavior can be obtained using [`Encoder.SetIndentTables`][sit]. For example: + + +```go +data := map[string]interface{}{ + "table": map[string]string{ + "key": "value", + }, +} + +b, _ := tomlv1.Marshal(data) +fmt.Println("v1:\n" + string(b)) + +b, _ = tomlv2.Marshal(data) +fmt.Println("v2:\n" + string(b)) + +buf := bytes.Buffer{} +enc := tomlv2.NewEncoder(&buf) +enc.SetIndentTables(true) +enc.Encode(data) +fmt.Println("v2 Encoder:\n" + string(buf.Bytes())) + +// Output: +// v1: +// +// [table] +// key = "value" +// +// v2: +// [table] +// key = 'value' +// +// +// v2 Encoder: +// [table] +// key = 'value' +``` + +[sit]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#Encoder.SetIndentTables + +#### Keys and strings are single quoted + +V1 always uses double quotes (`"`) around strings and keys that cannot be +represented bare (unquoted). V2 uses single quotes instead by default (`'`), +unless a character cannot be represented, then falls back to double quotes. + +There is no way to make v2 encoder behave like v1. + +#### `TextMarshaler` emits as a string, not TOML + +Types that implement [`encoding.TextMarshaler`][tm] can emit arbitrary TOML in +v1. The encoder would append the result to the output directly. In v2 the result +is wrapped in a string. As a result, this interface cannot be implemented by the +root object. + +There is no way to make v2 encoder behave like v1. + +[tm]: https://golang.org/pkg/encoding/#TextMarshaler + +## License + +The MIT License (MIT). Read [LICENSE](LICENSE). diff --git a/vendor/github.com/pelletier/go-toml/v2/ci.sh b/vendor/github.com/pelletier/go-toml/v2/ci.sh new file mode 100644 index 0000000000..75d7008b30 --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/ci.sh @@ -0,0 +1,162 @@ +#!/usr/bin/env bash + + +stderr() { + echo "$@" 1>&2 +} + +usage() { + b=$(basename "$0") + echo $b: ERROR: "$@" 1>&2 + + cat 1>&2 < "${target_out}" + cover "HEAD" > "${head_out}" + + cat "${target_out}" + cat "${head_out}" + + echo "" + + target_pct="$(cat ${target_out} |sed -E 's/.*total.*\t([0-9.]+)%/\1/;t;d')" + head_pct="$(cat ${head_out} |sed -E 's/.*total.*\t([0-9.]+)%/\1/;t;d')" + echo "Results: ${target} ${target_pct}% HEAD ${head_pct}%" + + delta_pct=$(echo "$head_pct - $target_pct" | bc -l) + echo "Delta: ${delta_pct}" + + if [[ $delta_pct = \-* ]]; then + echo "Regression!"; + return 1 + fi + return 0 + ;; + esac + + cover "${1-HEAD}" +} + +bench() { + branch="${1}" + out="${2}" + dir="$(mktemp -d)" + + stderr "Executing benchmark for ${branch} at ${dir}" + + if [ "${branch}" = "HEAD" ]; then + cp -r . "${dir}/" + else + git worktree add "$dir" "$branch" + fi + + pushd "$dir" + go test -bench=. -count=10 ./... | tee "${out}" + popd + + if [ "${branch}" != "HEAD" ]; then + git worktree remove --force "$dir" + fi +} + +benchmark() { + case "$1" in + -d) + shift + target="${1?Need to provide a target branch argument}" + old=`mktemp` + bench "${target}" "${old}" + + new=`mktemp` + bench HEAD "${new}" + benchstat "${old}" "${new}" + return 0 + ;; + esac + + bench "${1-HEAD}" `mktemp` +} + +case "$1" in + coverage) shift; coverage $@;; + benchmark) shift; benchmark $@;; + *) usage "bad argument $1";; +esac diff --git a/vendor/github.com/pelletier/go-toml/v2/decode.go b/vendor/github.com/pelletier/go-toml/v2/decode.go new file mode 100644 index 0000000000..33ac2a967b --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/decode.go @@ -0,0 +1,314 @@ +package toml + +import ( + "fmt" + "math" + "strconv" + "time" +) + +func parseInteger(b []byte) (int64, error) { + if len(b) > 2 && b[0] == '0' { + switch b[1] { + case 'x': + return parseIntHex(b) + case 'b': + return parseIntBin(b) + case 'o': + return parseIntOct(b) + default: + panic(fmt.Errorf("invalid base '%c', should have been checked by scanIntOrFloat", b[1])) + } + } + + return parseIntDec(b) +} + +func parseLocalDate(b []byte) (LocalDate, error) { + // full-date = date-fullyear "-" date-month "-" date-mday + // date-fullyear = 4DIGIT + // date-month = 2DIGIT ; 01-12 + // date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on month/year + var date LocalDate + + if len(b) != 10 || b[4] != '-' || b[7] != '-' { + return date, newDecodeError(b, "dates are expected to have the format YYYY-MM-DD") + } + + date.Year = parseDecimalDigits(b[0:4]) + + v := parseDecimalDigits(b[5:7]) + + date.Month = time.Month(v) + + date.Day = parseDecimalDigits(b[8:10]) + + return date, nil +} + +func parseDecimalDigits(b []byte) int { + v := 0 + + for _, c := range b { + v *= 10 + v += int(c - '0') + } + + return v +} + +func parseDateTime(b []byte) (time.Time, error) { + // offset-date-time = full-date time-delim full-time + // full-time = partial-time time-offset + // time-offset = "Z" / time-numoffset + // time-numoffset = ( "+" / "-" ) time-hour ":" time-minute + + dt, b, err := parseLocalDateTime(b) + if err != nil { + return time.Time{}, err + } + + var zone *time.Location + + if len(b) == 0 { + // parser should have checked that when assigning the date time node + panic("date time should have a timezone") + } + + if b[0] == 'Z' { + b = b[1:] + zone = time.UTC + } else { + const dateTimeByteLen = 6 + if len(b) != dateTimeByteLen { + return time.Time{}, newDecodeError(b, "invalid date-time timezone") + } + direction := 1 + if b[0] == '-' { + direction = -1 + } + + hours := digitsToInt(b[1:3]) + minutes := digitsToInt(b[4:6]) + seconds := direction * (hours*3600 + minutes*60) + zone = time.FixedZone("", seconds) + b = b[dateTimeByteLen:] + } + + if len(b) > 0 { + return time.Time{}, newDecodeError(b, "extra bytes at the end of the timezone") + } + + t := time.Date( + dt.Date.Year, + dt.Date.Month, + dt.Date.Day, + dt.Time.Hour, + dt.Time.Minute, + dt.Time.Second, + dt.Time.Nanosecond, + zone) + + return t, nil +} + +func parseLocalDateTime(b []byte) (LocalDateTime, []byte, error) { + var dt LocalDateTime + + const localDateTimeByteMinLen = 11 + if len(b) < localDateTimeByteMinLen { + return dt, nil, newDecodeError(b, "local datetimes are expected to have the format YYYY-MM-DDTHH:MM:SS[.NNNNNNNNN]") + } + + date, err := parseLocalDate(b[:10]) + if err != nil { + return dt, nil, err + } + dt.Date = date + + sep := b[10] + if sep != 'T' && sep != ' ' { + return dt, nil, newDecodeError(b[10:11], "datetime separator is expected to be T or a space") + } + + t, rest, err := parseLocalTime(b[11:]) + if err != nil { + return dt, nil, err + } + dt.Time = t + + return dt, rest, nil +} + +// parseLocalTime is a bit different because it also returns the remaining +// []byte that is didn't need. This is to allow parseDateTime to parse those +// remaining bytes as a timezone. +func parseLocalTime(b []byte) (LocalTime, []byte, error) { + var ( + nspow = [10]int{0, 1e8, 1e7, 1e6, 1e5, 1e4, 1e3, 1e2, 1e1, 1e0} + t LocalTime + ) + + const localTimeByteLen = 8 + if len(b) < localTimeByteLen { + return t, nil, newDecodeError(b, "times are expected to have the format HH:MM:SS[.NNNNNN]") + } + + t.Hour = parseDecimalDigits(b[0:2]) + if b[2] != ':' { + return t, nil, newDecodeError(b[2:3], "expecting colon between hours and minutes") + } + + t.Minute = parseDecimalDigits(b[3:5]) + if b[5] != ':' { + return t, nil, newDecodeError(b[5:6], "expecting colon between minutes and seconds") + } + + t.Second = parseDecimalDigits(b[6:8]) + + const minLengthWithFrac = 9 + if len(b) >= minLengthWithFrac && b[minLengthWithFrac-1] == '.' { + frac := 0 + digits := 0 + + for i, c := range b[minLengthWithFrac:] { + const maxFracPrecision = 9 + if i >= maxFracPrecision { + return t, nil, newDecodeError(b[i:i+1], "maximum precision for date time is nanosecond") + } + + frac *= 10 + frac += int(c - '0') + digits++ + } + + t.Nanosecond = frac * nspow[digits] + + return t, b[9+digits:], nil + } + + return t, b[8:], nil +} + +//nolint:cyclop +func parseFloat(b []byte) (float64, error) { + if len(b) == 4 && (b[0] == '+' || b[0] == '-') && b[1] == 'n' && b[2] == 'a' && b[3] == 'n' { + return math.NaN(), nil + } + + cleaned, err := checkAndRemoveUnderscores(b) + if err != nil { + return 0, err + } + + if cleaned[0] == '.' { + return 0, newDecodeError(b, "float cannot start with a dot") + } + + if cleaned[len(cleaned)-1] == '.' { + return 0, newDecodeError(b, "float cannot end with a dot") + } + + f, err := strconv.ParseFloat(string(cleaned), 64) + if err != nil { + return 0, newDecodeError(b, "unable to parse float: %w", err) + } + + return f, nil +} + +func parseIntHex(b []byte) (int64, error) { + cleaned, err := checkAndRemoveUnderscores(b[2:]) + if err != nil { + return 0, err + } + + i, err := strconv.ParseInt(string(cleaned), 16, 64) + if err != nil { + return 0, newDecodeError(b, "couldn't parse hexadecimal number: %w", err) + } + + return i, nil +} + +func parseIntOct(b []byte) (int64, error) { + cleaned, err := checkAndRemoveUnderscores(b[2:]) + if err != nil { + return 0, err + } + + i, err := strconv.ParseInt(string(cleaned), 8, 64) + if err != nil { + return 0, newDecodeError(b, "couldn't parse octal number: %w", err) + } + + return i, nil +} + +func parseIntBin(b []byte) (int64, error) { + cleaned, err := checkAndRemoveUnderscores(b[2:]) + if err != nil { + return 0, err + } + + i, err := strconv.ParseInt(string(cleaned), 2, 64) + if err != nil { + return 0, newDecodeError(b, "couldn't parse binary number: %w", err) + } + + return i, nil +} + +func parseIntDec(b []byte) (int64, error) { + cleaned, err := checkAndRemoveUnderscores(b) + if err != nil { + return 0, err + } + + i, err := strconv.ParseInt(string(cleaned), 10, 64) + if err != nil { + return 0, newDecodeError(b, "couldn't parse decimal number: %w", err) + } + + return i, nil +} + +func checkAndRemoveUnderscores(b []byte) ([]byte, error) { + if b[0] == '_' { + return nil, newDecodeError(b[0:1], "number cannot start with underscore") + } + + if b[len(b)-1] == '_' { + return nil, newDecodeError(b[len(b)-1:], "number cannot end with underscore") + } + + // fast path + i := 0 + for ; i < len(b); i++ { + if b[i] == '_' { + break + } + } + if i == len(b) { + return b, nil + } + + before := false + cleaned := make([]byte, i, len(b)) + copy(cleaned, b) + + for i++; i < len(b); i++ { + c := b[i] + if c == '_' { + if !before { + return nil, newDecodeError(b[i-1:i+1], "number must have at least one digit between underscores") + } + before = false + } else { + before = true + cleaned = append(cleaned, c) + } + } + + return cleaned, nil +} diff --git a/vendor/github.com/pelletier/go-toml/v2/doc.go b/vendor/github.com/pelletier/go-toml/v2/doc.go new file mode 100644 index 0000000000..b7bc599bde --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/doc.go @@ -0,0 +1,2 @@ +// Package toml is a library to read and write TOML documents. +package toml diff --git a/vendor/github.com/pelletier/go-toml/v2/errors.go b/vendor/github.com/pelletier/go-toml/v2/errors.go new file mode 100644 index 0000000000..712765bba0 --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/errors.go @@ -0,0 +1,260 @@ +package toml + +import ( + "fmt" + "strconv" + "strings" + + "github.com/pelletier/go-toml/v2/internal/unsafe" +) + +// DecodeError represents an error encountered during the parsing or decoding +// of a TOML document. +// +// In addition to the error message, it contains the position in the document +// where it happened, as well as a human-readable representation that shows +// where the error occurred in the document. +type DecodeError struct { + message string + line int + column int + key Key + + human string +} + +// StrictMissingError occurs in a TOML document that does not have a +// corresponding field in the target value. It contains all the missing fields +// in Errors. +// +// Emitted by Decoder when SetStrict(true) was called. +type StrictMissingError struct { + // One error per field that could not be found. + Errors []DecodeError +} + +// Error returns the canonical string for this error. +func (s *StrictMissingError) Error() string { + return "strict mode: fields in the document are missing in the target struct" +} + +// String returns a human readable description of all errors. +func (s *StrictMissingError) String() string { + var buf strings.Builder + + for i, e := range s.Errors { + if i > 0 { + buf.WriteString("\n---\n") + } + + buf.WriteString(e.String()) + } + + return buf.String() +} + +type Key []string + +// internal version of DecodeError that is used as the base to create a +// DecodeError with full context. +type decodeError struct { + highlight []byte + message string + key Key // optional +} + +func (de *decodeError) Error() string { + return de.message +} + +func newDecodeError(highlight []byte, format string, args ...interface{}) error { + return &decodeError{ + highlight: highlight, + message: fmt.Errorf(format, args...).Error(), + } +} + +// Error returns the error message contained in the DecodeError. +func (e *DecodeError) Error() string { + return "toml: " + e.message +} + +// String returns the human-readable contextualized error. This string is multi-line. +func (e *DecodeError) String() string { + return e.human +} + +// Position returns the (line, column) pair indicating where the error +// occurred in the document. Positions are 1-indexed. +func (e *DecodeError) Position() (row int, column int) { + return e.line, e.column +} + +// Key that was being processed when the error occurred. The key is present only +// if this DecodeError is part of a StrictMissingError. +func (e *DecodeError) Key() Key { + return e.key +} + +// decodeErrorFromHighlight creates a DecodeError referencing a highlighted +// range of bytes from document. +// +// highlight needs to be a sub-slice of document, or this function panics. +// +// The function copies all bytes used in DecodeError, so that document and +// highlight can be freely deallocated. +//nolint:funlen +func wrapDecodeError(document []byte, de *decodeError) *DecodeError { + offset := unsafe.SubsliceOffset(document, de.highlight) + + errMessage := de.Error() + errLine, errColumn := positionAtEnd(document[:offset]) + before, after := linesOfContext(document, de.highlight, offset, 3) + + var buf strings.Builder + + maxLine := errLine + len(after) - 1 + lineColumnWidth := len(strconv.Itoa(maxLine)) + + for i := len(before) - 1; i > 0; i-- { + line := errLine - i + buf.WriteString(formatLineNumber(line, lineColumnWidth)) + buf.WriteString("|") + + if len(before[i]) > 0 { + buf.WriteString(" ") + buf.Write(before[i]) + } + + buf.WriteRune('\n') + } + + buf.WriteString(formatLineNumber(errLine, lineColumnWidth)) + buf.WriteString("| ") + + if len(before) > 0 { + buf.Write(before[0]) + } + + buf.Write(de.highlight) + + if len(after) > 0 { + buf.Write(after[0]) + } + + buf.WriteRune('\n') + buf.WriteString(strings.Repeat(" ", lineColumnWidth)) + buf.WriteString("| ") + + if len(before) > 0 { + buf.WriteString(strings.Repeat(" ", len(before[0]))) + } + + buf.WriteString(strings.Repeat("~", len(de.highlight))) + + if len(errMessage) > 0 { + buf.WriteString(" ") + buf.WriteString(errMessage) + } + + for i := 1; i < len(after); i++ { + buf.WriteRune('\n') + line := errLine + i + buf.WriteString(formatLineNumber(line, lineColumnWidth)) + buf.WriteString("|") + + if len(after[i]) > 0 { + buf.WriteString(" ") + buf.Write(after[i]) + } + } + + return &DecodeError{ + message: errMessage, + line: errLine, + column: errColumn, + key: de.key, + human: buf.String(), + } +} + +func formatLineNumber(line int, width int) string { + format := "%" + strconv.Itoa(width) + "d" + + return fmt.Sprintf(format, line) +} + +func linesOfContext(document []byte, highlight []byte, offset int, linesAround int) ([][]byte, [][]byte) { + return beforeLines(document, offset, linesAround), afterLines(document, highlight, offset, linesAround) +} + +func beforeLines(document []byte, offset int, linesAround int) [][]byte { + var beforeLines [][]byte + + // Walk the document backward from the highlight to find previous lines + // of context. + rest := document[:offset] +backward: + for o := len(rest) - 1; o >= 0 && len(beforeLines) <= linesAround && len(rest) > 0; { + switch { + case rest[o] == '\n': + // handle individual lines + beforeLines = append(beforeLines, rest[o+1:]) + rest = rest[:o] + o = len(rest) - 1 + case o == 0: + // add the first line only if it's non-empty + beforeLines = append(beforeLines, rest) + + break backward + default: + o-- + } + } + + return beforeLines +} + +func afterLines(document []byte, highlight []byte, offset int, linesAround int) [][]byte { + var afterLines [][]byte + + // Walk the document forward from the highlight to find the following + // lines of context. + rest := document[offset+len(highlight):] +forward: + for o := 0; o < len(rest) && len(afterLines) <= linesAround; { + switch { + case rest[o] == '\n': + // handle individual lines + afterLines = append(afterLines, rest[:o]) + rest = rest[o+1:] + o = 0 + + case o == len(rest)-1 && o > 0: + // add last line only if it's non-empty + afterLines = append(afterLines, rest) + + break forward + default: + o++ + } + } + + return afterLines +} + +func positionAtEnd(b []byte) (row int, column int) { + row = 1 + column = 1 + + for _, c := range b { + if c == '\n' { + row++ + column = 1 + } else { + column++ + } + } + + return +} diff --git a/vendor/github.com/pelletier/go-toml/v2/go.mod b/vendor/github.com/pelletier/go-toml/v2/go.mod new file mode 100644 index 0000000000..6745f0a559 --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/go.mod @@ -0,0 +1,5 @@ +module github.com/pelletier/go-toml/v2 + +go 1.15 + +require github.com/stretchr/testify v1.7.0 diff --git a/vendor/github.com/pelletier/go-toml/v2/go.sum b/vendor/github.com/pelletier/go-toml/v2/go.sum new file mode 100644 index 0000000000..acb88a48f6 --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/go.sum @@ -0,0 +1,11 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/ast/ast.go b/vendor/github.com/pelletier/go-toml/v2/internal/ast/ast.go new file mode 100644 index 0000000000..ba2729e05e --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/internal/ast/ast.go @@ -0,0 +1,138 @@ +package ast + +import ( + "fmt" +) + +// Iterator starts uninitialized, you need to call Next() first. +// +// For example: +// +// it := n.Children() +// for it.Next() { +// it.Node() +// } +type Iterator struct { + started bool + node Node +} + +// Next moves the iterator forward and returns true if points to a node, false +// otherwise. +func (c *Iterator) Next() bool { + if !c.started { + c.started = true + } else if c.node.Valid() { + c.node = c.node.Next() + } + return c.node.Valid() +} + +// Node returns a copy of the node pointed at by the iterator. +func (c *Iterator) Node() Node { + return c.node +} + +// Root contains a full AST. +// +// It is immutable once constructed with Builder. +type Root struct { + nodes []Node +} + +// Iterator over the top level nodes. +func (r *Root) Iterator() Iterator { + it := Iterator{} + if len(r.nodes) > 0 { + it.node = r.nodes[0] + } + return it +} + +func (r *Root) at(idx int) Node { + // TODO: unsafe to point to the node directly + return r.nodes[idx] +} + +// Arrays have one child per element in the array. +// InlineTables have one child per key-value pair in the table. +// KeyValues have at least two children. The first one is the value. The +// rest make a potentially dotted key. +// Table and Array table have one child per element of the key they +// represent (same as KeyValue, but without the last node being the value). +// children []Node +type Node struct { + Kind Kind + Data []byte // Raw bytes from the input + + // next idx (in the root array). 0 if last of the collection. + next int + // child idx (in the root array). 0 if no child. + child int + // pointer to the root array + root *Root +} + +// Next returns a copy of the next node, or an invalid Node if there is no +// next node. +func (n Node) Next() Node { + if n.next <= 0 { + return noNode + } + return n.root.at(n.next) +} + +// Child returns a copy of the first child node of this node. Other children +// can be accessed calling Next on the first child. +// Returns an invalid Node if there is none. +func (n Node) Child() Node { + if n.child <= 0 { + return noNode + } + return n.root.at(n.child) +} + +// Valid returns true if the node's kind is set (not to Invalid). +func (n Node) Valid() bool { + return n.Kind != Invalid +} + +var noNode = Node{} + +// Key returns the child nodes making the Key on a supported node. Panics +// otherwise. +// They are guaranteed to be all be of the Kind Key. A simple key would return +// just one element. +func (n *Node) Key() Iterator { + switch n.Kind { + case KeyValue: + value := n.Child() + if !value.Valid() { + panic(fmt.Errorf("KeyValue should have at least two children")) + } + return Iterator{node: value.Next()} + case Table, ArrayTable: + return Iterator{node: n.Child()} + default: + panic(fmt.Errorf("Key() is not supported on a %s", n.Kind)) + } +} + +// Value returns a pointer to the value node of a KeyValue. +// Guaranteed to be non-nil. +// Panics if not called on a KeyValue node, or if the Children are malformed. +func (n Node) Value() Node { + assertKind(KeyValue, n) + return n.Child() +} + +// Children returns an iterator over a node's children. +func (n Node) Children() Iterator { + return Iterator{node: n.Child()} +} + +func assertKind(k Kind, n Node) { + if n.Kind != k { + panic(fmt.Errorf("method was expecting a %s, not a %s", k, n.Kind)) + } +} diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/ast/builder.go b/vendor/github.com/pelletier/go-toml/v2/internal/ast/builder.go new file mode 100644 index 0000000000..796b7f1d95 --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/internal/ast/builder.go @@ -0,0 +1,60 @@ +package ast + +type Reference struct { + idx int + set bool +} + +func (r Reference) Valid() bool { + return r.set +} + +type Builder struct { + tree Root + lastIdx int +} + +func (b *Builder) Tree() *Root { + return &b.tree +} + +func (b *Builder) NodeAt(ref Reference) Node { + return b.tree.at(ref.idx) +} + +func (b *Builder) Reset() { + b.tree.nodes = b.tree.nodes[:0] + b.lastIdx = 0 +} + +func (b *Builder) Push(n Node) Reference { + n.root = &b.tree + b.lastIdx = len(b.tree.nodes) + b.tree.nodes = append(b.tree.nodes, n) + return Reference{ + idx: b.lastIdx, + set: true, + } +} + +func (b *Builder) PushAndChain(n Node) Reference { + n.root = &b.tree + newIdx := len(b.tree.nodes) + b.tree.nodes = append(b.tree.nodes, n) + if b.lastIdx >= 0 { + b.tree.nodes[b.lastIdx].next = newIdx + } + b.lastIdx = newIdx + return Reference{ + idx: b.lastIdx, + set: true, + } +} + +func (b *Builder) AttachChild(parent Reference, child Reference) { + b.tree.nodes[parent.idx].child = child.idx +} + +func (b *Builder) Chain(from Reference, to Reference) { + b.tree.nodes[from.idx].next = to.idx +} diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/ast/kind.go b/vendor/github.com/pelletier/go-toml/v2/internal/ast/kind.go new file mode 100644 index 0000000000..bcf6c91c12 --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/internal/ast/kind.go @@ -0,0 +1,69 @@ +package ast + +import "fmt" + +type Kind int + +const ( + // meta + Invalid Kind = iota + Comment + Key + + // top level structures + Table + ArrayTable + KeyValue + + // containers values + Array + InlineTable + + // values + String + Bool + Float + Integer + LocalDate + LocalDateTime + DateTime + Time +) + +func (k Kind) String() string { + switch k { + case Invalid: + return "Invalid" + case Comment: + return "Comment" + case Key: + return "Key" + case Table: + return "Table" + case ArrayTable: + return "ArrayTable" + case KeyValue: + return "KeyValue" + case Array: + return "Array" + case InlineTable: + return "InlineTable" + case String: + return "String" + case Bool: + return "Bool" + case Float: + return "Float" + case Integer: + return "Integer" + case LocalDate: + return "LocalDate" + case LocalDateTime: + return "LocalDateTime" + case DateTime: + return "DateTime" + case Time: + return "Time" + } + panic(fmt.Errorf("Kind.String() not implemented for '%d'", k)) +} diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/tracker/key.go b/vendor/github.com/pelletier/go-toml/v2/internal/tracker/key.go new file mode 100644 index 0000000000..be99f72061 --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/internal/tracker/key.go @@ -0,0 +1,50 @@ +package tracker + +import ( + "github.com/pelletier/go-toml/v2/internal/ast" +) + +// KeyTracker is a tracker that keeps track of the current Key as the AST is +// walked. +type KeyTracker struct { + k []string +} + +// UpdateTable sets the state of the tracker with the AST table node. +func (t *KeyTracker) UpdateTable(node ast.Node) { + t.reset() + t.Push(node) +} + +// UpdateArrayTable sets the state of the tracker with the AST array table node. +func (t *KeyTracker) UpdateArrayTable(node ast.Node) { + t.reset() + t.Push(node) +} + +// Push the given key on the stack. +func (t *KeyTracker) Push(node ast.Node) { + it := node.Key() + for it.Next() { + t.k = append(t.k, string(it.Node().Data)) + } +} + +// Pop key from stack. +func (t *KeyTracker) Pop(node ast.Node) { + it := node.Key() + for it.Next() { + t.k = t.k[:len(t.k)-1] + } +} + +// Key returns the current key +func (t *KeyTracker) Key() []string { + k := make([]string, len(t.k)) + copy(k, t.k) + return k +} + +func (t *KeyTracker) reset() { + t.k = t.k[:0] +} diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/tracker/seen.go b/vendor/github.com/pelletier/go-toml/v2/internal/tracker/seen.go new file mode 100644 index 0000000000..e245aace29 --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/internal/tracker/seen.go @@ -0,0 +1,200 @@ +package tracker + +import ( + "fmt" + + "github.com/pelletier/go-toml/v2/internal/ast" +) + +type keyKind uint8 + +const ( + invalidKind keyKind = iota + valueKind + tableKind + arrayTableKind +) + +func (k keyKind) String() string { + switch k { + case invalidKind: + return "invalid" + case valueKind: + return "value" + case tableKind: + return "table" + case arrayTableKind: + return "array table" + } + panic("missing keyKind string mapping") +} + +// SeenTracker tracks which keys have been seen with which TOML type to flag duplicates +// and mismatches according to the spec. +type SeenTracker struct { + root *info + current *info +} + +type info struct { + parent *info + kind keyKind + children map[string]*info + explicit bool +} + +func (i *info) Clear() { + i.children = nil +} + +func (i *info) Has(k string) (*info, bool) { + c, ok := i.children[k] + return c, ok +} + +func (i *info) SetKind(kind keyKind) { + i.kind = kind +} + +func (i *info) CreateTable(k string, explicit bool) *info { + return i.createChild(k, tableKind, explicit) +} + +func (i *info) CreateArrayTable(k string, explicit bool) *info { + return i.createChild(k, arrayTableKind, explicit) +} + +func (i *info) createChild(k string, kind keyKind, explicit bool) *info { + if i.children == nil { + i.children = make(map[string]*info, 1) + } + + x := &info{ + parent: i, + kind: kind, + explicit: explicit, + } + i.children[k] = x + return x +} + +// CheckExpression takes a top-level node and checks that it does not contain keys +// that have been seen in previous calls, and validates that types are consistent. +func (s *SeenTracker) CheckExpression(node ast.Node) error { + if s.root == nil { + s.root = &info{ + kind: tableKind, + } + s.current = s.root + } + switch node.Kind { + case ast.KeyValue: + return s.checkKeyValue(s.current, node) + case ast.Table: + return s.checkTable(node) + case ast.ArrayTable: + return s.checkArrayTable(node) + default: + panic(fmt.Errorf("this should not be a top level node type: %s", node.Kind)) + } + +} +func (s *SeenTracker) checkTable(node ast.Node) error { + s.current = s.root + + it := node.Key() + // handle the first parts of the key, excluding the last one + for it.Next() { + if !it.Node().Next().Valid() { + break + } + + k := string(it.Node().Data) + child, found := s.current.Has(k) + if !found { + child = s.current.CreateTable(k, false) + } + s.current = child + } + + // handle the last part of the key + k := string(it.Node().Data) + + i, found := s.current.Has(k) + if found { + if i.kind != tableKind { + return fmt.Errorf("toml: key %s should be a table, not a %s", k, i.kind) + } + if i.explicit { + return fmt.Errorf("toml: table %s already exists", k) + } + i.explicit = true + s.current = i + } else { + s.current = s.current.CreateTable(k, true) + } + + return nil +} + +func (s *SeenTracker) checkArrayTable(node ast.Node) error { + s.current = s.root + + it := node.Key() + + // handle the first parts of the key, excluding the last one + for it.Next() { + if !it.Node().Next().Valid() { + break + } + + k := string(it.Node().Data) + child, found := s.current.Has(k) + if !found { + child = s.current.CreateTable(k, false) + } + s.current = child + } + + // handle the last part of the key + k := string(it.Node().Data) + + info, found := s.current.Has(k) + if found { + if info.kind != arrayTableKind { + return fmt.Errorf("toml: key %s already exists as a %s, but should be an array table", info.kind, k) + } + info.Clear() + } else { + info = s.current.CreateArrayTable(k, true) + } + + s.current = info + return nil +} + +func (s *SeenTracker) checkKeyValue(context *info, node ast.Node) error { + it := node.Key() + + // handle the first parts of the key, excluding the last one + for it.Next() { + k := string(it.Node().Data) + child, found := context.Has(k) + if found { + if child.kind != tableKind { + return fmt.Errorf("toml: expected %s to be a table, not a %s", k, child.kind) + } + } else { + child = context.CreateTable(k, false) + } + context = child + } + + if node.Value().Kind == ast.InlineTable { + context.SetKind(tableKind) + } else { + context.SetKind(valueKind) + } + + return nil +} diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/tracker/tracker.go b/vendor/github.com/pelletier/go-toml/v2/internal/tracker/tracker.go new file mode 100644 index 0000000000..bf0317392f --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/internal/tracker/tracker.go @@ -0,0 +1 @@ +package tracker diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/unsafe/unsafe.go b/vendor/github.com/pelletier/go-toml/v2/internal/unsafe/unsafe.go new file mode 100644 index 0000000000..742c6ab27a --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/internal/unsafe/unsafe.go @@ -0,0 +1,59 @@ +package unsafe + +import ( + "fmt" + "reflect" + "unsafe" +) + +const maxInt = uintptr(int(^uint(0) >> 1)) + +func SubsliceOffset(data []byte, subslice []byte) int { + datap := (*reflect.SliceHeader)(unsafe.Pointer(&data)) + hlp := (*reflect.SliceHeader)(unsafe.Pointer(&subslice)) + + if hlp.Data < datap.Data { + panic(fmt.Errorf("subslice address (%d) is before data address (%d)", hlp.Data, datap.Data)) + } + offset := hlp.Data - datap.Data + + if offset > maxInt { + panic(fmt.Errorf("slice offset larger than int (%d)", offset)) + } + + intoffset := int(offset) + + if intoffset > datap.Len { + panic(fmt.Errorf("slice offset (%d) is farther than data length (%d)", intoffset, datap.Len)) + } + + if intoffset+hlp.Len > datap.Len { + panic(fmt.Errorf("slice ends (%d+%d) is farther than data length (%d)", intoffset, hlp.Len, datap.Len)) + } + + return intoffset +} + +func BytesRange(start []byte, end []byte) []byte { + if start == nil || end == nil { + panic("cannot call BytesRange with nil") + } + startp := (*reflect.SliceHeader)(unsafe.Pointer(&start)) + endp := (*reflect.SliceHeader)(unsafe.Pointer(&end)) + + if startp.Data > endp.Data { + panic(fmt.Errorf("start pointer address (%d) is after end pointer address (%d)", startp.Data, endp.Data)) + } + + l := startp.Len + endLen := int(endp.Data-startp.Data) + endp.Len + if endLen > l { + l = endLen + } + + if l > startp.Cap { + panic(fmt.Errorf("range length is larger than capacity")) + } + + return start[:l] +} diff --git a/vendor/github.com/pelletier/go-toml/localtime.go b/vendor/github.com/pelletier/go-toml/v2/localtime.go similarity index 90% rename from vendor/github.com/pelletier/go-toml/localtime.go rename to vendor/github.com/pelletier/go-toml/v2/localtime.go index a2149e9663..a947044ac7 100644 --- a/vendor/github.com/pelletier/go-toml/localtime.go +++ b/vendor/github.com/pelletier/go-toml/v2/localtime.go @@ -23,6 +23,7 @@ // // Because they lack location information, these types do not represent unique // moments or intervals of time. Use time.Time for that purpose. + package toml import ( @@ -44,6 +45,7 @@ type LocalDate struct { func LocalDateOf(t time.Time) LocalDate { var d LocalDate d.Year, d.Month, d.Day = t.Date() + return d } @@ -53,6 +55,7 @@ func ParseLocalDate(s string) (LocalDate, error) { if err != nil { return LocalDate{}, err } + return LocalDateOf(t), nil } @@ -92,23 +95,28 @@ func (d LocalDate) DaysSince(s LocalDate) (days int) { // We convert to Unix time so we do not have to worry about leap seconds: // Unix time increases by exactly 86400 seconds per day. deltaUnix := d.In(time.UTC).Unix() - s.In(time.UTC).Unix() - return int(deltaUnix / 86400) + + const secondsInADay = 86400 + + return int(deltaUnix / secondsInADay) } -// Before reports whether d1 occurs before d2. -func (d1 LocalDate) Before(d2 LocalDate) bool { - if d1.Year != d2.Year { - return d1.Year < d2.Year +// Before reports whether d1 occurs before future date. +func (d LocalDate) Before(future LocalDate) bool { + if d.Year != future.Year { + return d.Year < future.Year } - if d1.Month != d2.Month { - return d1.Month < d2.Month + + if d.Month != future.Month { + return d.Month < future.Month } - return d1.Day < d2.Day + + return d.Day < future.Day } -// After reports whether d1 occurs after d2. -func (d1 LocalDate) After(d2 LocalDate) bool { - return d2.Before(d1) +// After reports whether d1 occurs after past date. +func (d LocalDate) After(past LocalDate) bool { + return past.Before(d) } // MarshalText implements the encoding.TextMarshaler interface. @@ -122,6 +130,7 @@ func (d LocalDate) MarshalText() ([]byte, error) { func (d *LocalDate) UnmarshalText(data []byte) error { var err error *d, err = ParseLocalDate(string(data)) + return err } @@ -145,6 +154,7 @@ func LocalTimeOf(t time.Time) LocalTime { var tm LocalTime tm.Hour, tm.Minute, tm.Second = t.Clock() tm.Nanosecond = t.Nanosecond() + return tm } @@ -158,6 +168,7 @@ func ParseLocalTime(s string) (LocalTime, error) { if err != nil { return LocalTime{}, err } + return LocalTimeOf(t), nil } @@ -169,6 +180,7 @@ func (t LocalTime) String() string { if t.Nanosecond == 0 { return s } + return s + fmt.Sprintf(".%09d", t.Nanosecond) } @@ -176,6 +188,7 @@ func (t LocalTime) String() string { func (t LocalTime) IsValid() bool { // Construct a non-zero time. tm := time.Date(2, 2, 2, t.Hour, t.Minute, t.Second, t.Nanosecond, time.UTC) + return LocalTimeOf(tm) == t } @@ -190,6 +203,7 @@ func (t LocalTime) MarshalText() ([]byte, error) { func (t *LocalTime) UnmarshalText(data []byte) error { var err error *t, err = ParseLocalTime(string(data)) + return err } @@ -226,6 +240,7 @@ func ParseLocalDateTime(s string) (LocalDateTime, error) { return LocalDateTime{}, err } } + return LocalDateTimeOf(t), nil } @@ -253,17 +268,20 @@ func (dt LocalDateTime) IsValid() bool { // // In panics if loc is nil. func (dt LocalDateTime) In(loc *time.Location) time.Time { - return time.Date(dt.Date.Year, dt.Date.Month, dt.Date.Day, dt.Time.Hour, dt.Time.Minute, dt.Time.Second, dt.Time.Nanosecond, loc) + return time.Date( + dt.Date.Year, dt.Date.Month, dt.Date.Day, + dt.Time.Hour, dt.Time.Minute, dt.Time.Second, dt.Time.Nanosecond, loc, + ) } -// Before reports whether dt1 occurs before dt2. -func (dt1 LocalDateTime) Before(dt2 LocalDateTime) bool { - return dt1.In(time.UTC).Before(dt2.In(time.UTC)) +// Before reports whether dt occurs before future. +func (dt LocalDateTime) Before(future LocalDateTime) bool { + return dt.In(time.UTC).Before(future.In(time.UTC)) } -// After reports whether dt1 occurs after dt2. -func (dt1 LocalDateTime) After(dt2 LocalDateTime) bool { - return dt2.Before(dt1) +// After reports whether dt occurs after past. +func (dt LocalDateTime) After(past LocalDateTime) bool { + return past.Before(dt) } // MarshalText implements the encoding.TextMarshaler interface. @@ -273,9 +291,10 @@ func (dt LocalDateTime) MarshalText() ([]byte, error) { } // UnmarshalText implements the encoding.TextUnmarshaler interface. -// The datetime is expected to be a string in a format accepted by ParseLocalDateTime +// The datetime is expected to be a string in a format accepted by ParseLocalDateTime. func (dt *LocalDateTime) UnmarshalText(data []byte) error { var err error *dt, err = ParseLocalDateTime(string(data)) + return err } diff --git a/vendor/github.com/pelletier/go-toml/v2/marshaler.go b/vendor/github.com/pelletier/go-toml/v2/marshaler.go new file mode 100644 index 0000000000..ce9972aae8 --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/marshaler.go @@ -0,0 +1,796 @@ +package toml + +import ( + "bytes" + "encoding" + "fmt" + "io" + "reflect" + "sort" + "strconv" + "strings" + "time" +) + +// Marshal serializes a Go value as a TOML document. +// +// It is a shortcut for Encoder.Encode() with the default options. +func Marshal(v interface{}) ([]byte, error) { + var buf bytes.Buffer + enc := NewEncoder(&buf) + + err := enc.Encode(v) + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +// Encoder writes a TOML document to an output stream. +type Encoder struct { + // output + w io.Writer + + // global settings + tablesInline bool + arraysMultiline bool + indentSymbol string + indentTables bool +} + +// NewEncoder returns a new Encoder that writes to w. +func NewEncoder(w io.Writer) *Encoder { + return &Encoder{ + w: w, + indentSymbol: " ", + } +} + +// SetTablesInline forces the encoder to emit all tables inline. +// +// This behavior can be controlled on an individual struct field basis with the +// inline tag: +// +// MyField `inline:"true"` +func (enc *Encoder) SetTablesInline(inline bool) { + enc.tablesInline = inline +} + +// SetArraysMultiline forces the encoder to emit all arrays with one element per +// line. +// +// This behavior can be controlled on an individual struct field basis with the multiline tag: +// +// MyField `multiline:"true"` +func (enc *Encoder) SetArraysMultiline(multiline bool) { + enc.arraysMultiline = multiline +} + +// SetIndentSymbol defines the string that should be used for indentation. The +// provided string is repeated for each indentation level. Defaults to two +// spaces. +func (enc *Encoder) SetIndentSymbol(s string) { + enc.indentSymbol = s +} + +// SetIndentTables forces the encoder to intent tables and array tables. +func (enc *Encoder) SetIndentTables(indent bool) { + enc.indentTables = indent +} + +// Encode writes a TOML representation of v to the stream. +// +// If v cannot be represented to TOML it returns an error. +// +// Encoding rules +// +// A top level slice containing only maps or structs is encoded as [[table +// array]]. +// +// All slices not matching rule 1 are encoded as [array]. As a result, any map +// or struct they contain is encoded as an {inline table}. +// +// Nil interfaces and nil pointers are not supported. +// +// Keys in key-values always have one part. +// +// Intermediate tables are always printed. +// +// By default, strings are encoded as literal string, unless they contain either +// a newline character or a single quote. In that case they are emitted as quoted +// strings. +// +// When encoding structs, fields are encoded in order of definition, with their +// exact name. +// +// Struct tags +// +// The following struct tags are available to tweak encoding on a per-field +// basis: +// +// toml:"foo" +// Changes the name of the key to use for the field to foo. +// +// multiline:"true" +// When the field contains a string, it will be emitted as a quoted +// multi-line TOML string. +// +// inline:"true" +// When the field would normally be encoded as a table, it is instead +// encoded as an inline table. +func (enc *Encoder) Encode(v interface{}) error { + var ( + b []byte + ctx encoderCtx + ) + + ctx.inline = enc.tablesInline + + if v == nil { + return fmt.Errorf("toml: cannot encode a nil interface") + } + + b, err := enc.encode(b, ctx, reflect.ValueOf(v)) + if err != nil { + return err + } + + _, err = enc.w.Write(b) + if err != nil { + return fmt.Errorf("toml: cannot write: %w", err) + } + + return nil +} + +type valueOptions struct { + multiline bool +} + +type encoderCtx struct { + // Current top-level key. + parentKey []string + + // Key that should be used for a KV. + key string + // Extra flag to account for the empty string + hasKey bool + + // Set to true to indicate that the encoder is inside a KV, so that all + // tables need to be inlined. + insideKv bool + + // Set to true to skip the first table header in an array table. + skipTableHeader bool + + // Should the next table be encoded as inline + inline bool + + // Indentation level + indent int + + // Options coming from struct tags + options valueOptions +} + +func (ctx *encoderCtx) shiftKey() { + if ctx.hasKey { + ctx.parentKey = append(ctx.parentKey, ctx.key) + ctx.clearKey() + } +} + +func (ctx *encoderCtx) setKey(k string) { + ctx.key = k + ctx.hasKey = true +} + +func (ctx *encoderCtx) clearKey() { + ctx.key = "" + ctx.hasKey = false +} + +func (ctx *encoderCtx) isRoot() bool { + return len(ctx.parentKey) == 0 && !ctx.hasKey +} + +//nolint:cyclop,funlen +func (enc *Encoder) encode(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) { + if !v.IsZero() { + i, ok := v.Interface().(time.Time) + if ok { + return i.AppendFormat(b, time.RFC3339), nil + } + } + + if v.Type().Implements(textMarshalerType) { + if ctx.isRoot() { + return nil, fmt.Errorf("toml: type %s implementing the TextMarshaler interface cannot be a root element", v.Type()) + } + + text, err := v.Interface().(encoding.TextMarshaler).MarshalText() + if err != nil { + return nil, err + } + + b = enc.encodeString(b, string(text), ctx.options) + + return b, nil + } + + switch v.Kind() { + // containers + case reflect.Map: + return enc.encodeMap(b, ctx, v) + case reflect.Struct: + return enc.encodeStruct(b, ctx, v) + case reflect.Slice: + return enc.encodeSlice(b, ctx, v) + case reflect.Interface: + if v.IsNil() { + return nil, fmt.Errorf("toml: encoding a nil interface is not supported") + } + + return enc.encode(b, ctx, v.Elem()) + case reflect.Ptr: + if v.IsNil() { + return enc.encode(b, ctx, reflect.Zero(v.Type().Elem())) + } + + return enc.encode(b, ctx, v.Elem()) + + // values + case reflect.String: + b = enc.encodeString(b, v.String(), ctx.options) + case reflect.Float32: + b = strconv.AppendFloat(b, v.Float(), 'f', -1, 32) + case reflect.Float64: + b = strconv.AppendFloat(b, v.Float(), 'f', -1, 64) + case reflect.Bool: + if v.Bool() { + b = append(b, "true"...) + } else { + b = append(b, "false"...) + } + case reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uint: + b = strconv.AppendUint(b, v.Uint(), 10) + case reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int: + b = strconv.AppendInt(b, v.Int(), 10) + default: + return nil, fmt.Errorf("toml: cannot encode value of type %s", v.Kind()) + } + + return b, nil +} + +func isNil(v reflect.Value) bool { + switch v.Kind() { + case reflect.Ptr, reflect.Interface, reflect.Map: + return v.IsNil() + default: + return false + } +} + +func (enc *Encoder) encodeKv(b []byte, ctx encoderCtx, options valueOptions, v reflect.Value) ([]byte, error) { + var err error + + if !ctx.hasKey { + panic("caller of encodeKv should have set the key in the context") + } + b = enc.indent(ctx.indent, b) + + b, err = enc.encodeKey(b, ctx.key) + if err != nil { + return nil, err + } + + b = append(b, " = "...) + + // create a copy of the context because the value of a KV shouldn't + // modify the global context. + subctx := ctx + subctx.insideKv = true + subctx.shiftKey() + subctx.options = options + + b, err = enc.encode(b, subctx, v) + if err != nil { + return nil, err + } + + return b, nil +} + +const literalQuote = '\'' + +func (enc *Encoder) encodeString(b []byte, v string, options valueOptions) []byte { + if needsQuoting(v) { + return enc.encodeQuotedString(options.multiline, b, v) + } + + return enc.encodeLiteralString(b, v) +} + +func needsQuoting(v string) bool { + return strings.ContainsAny(v, "'\b\f\n\r\t") +} + +// caller should have checked that the string does not contain new lines or ' . +func (enc *Encoder) encodeLiteralString(b []byte, v string) []byte { + b = append(b, literalQuote) + b = append(b, v...) + b = append(b, literalQuote) + + return b +} + +//nolint:cyclop +func (enc *Encoder) encodeQuotedString(multiline bool, b []byte, v string) []byte { + stringQuote := `"` + + if multiline { + stringQuote = `"""` + } + + b = append(b, stringQuote...) + if multiline { + b = append(b, '\n') + } + + const ( + hextable = "0123456789ABCDEF" + // U+0000 to U+0008, U+000A to U+001F, U+007F + nul = 0x0 + bs = 0x8 + lf = 0xa + us = 0x1f + del = 0x7f + ) + + for _, r := range []byte(v) { + switch r { + case '\\': + b = append(b, `\\`...) + case '"': + b = append(b, `\"`...) + case '\b': + b = append(b, `\b`...) + case '\f': + b = append(b, `\f`...) + case '\n': + if multiline { + b = append(b, r) + } else { + b = append(b, `\n`...) + } + case '\r': + b = append(b, `\r`...) + case '\t': + b = append(b, `\t`...) + default: + switch { + case r >= nul && r <= bs, r >= lf && r <= us, r == del: + b = append(b, `\u00`...) + b = append(b, hextable[r>>4]) + b = append(b, hextable[r&0x0f]) + default: + b = append(b, r) + } + } + } + + b = append(b, stringQuote...) + + return b +} + +// called should have checked that the string is in A-Z / a-z / 0-9 / - / _ . +func (enc *Encoder) encodeUnquotedKey(b []byte, v string) []byte { + return append(b, v...) +} + +func (enc *Encoder) encodeTableHeader(ctx encoderCtx, b []byte) ([]byte, error) { + if len(ctx.parentKey) == 0 { + return b, nil + } + + b = enc.indent(ctx.indent, b) + + b = append(b, '[') + + var err error + + b, err = enc.encodeKey(b, ctx.parentKey[0]) + if err != nil { + return nil, err + } + + for _, k := range ctx.parentKey[1:] { + b = append(b, '.') + + b, err = enc.encodeKey(b, k) + if err != nil { + return nil, err + } + } + + b = append(b, "]\n"...) + + return b, nil +} + +//nolint:cyclop +func (enc *Encoder) encodeKey(b []byte, k string) ([]byte, error) { + needsQuotation := false + cannotUseLiteral := false + + for _, c := range k { + if (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '_' { + continue + } + + if c == '\n' { + return nil, fmt.Errorf("toml: new line characters in keys are not supported") + } + + if c == literalQuote { + cannotUseLiteral = true + } + + needsQuotation = true + } + + switch { + case cannotUseLiteral: + return enc.encodeQuotedString(false, b, k), nil + case needsQuotation: + return enc.encodeLiteralString(b, k), nil + default: + return enc.encodeUnquotedKey(b, k), nil + } +} + +func (enc *Encoder) encodeMap(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) { + if v.Type().Key().Kind() != reflect.String { + return nil, fmt.Errorf("toml: type %s is not supported as a map key", v.Type().Key().Kind()) + } + + var ( + t table + emptyValueOptions valueOptions + ) + + iter := v.MapRange() + for iter.Next() { + k := iter.Key().String() + v := iter.Value() + + if isNil(v) { + continue + } + + if willConvertToTableOrArrayTable(ctx, v) { + t.pushTable(k, v, emptyValueOptions) + } else { + t.pushKV(k, v, emptyValueOptions) + } + } + + sortEntriesByKey(t.kvs) + sortEntriesByKey(t.tables) + + return enc.encodeTable(b, ctx, t) +} + +func sortEntriesByKey(e []entry) { + sort.Slice(e, func(i, j int) bool { + return e[i].Key < e[j].Key + }) +} + +type entry struct { + Key string + Value reflect.Value + Options valueOptions +} + +type table struct { + kvs []entry + tables []entry +} + +func (t *table) pushKV(k string, v reflect.Value, options valueOptions) { + t.kvs = append(t.kvs, entry{Key: k, Value: v, Options: options}) +} + +func (t *table) pushTable(k string, v reflect.Value, options valueOptions) { + t.tables = append(t.tables, entry{Key: k, Value: v, Options: options}) +} + +func (enc *Encoder) encodeStruct(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) { + var t table + + //nolint:godox + // TODO: cache this? + typ := v.Type() + for i := 0; i < typ.NumField(); i++ { + fieldType := typ.Field(i) + + // only consider exported fields + if fieldType.PkgPath != "" { + continue + } + + k, ok := fieldType.Tag.Lookup("toml") + if !ok { + k = fieldType.Name + } + + // special field name to skip field + if k == "-" { + continue + } + + f := v.Field(i) + + if isNil(f) { + continue + } + + options := valueOptions{ + multiline: fieldBoolTag(fieldType, "multiline"), + } + + inline := fieldBoolTag(fieldType, "inline") + + if inline || !willConvertToTableOrArrayTable(ctx, f) { + t.pushKV(k, f, options) + } else { + t.pushTable(k, f, options) + } + } + + return enc.encodeTable(b, ctx, t) +} + +func fieldBoolTag(field reflect.StructField, tag string) bool { + x, ok := field.Tag.Lookup(tag) + + return ok && x == "true" +} + +//nolint:cyclop +func (enc *Encoder) encodeTable(b []byte, ctx encoderCtx, t table) ([]byte, error) { + var err error + + ctx.shiftKey() + + if ctx.insideKv || (ctx.inline && !ctx.isRoot()) { + return enc.encodeTableInline(b, ctx, t) + } + + if !ctx.skipTableHeader { + b, err = enc.encodeTableHeader(ctx, b) + if err != nil { + return nil, err + } + + if enc.indentTables && len(ctx.parentKey) > 0 { + ctx.indent++ + } + } + ctx.skipTableHeader = false + + for _, kv := range t.kvs { + ctx.setKey(kv.Key) + + b, err = enc.encodeKv(b, ctx, kv.Options, kv.Value) + if err != nil { + return nil, err + } + + b = append(b, '\n') + } + + for _, table := range t.tables { + ctx.setKey(table.Key) + + ctx.options = table.Options + + b, err = enc.encode(b, ctx, table.Value) + if err != nil { + return nil, err + } + + b = append(b, '\n') + } + + return b, nil +} + +func (enc *Encoder) encodeTableInline(b []byte, ctx encoderCtx, t table) ([]byte, error) { + var err error + + b = append(b, '{') + + first := true + for _, kv := range t.kvs { + if first { + first = false + } else { + b = append(b, `, `...) + } + + ctx.setKey(kv.Key) + + b, err = enc.encodeKv(b, ctx, kv.Options, kv.Value) + if err != nil { + return nil, err + } + } + + if len(t.tables) > 0 { + panic("inline table cannot contain nested tables, online key-values") + } + + b = append(b, "}"...) + + return b, nil +} + +var textMarshalerType = reflect.TypeOf(new(encoding.TextMarshaler)).Elem() + +func willConvertToTable(ctx encoderCtx, v reflect.Value) bool { + if v.Type() == timeType || v.Type().Implements(textMarshalerType) { + return false + } + + t := v.Type() + switch t.Kind() { + case reflect.Map, reflect.Struct: + return !ctx.inline + case reflect.Interface: + return willConvertToTable(ctx, v.Elem()) + case reflect.Ptr: + if v.IsNil() { + return false + } + + return willConvertToTable(ctx, v.Elem()) + default: + return false + } +} + +func willConvertToTableOrArrayTable(ctx encoderCtx, v reflect.Value) bool { + t := v.Type() + + if t.Kind() == reflect.Interface { + return willConvertToTableOrArrayTable(ctx, v.Elem()) + } + + if t.Kind() == reflect.Slice { + if v.Len() == 0 { + // An empty slice should be a kv = []. + return false + } + + for i := 0; i < v.Len(); i++ { + t := willConvertToTable(ctx, v.Index(i)) + + if !t { + return false + } + } + + return true + } + + return willConvertToTable(ctx, v) +} + +func (enc *Encoder) encodeSlice(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) { + if v.Len() == 0 { + b = append(b, "[]"...) + + return b, nil + } + + if willConvertToTableOrArrayTable(ctx, v) { + return enc.encodeSliceAsArrayTable(b, ctx, v) + } + + return enc.encodeSliceAsArray(b, ctx, v) +} + +// caller should have checked that v is a slice that only contains values that +// encode into tables. +func (enc *Encoder) encodeSliceAsArrayTable(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) { + ctx.shiftKey() + + var err error + scratch := make([]byte, 0, 64) + scratch = append(scratch, "[["...) + + for i, k := range ctx.parentKey { + if i > 0 { + scratch = append(scratch, '.') + } + + scratch, err = enc.encodeKey(scratch, k) + if err != nil { + return nil, err + } + } + + scratch = append(scratch, "]]\n"...) + ctx.skipTableHeader = true + + for i := 0; i < v.Len(); i++ { + b = append(b, scratch...) + + b, err = enc.encode(b, ctx, v.Index(i)) + if err != nil { + return nil, err + } + } + + return b, nil +} + +func (enc *Encoder) encodeSliceAsArray(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) { + multiline := ctx.options.multiline || enc.arraysMultiline + separator := ", " + + b = append(b, '[') + + subCtx := ctx + subCtx.options = valueOptions{} + + if multiline { + separator = ",\n" + + b = append(b, '\n') + + subCtx.indent++ + } + + var err error + first := true + + for i := 0; i < v.Len(); i++ { + if first { + first = false + } else { + b = append(b, separator...) + } + + if multiline { + b = enc.indent(subCtx.indent, b) + } + + b, err = enc.encode(b, subCtx, v.Index(i)) + if err != nil { + return nil, err + } + } + + if multiline { + b = append(b, '\n') + b = enc.indent(ctx.indent, b) + } + + b = append(b, ']') + + return b, nil +} + +func (enc *Encoder) indent(level int, b []byte) []byte { + for i := 0; i < level; i++ { + b = append(b, enc.indentSymbol...) + } + + return b +} diff --git a/vendor/github.com/pelletier/go-toml/v2/parser.go b/vendor/github.com/pelletier/go-toml/v2/parser.go new file mode 100644 index 0000000000..eefbf032a5 --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/parser.go @@ -0,0 +1,981 @@ +package toml + +import ( + "bytes" + "strconv" + + "github.com/pelletier/go-toml/v2/internal/ast" +) + +type parser struct { + builder ast.Builder + ref ast.Reference + data []byte + left []byte + err error + first bool +} + +func (p *parser) Reset(b []byte) { + p.builder.Reset() + p.ref = ast.Reference{} + p.data = b + p.left = b + p.err = nil + p.first = true +} + +//nolint:cyclop +func (p *parser) NextExpression() bool { + if len(p.left) == 0 || p.err != nil { + return false + } + + p.builder.Reset() + p.ref = ast.Reference{} + + for { + if len(p.left) == 0 || p.err != nil { + return false + } + + if !p.first { + p.left, p.err = p.parseNewline(p.left) + } + + if len(p.left) == 0 || p.err != nil { + return false + } + + p.ref, p.left, p.err = p.parseExpression(p.left) + + if p.err != nil { + return false + } + + p.first = false + + if p.ref.Valid() { + return true + } + } +} + +func (p *parser) Expression() ast.Node { + return p.builder.NodeAt(p.ref) +} + +func (p *parser) Error() error { + return p.err +} + +func (p *parser) parseNewline(b []byte) ([]byte, error) { + if b[0] == '\n' { + return b[1:], nil + } + + if b[0] == '\r' { + _, rest, err := scanWindowsNewline(b) + return rest, err + } + + return nil, newDecodeError(b[0:1], "expected newline but got %#U", b[0]) +} + +func (p *parser) parseExpression(b []byte) (ast.Reference, []byte, error) { + // expression = ws [ comment ] + // expression =/ ws keyval ws [ comment ] + // expression =/ ws table ws [ comment ] + var ref ast.Reference + + b = p.parseWhitespace(b) + + if len(b) == 0 { + return ref, b, nil + } + + if b[0] == '#' { + _, rest := scanComment(b) + + return ref, rest, nil + } + + if b[0] == '\n' || b[0] == '\r' { + return ref, b, nil + } + + var err error + if b[0] == '[' { + ref, b, err = p.parseTable(b) + } else { + ref, b, err = p.parseKeyval(b) + } + + if err != nil { + return ref, nil, err + } + + b = p.parseWhitespace(b) + + if len(b) > 0 && b[0] == '#' { + _, rest := scanComment(b) + + return ref, rest, nil + } + + return ref, b, nil +} + +func (p *parser) parseTable(b []byte) (ast.Reference, []byte, error) { + // table = std-table / array-table + if len(b) > 1 && b[1] == '[' { + return p.parseArrayTable(b) + } + + return p.parseStdTable(b) +} + +func (p *parser) parseArrayTable(b []byte) (ast.Reference, []byte, error) { + // array-table = array-table-open key array-table-close + // array-table-open = %x5B.5B ws ; [[ Double left square bracket + // array-table-close = ws %x5D.5D ; ]] Double right square bracket + ref := p.builder.Push(ast.Node{ + Kind: ast.ArrayTable, + }) + + b = b[2:] + b = p.parseWhitespace(b) + + k, b, err := p.parseKey(b) + if err != nil { + return ref, nil, err + } + + p.builder.AttachChild(ref, k) + b = p.parseWhitespace(b) + + b, err = expect(']', b) + if err != nil { + return ref, nil, err + } + + b, err = expect(']', b) + + return ref, b, err +} + +func (p *parser) parseStdTable(b []byte) (ast.Reference, []byte, error) { + // std-table = std-table-open key std-table-close + // std-table-open = %x5B ws ; [ Left square bracket + // std-table-close = ws %x5D ; ] Right square bracket + ref := p.builder.Push(ast.Node{ + Kind: ast.Table, + }) + + b = b[1:] + b = p.parseWhitespace(b) + + key, b, err := p.parseKey(b) + if err != nil { + return ref, nil, err + } + + p.builder.AttachChild(ref, key) + + b = p.parseWhitespace(b) + + b, err = expect(']', b) + + return ref, b, err +} + +func (p *parser) parseKeyval(b []byte) (ast.Reference, []byte, error) { + // keyval = key keyval-sep val + ref := p.builder.Push(ast.Node{ + Kind: ast.KeyValue, + }) + + key, b, err := p.parseKey(b) + if err != nil { + return ast.Reference{}, nil, err + } + + // keyval-sep = ws %x3D ws ; = + + b = p.parseWhitespace(b) + + if len(b) == 0 { + return ast.Reference{}, nil, newDecodeError(b, "expected = after a key, but the document ends there") + } + + b, err = expect('=', b) + if err != nil { + return ast.Reference{}, nil, err + } + + b = p.parseWhitespace(b) + + valRef, b, err := p.parseVal(b) + if err != nil { + return ref, b, err + } + + p.builder.Chain(valRef, key) + p.builder.AttachChild(ref, valRef) + + return ref, b, err +} + +//nolint:cyclop,funlen +func (p *parser) parseVal(b []byte) (ast.Reference, []byte, error) { + // val = string / boolean / array / inline-table / date-time / float / integer + var ref ast.Reference + + if len(b) == 0 { + return ref, nil, newDecodeError(b, "expected value, not eof") + } + + var err error + c := b[0] + + switch c { + case '"': + var v []byte + if scanFollowsMultilineBasicStringDelimiter(b) { + v, b, err = p.parseMultilineBasicString(b) + } else { + v, b, err = p.parseBasicString(b) + } + + if err == nil { + ref = p.builder.Push(ast.Node{ + Kind: ast.String, + Data: v, + }) + } + + return ref, b, err + case '\'': + var v []byte + if scanFollowsMultilineLiteralStringDelimiter(b) { + v, b, err = p.parseMultilineLiteralString(b) + } else { + v, b, err = p.parseLiteralString(b) + } + + if err == nil { + ref = p.builder.Push(ast.Node{ + Kind: ast.String, + Data: v, + }) + } + + return ref, b, err + case 't': + if !scanFollowsTrue(b) { + return ref, nil, newDecodeError(atmost(b, 4), "expected 'true'") + } + + ref = p.builder.Push(ast.Node{ + Kind: ast.Bool, + Data: b[:4], + }) + + return ref, b[4:], nil + case 'f': + if !scanFollowsFalse(b) { + return ref, nil, newDecodeError(atmost(b, 5), "expected 'false'") + } + + ref = p.builder.Push(ast.Node{ + Kind: ast.Bool, + Data: b[:5], + }) + + return ref, b[5:], nil + case '[': + return p.parseValArray(b) + case '{': + return p.parseInlineTable(b) + default: + return p.parseIntOrFloatOrDateTime(b) + } +} + +func atmost(b []byte, n int) []byte { + if n >= len(b) { + return b + } + + return b[:n] +} + +func (p *parser) parseLiteralString(b []byte) ([]byte, []byte, error) { + v, rest, err := scanLiteralString(b) + if err != nil { + return nil, nil, err + } + + return v[1 : len(v)-1], rest, nil +} + +func (p *parser) parseInlineTable(b []byte) (ast.Reference, []byte, error) { + // inline-table = inline-table-open [ inline-table-keyvals ] inline-table-close + // inline-table-open = %x7B ws ; { + // inline-table-close = ws %x7D ; } + // inline-table-sep = ws %x2C ws ; , Comma + // inline-table-keyvals = keyval [ inline-table-sep inline-table-keyvals ] + parent := p.builder.Push(ast.Node{ + Kind: ast.InlineTable, + }) + + first := true + + var child ast.Reference + + b = b[1:] + + var err error + + for len(b) > 0 { + b = p.parseWhitespace(b) + if b[0] == '}' { + break + } + + if !first { + b, err = expect(',', b) + if err != nil { + return parent, nil, err + } + b = p.parseWhitespace(b) + } + + var kv ast.Reference + + kv, b, err = p.parseKeyval(b) + if err != nil { + return parent, nil, err + } + + if first { + p.builder.AttachChild(parent, kv) + } else { + p.builder.Chain(child, kv) + } + child = kv + + first = false + } + + rest, err := expect('}', b) + + return parent, rest, err +} + +//nolint:funlen,cyclop +func (p *parser) parseValArray(b []byte) (ast.Reference, []byte, error) { + // array = array-open [ array-values ] ws-comment-newline array-close + // array-open = %x5B ; [ + // array-close = %x5D ; ] + // array-values = ws-comment-newline val ws-comment-newline array-sep array-values + // array-values =/ ws-comment-newline val ws-comment-newline [ array-sep ] + // array-sep = %x2C ; , Comma + // ws-comment-newline = *( wschar / [ comment ] newline ) + b = b[1:] + + parent := p.builder.Push(ast.Node{ + Kind: ast.Array, + }) + + first := true + + var lastChild ast.Reference + + var err error + for len(b) > 0 { + b, err = p.parseOptionalWhitespaceCommentNewline(b) + if err != nil { + return parent, nil, err + } + + if len(b) == 0 { + return parent, nil, newDecodeError(b, "array is incomplete") + } + + if b[0] == ']' { + break + } + + if b[0] == ',' { + if first { + return parent, nil, newDecodeError(b[0:1], "array cannot start with comma") + } + b = b[1:] + + b, err = p.parseOptionalWhitespaceCommentNewline(b) + if err != nil { + return parent, nil, err + } + } + + // TOML allows trailing commas in arrays. + if len(b) > 0 && b[0] == ']' { + break + } + + var valueRef ast.Reference + + valueRef, b, err = p.parseVal(b) + if err != nil { + return parent, nil, err + } + + if first { + p.builder.AttachChild(parent, valueRef) + } else { + p.builder.Chain(lastChild, valueRef) + } + lastChild = valueRef + + b, err = p.parseOptionalWhitespaceCommentNewline(b) + if err != nil { + return parent, nil, err + } + first = false + } + + rest, err := expect(']', b) + + return parent, rest, err +} + +func (p *parser) parseOptionalWhitespaceCommentNewline(b []byte) ([]byte, error) { + for len(b) > 0 { + var err error + b = p.parseWhitespace(b) + + if len(b) > 0 && b[0] == '#' { + _, b = scanComment(b) + } + + if len(b) == 0 { + break + } + + if b[0] == '\n' || b[0] == '\r' { + b, err = p.parseNewline(b) + if err != nil { + return nil, err + } + } else { + break + } + } + + return b, nil +} + +func (p *parser) parseMultilineLiteralString(b []byte) ([]byte, []byte, error) { + token, rest, err := scanMultilineLiteralString(b) + if err != nil { + return nil, nil, err + } + + i := 3 + + // skip the immediate new line + if token[i] == '\n' { + i++ + } else if token[i] == '\r' && token[i+1] == '\n' { + i += 2 + } + + return token[i : len(token)-3], rest, err +} + +//nolint:funlen,gocognit,cyclop +func (p *parser) parseMultilineBasicString(b []byte) ([]byte, []byte, error) { + // ml-basic-string = ml-basic-string-delim [ newline ] ml-basic-body + // ml-basic-string-delim + // ml-basic-string-delim = 3quotation-mark + // ml-basic-body = *mlb-content *( mlb-quotes 1*mlb-content ) [ mlb-quotes ] + // + // mlb-content = mlb-char / newline / mlb-escaped-nl + // mlb-char = mlb-unescaped / escaped + // mlb-quotes = 1*2quotation-mark + // mlb-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii + // mlb-escaped-nl = escape ws newline *( wschar / newline ) + token, rest, err := scanMultilineBasicString(b) + if err != nil { + return nil, nil, err + } + + var builder bytes.Buffer + + i := 3 + + // skip the immediate new line + if token[i] == '\n' { + i++ + } else if token[i] == '\r' && token[i+1] == '\n' { + i += 2 + } + + // The scanner ensures that the token starts and ends with quotes and that + // escapes are balanced. + for ; i < len(token)-3; i++ { + c := token[i] + + //nolint:nestif + if c == '\\' { + // When the last non-whitespace character on a line is an unescaped \, + // it will be trimmed along with all whitespace (including newlines) up + // to the next non-whitespace character or closing delimiter. + if token[i+1] == '\n' || (token[i+1] == '\r' && token[i+2] == '\n') { + i++ // skip the \ + for ; i < len(token)-3; i++ { + c := token[i] + if !(c == '\n' || c == '\r' || c == ' ' || c == '\t') { + i-- + + break + } + } + + continue + } + + // handle escaping + i++ + c = token[i] + + switch c { + case '"', '\\': + builder.WriteByte(c) + case 'b': + builder.WriteByte('\b') + case 'f': + builder.WriteByte('\f') + case 'n': + builder.WriteByte('\n') + case 'r': + builder.WriteByte('\r') + case 't': + builder.WriteByte('\t') + case 'u': + x, err := hexToString(atmost(token[i+1:], 4), 4) + if err != nil { + return nil, nil, err + } + + builder.WriteString(x) + i += 4 + case 'U': + x, err := hexToString(atmost(token[i+1:], 8), 8) + if err != nil { + return nil, nil, err + } + + builder.WriteString(x) + i += 8 + default: + return nil, nil, newDecodeError(token[i:i+1], "invalid escaped character %#U", c) + } + } else { + builder.WriteByte(c) + } + } + + return builder.Bytes(), rest, nil +} + +func (p *parser) parseKey(b []byte) (ast.Reference, []byte, error) { + // key = simple-key / dotted-key + // simple-key = quoted-key / unquoted-key + // + // unquoted-key = 1*( ALPHA / DIGIT / %x2D / %x5F ) ; A-Z / a-z / 0-9 / - / _ + // quoted-key = basic-string / literal-string + // dotted-key = simple-key 1*( dot-sep simple-key ) + // + // dot-sep = ws %x2E ws ; . Period + key, b, err := p.parseSimpleKey(b) + if err != nil { + return ast.Reference{}, nil, err + } + + ref := p.builder.Push(ast.Node{ + Kind: ast.Key, + Data: key, + }) + + for { + b = p.parseWhitespace(b) + if len(b) > 0 && b[0] == '.' { + b = p.parseWhitespace(b[1:]) + + key, b, err = p.parseSimpleKey(b) + if err != nil { + return ref, nil, err + } + + p.builder.PushAndChain(ast.Node{ + Kind: ast.Key, + Data: key, + }) + } else { + break + } + } + + return ref, b, nil +} + +func (p *parser) parseSimpleKey(b []byte) (key, rest []byte, err error) { + // simple-key = quoted-key / unquoted-key + // unquoted-key = 1*( ALPHA / DIGIT / %x2D / %x5F ) ; A-Z / a-z / 0-9 / - / _ + // quoted-key = basic-string / literal-string + if len(b) == 0 { + return nil, nil, newDecodeError(b, "key is incomplete") + } + + switch { + case b[0] == '\'': + return p.parseLiteralString(b) + case b[0] == '"': + return p.parseBasicString(b) + case isUnquotedKeyChar(b[0]): + key, rest = scanUnquotedKey(b) + return key, rest, nil + default: + return nil, nil, newDecodeError(b[0:1], "invalid character at start of key: %c", b[0]) + } +} + +//nolint:funlen,cyclop +func (p *parser) parseBasicString(b []byte) ([]byte, []byte, error) { + // basic-string = quotation-mark *basic-char quotation-mark + // quotation-mark = %x22 ; " + // basic-char = basic-unescaped / escaped + // basic-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii + // escaped = escape escape-seq-char + // escape-seq-char = %x22 ; " quotation mark U+0022 + // escape-seq-char =/ %x5C ; \ reverse solidus U+005C + // escape-seq-char =/ %x62 ; b backspace U+0008 + // escape-seq-char =/ %x66 ; f form feed U+000C + // escape-seq-char =/ %x6E ; n line feed U+000A + // escape-seq-char =/ %x72 ; r carriage return U+000D + // escape-seq-char =/ %x74 ; t tab U+0009 + // escape-seq-char =/ %x75 4HEXDIG ; uXXXX U+XXXX + // escape-seq-char =/ %x55 8HEXDIG ; UXXXXXXXX U+XXXXXXXX + token, rest, err := scanBasicString(b) + if err != nil { + return nil, nil, err + } + + var builder bytes.Buffer + + // The scanner ensures that the token starts and ends with quotes and that + // escapes are balanced. + for i := 1; i < len(token)-1; i++ { + c := token[i] + if c == '\\' { + i++ + c = token[i] + + switch c { + case '"', '\\': + builder.WriteByte(c) + case 'b': + builder.WriteByte('\b') + case 'f': + builder.WriteByte('\f') + case 'n': + builder.WriteByte('\n') + case 'r': + builder.WriteByte('\r') + case 't': + builder.WriteByte('\t') + case 'u': + x, err := hexToString(token[i+1:len(token)-1], 4) + if err != nil { + return nil, nil, err + } + + builder.WriteString(x) + i += 4 + case 'U': + x, err := hexToString(token[i+1:len(token)-1], 8) + if err != nil { + return nil, nil, err + } + + builder.WriteString(x) + i += 8 + default: + return nil, nil, newDecodeError(token[i:i+1], "invalid escaped character %#U", c) + } + } else { + builder.WriteByte(c) + } + } + + return builder.Bytes(), rest, nil +} + +func hexToString(b []byte, length int) (string, error) { + if len(b) < length { + return "", newDecodeError(b, "unicode point needs %d character, not %d", length, len(b)) + } + b = b[:length] + + //nolint:godox + // TODO: slow + intcode, err := strconv.ParseInt(string(b), 16, 32) + if err != nil { + return "", newDecodeError(b, "couldn't parse hexadecimal number: %w", err) + } + + return string(rune(intcode)), nil +} + +func (p *parser) parseWhitespace(b []byte) []byte { + // ws = *wschar + // wschar = %x20 ; Space + // wschar =/ %x09 ; Horizontal tab + _, rest := scanWhitespace(b) + + return rest +} + +//nolint:cyclop +func (p *parser) parseIntOrFloatOrDateTime(b []byte) (ast.Reference, []byte, error) { + switch b[0] { + case 'i': + if !scanFollowsInf(b) { + return ast.Reference{}, nil, newDecodeError(atmost(b, 3), "expected 'inf'") + } + + return p.builder.Push(ast.Node{ + Kind: ast.Float, + Data: b[:3], + }), b[3:], nil + case 'n': + if !scanFollowsNan(b) { + return ast.Reference{}, nil, newDecodeError(atmost(b, 3), "expected 'nan'") + } + + return p.builder.Push(ast.Node{ + Kind: ast.Float, + Data: b[:3], + }), b[3:], nil + case '+', '-': + return p.scanIntOrFloat(b) + } + + //nolint:gomnd + if len(b) < 3 { + return p.scanIntOrFloat(b) + } + + s := 5 + if len(b) < s { + s = len(b) + } + + for idx, c := range b[:s] { + if isDigit(c) { + continue + } + + if idx == 2 && c == ':' || (idx == 4 && c == '-') { + return p.scanDateTime(b) + } + } + + return p.scanIntOrFloat(b) +} + +func digitsToInt(b []byte) int { + x := 0 + + for _, d := range b { + x *= 10 + x += int(d - '0') + } + + return x +} + +//nolint:gocognit,cyclop +func (p *parser) scanDateTime(b []byte) (ast.Reference, []byte, error) { + // scans for contiguous characters in [0-9T:Z.+-], and up to one space if + // followed by a digit. + hasTime := false + hasTz := false + seenSpace := false + + i := 0 +byteLoop: + for ; i < len(b); i++ { + c := b[i] + + switch { + case isDigit(c): + case c == '-': + const offsetOfTz = 19 + if i == offsetOfTz { + hasTz = true + } + case c == 'T' || c == ':' || c == '.': + hasTime = true + case c == '+' || c == '-' || c == 'Z': + hasTz = true + case c == ' ': + if !seenSpace && i+1 < len(b) && isDigit(b[i+1]) { + i += 2 + seenSpace = true + hasTime = true + } else { + break byteLoop + } + default: + break byteLoop + } + } + + var kind ast.Kind + + if hasTime { + if hasTz { + kind = ast.DateTime + } else { + kind = ast.LocalDateTime + } + } else { + kind = ast.LocalDate + } + + return p.builder.Push(ast.Node{ + Kind: kind, + Data: b[:i], + }), b[i:], nil +} + +//nolint:funlen,gocognit,cyclop +func (p *parser) scanIntOrFloat(b []byte) (ast.Reference, []byte, error) { + i := 0 + + if len(b) > 2 && b[0] == '0' && b[1] != '.' { + var isValidRune validRuneFn + + switch b[1] { + case 'x': + isValidRune = isValidHexRune + case 'o': + isValidRune = isValidOctalRune + case 'b': + isValidRune = isValidBinaryRune + default: + i++ + } + + if isValidRune != nil { + i += 2 + for ; i < len(b); i++ { + if !isValidRune(b[i]) { + break + } + } + } + + return p.builder.Push(ast.Node{ + Kind: ast.Integer, + Data: b[:i], + }), b[i:], nil + } + + isFloat := false + + for ; i < len(b); i++ { + c := b[i] + + if c >= '0' && c <= '9' || c == '+' || c == '-' || c == '_' { + continue + } + + if c == '.' || c == 'e' || c == 'E' { + isFloat = true + + continue + } + + if c == 'i' { + if scanFollowsInf(b[i:]) { + return p.builder.Push(ast.Node{ + Kind: ast.Float, + Data: b[:i+3], + }), b[i+3:], nil + } + + return ast.Reference{}, nil, newDecodeError(b[i:i+1], "unexpected character 'i' while scanning for a number") + } + + if c == 'n' { + if scanFollowsNan(b[i:]) { + return p.builder.Push(ast.Node{ + Kind: ast.Float, + Data: b[:i+3], + }), b[i+3:], nil + } + + return ast.Reference{}, nil, newDecodeError(b[i:i+1], "unexpected character 'n' while scanning for a number") + } + + break + } + + if i == 0 { + return ast.Reference{}, b, newDecodeError(b, "incomplete number") + } + + kind := ast.Integer + + if isFloat { + kind = ast.Float + } + + return p.builder.Push(ast.Node{ + Kind: kind, + Data: b[:i], + }), b[i:], nil +} + +func isDigit(r byte) bool { + return r >= '0' && r <= '9' +} + +type validRuneFn func(r byte) bool + +func isValidHexRune(r byte) bool { + return r >= 'a' && r <= 'f' || + r >= 'A' && r <= 'F' || + r >= '0' && r <= '9' || + r == '_' +} + +func isValidOctalRune(r byte) bool { + return r >= '0' && r <= '7' || r == '_' +} + +func isValidBinaryRune(r byte) bool { + return r == '0' || r == '1' || r == '_' +} + +func expect(x byte, b []byte) ([]byte, error) { + if b[0] != x { + return nil, newDecodeError(b[0:1], "expected character %U", x) + } + + return b[1:], nil +} diff --git a/vendor/github.com/pelletier/go-toml/v2/scanner.go b/vendor/github.com/pelletier/go-toml/v2/scanner.go new file mode 100644 index 0000000000..b203702238 --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/scanner.go @@ -0,0 +1,173 @@ +package toml + +func scanFollows(b []byte, pattern string) bool { + n := len(pattern) + + return len(b) >= n && string(b[:n]) == pattern +} + +func scanFollowsMultilineBasicStringDelimiter(b []byte) bool { + return scanFollows(b, `"""`) +} + +func scanFollowsMultilineLiteralStringDelimiter(b []byte) bool { + return scanFollows(b, `'''`) +} + +func scanFollowsTrue(b []byte) bool { + return scanFollows(b, `true`) +} + +func scanFollowsFalse(b []byte) bool { + return scanFollows(b, `false`) +} + +func scanFollowsInf(b []byte) bool { + return scanFollows(b, `inf`) +} + +func scanFollowsNan(b []byte) bool { + return scanFollows(b, `nan`) +} + +func scanUnquotedKey(b []byte) ([]byte, []byte) { + // unquoted-key = 1*( ALPHA / DIGIT / %x2D / %x5F ) ; A-Z / a-z / 0-9 / - / _ + for i := 0; i < len(b); i++ { + if !isUnquotedKeyChar(b[i]) { + return b[:i], b[i:] + } + } + + return b, b[len(b):] +} + +func isUnquotedKeyChar(r byte) bool { + return (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '-' || r == '_' +} + +func scanLiteralString(b []byte) ([]byte, []byte, error) { + // literal-string = apostrophe *literal-char apostrophe + // apostrophe = %x27 ; ' apostrophe + // literal-char = %x09 / %x20-26 / %x28-7E / non-ascii + for i := 1; i < len(b); i++ { + switch b[i] { + case '\'': + return b[:i+1], b[i+1:], nil + case '\n': + return nil, nil, newDecodeError(b[i:i+1], "literal strings cannot have new lines") + } + } + + return nil, nil, newDecodeError(b[len(b):], "unterminated literal string") +} + +func scanMultilineLiteralString(b []byte) ([]byte, []byte, error) { + // ml-literal-string = ml-literal-string-delim [ newline ] ml-literal-body + // ml-literal-string-delim + // ml-literal-string-delim = 3apostrophe + // ml-literal-body = *mll-content *( mll-quotes 1*mll-content ) [ mll-quotes ] + // + // mll-content = mll-char / newline + // mll-char = %x09 / %x20-26 / %x28-7E / non-ascii + // mll-quotes = 1*2apostrophe + for i := 3; i < len(b); i++ { + if b[i] == '\'' && scanFollowsMultilineLiteralStringDelimiter(b[i:]) { + return b[:i+3], b[i+3:], nil + } + } + + return nil, nil, newDecodeError(b[len(b):], `multiline literal string not terminated by '''`) +} + +func scanWindowsNewline(b []byte) ([]byte, []byte, error) { + const lenCRLF = 2 + if len(b) < lenCRLF { + return nil, nil, newDecodeError(b, "windows new line expected") + } + + if b[1] != '\n' { + return nil, nil, newDecodeError(b, `windows new line should be \r\n`) + } + + return b[:lenCRLF], b[lenCRLF:], nil +} + +func scanWhitespace(b []byte) ([]byte, []byte) { + for i := 0; i < len(b); i++ { + switch b[i] { + case ' ', '\t': + continue + default: + return b[:i], b[i:] + } + } + + return b, b[len(b):] +} + +//nolint:unparam +func scanComment(b []byte) ([]byte, []byte) { + // comment-start-symbol = %x23 ; # + // non-ascii = %x80-D7FF / %xE000-10FFFF + // non-eol = %x09 / %x20-7F / non-ascii + // + // comment = comment-start-symbol *non-eol + for i := 1; i < len(b); i++ { + if b[i] == '\n' { + return b[:i], b[i:] + } + } + + return b, nil +} + +func scanBasicString(b []byte) ([]byte, []byte, error) { + // basic-string = quotation-mark *basic-char quotation-mark + // quotation-mark = %x22 ; " + // basic-char = basic-unescaped / escaped + // basic-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii + // escaped = escape escape-seq-char + for i := 1; i < len(b); i++ { + switch b[i] { + case '"': + return b[:i+1], b[i+1:], nil + case '\n': + return nil, nil, newDecodeError(b[i:i+1], "basic strings cannot have new lines") + case '\\': + if len(b) < i+2 { + return nil, nil, newDecodeError(b[i:i+1], "need a character after \\") + } + i++ // skip the next character + } + } + + return nil, nil, newDecodeError(b[len(b):], `basic string not terminated by "`) +} + +func scanMultilineBasicString(b []byte) ([]byte, []byte, error) { + // ml-basic-string = ml-basic-string-delim [ newline ] ml-basic-body + // ml-basic-string-delim + // ml-basic-string-delim = 3quotation-mark + // ml-basic-body = *mlb-content *( mlb-quotes 1*mlb-content ) [ mlb-quotes ] + // + // mlb-content = mlb-char / newline / mlb-escaped-nl + // mlb-char = mlb-unescaped / escaped + // mlb-quotes = 1*2quotation-mark + // mlb-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii + // mlb-escaped-nl = escape ws newline *( wschar / newline ) + for i := 3; i < len(b); i++ { + switch b[i] { + case '"': + if scanFollowsMultilineBasicStringDelimiter(b[i:]) { + return b[:i+3], b[i+3:], nil + } + case '\\': + if len(b) < i+2 { + return nil, nil, newDecodeError(b[len(b):], "need a character after \\") + } + i++ // skip the next character + } + } + + return nil, nil, newDecodeError(b[len(b):], `multiline basic string not terminated by """`) +} diff --git a/vendor/github.com/pelletier/go-toml/v2/strict.go b/vendor/github.com/pelletier/go-toml/v2/strict.go new file mode 100644 index 0000000000..2b2e7d652c --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/strict.go @@ -0,0 +1,88 @@ +package toml + +import ( + "github.com/pelletier/go-toml/v2/internal/ast" + "github.com/pelletier/go-toml/v2/internal/tracker" +) + +type strict struct { + Enabled bool + + // Tracks the current key being processed. + key tracker.KeyTracker + + missing []decodeError +} + +func (s *strict) EnterTable(node ast.Node) { + if !s.Enabled { + return + } + + s.key.UpdateTable(node) +} + +func (s *strict) EnterArrayTable(node ast.Node) { + if !s.Enabled { + return + } + + s.key.UpdateArrayTable(node) +} + +func (s *strict) EnterKeyValue(node ast.Node) { + if !s.Enabled { + return + } + + s.key.Push(node) +} + +func (s *strict) ExitKeyValue(node ast.Node) { + if !s.Enabled { + return + } + + s.key.Pop(node) +} + +func (s *strict) MissingTable(node ast.Node) { + if !s.Enabled { + return + } + + s.missing = append(s.missing, decodeError{ + highlight: keyLocation(node), + message: "missing table", + key: s.key.Key(), + }) +} + +func (s *strict) MissingField(node ast.Node) { + if !s.Enabled { + return + } + + s.missing = append(s.missing, decodeError{ + highlight: keyLocation(node), + message: "missing field", + key: s.key.Key(), + }) +} + +func (s *strict) Error(doc []byte) error { + if !s.Enabled || len(s.missing) == 0 { + return nil + } + + err := &StrictMissingError{ + Errors: make([]DecodeError, 0, len(s.missing)), + } + + for _, derr := range s.missing { + derr := derr + err.Errors = append(err.Errors, *wrapDecodeError(doc, &derr)) + } + + return err +} diff --git a/vendor/github.com/pelletier/go-toml/v2/targets.go b/vendor/github.com/pelletier/go-toml/v2/targets.go new file mode 100644 index 0000000000..370892f0dd --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/targets.go @@ -0,0 +1,536 @@ +package toml + +import ( + "fmt" + "math" + "reflect" + "strings" + "sync" +) + +type target interface { + // Dereferences the target. + get() reflect.Value + + // Store a string at the target. + setString(v string) + + // Store a boolean at the target + setBool(v bool) + + // Store an int64 at the target + setInt64(v int64) + + // Store a float64 at the target + setFloat64(v float64) + + // Stores any value at the target + set(v reflect.Value) +} + +// valueTarget just contains a reflect.Value that can be set. +// It is used for struct fields. +type valueTarget reflect.Value + +func (t valueTarget) get() reflect.Value { + return reflect.Value(t) +} + +func (t valueTarget) set(v reflect.Value) { + reflect.Value(t).Set(v) +} + +func (t valueTarget) setString(v string) { + t.get().SetString(v) +} + +func (t valueTarget) setBool(v bool) { + t.get().SetBool(v) +} + +func (t valueTarget) setInt64(v int64) { + t.get().SetInt(v) +} + +func (t valueTarget) setFloat64(v float64) { + t.get().SetFloat(v) +} + +// interfaceTarget wraps an other target to dereference on get. +type interfaceTarget struct { + x target +} + +func (t interfaceTarget) get() reflect.Value { + return t.x.get().Elem() +} + +func (t interfaceTarget) set(v reflect.Value) { + t.x.set(v) +} + +func (t interfaceTarget) setString(v string) { + panic("interface targets should always go through set") +} + +func (t interfaceTarget) setBool(v bool) { + panic("interface targets should always go through set") +} + +func (t interfaceTarget) setInt64(v int64) { + panic("interface targets should always go through set") +} + +func (t interfaceTarget) setFloat64(v float64) { + panic("interface targets should always go through set") +} + +// mapTarget targets a specific key of a map. +type mapTarget struct { + v reflect.Value + k reflect.Value +} + +func (t mapTarget) get() reflect.Value { + return t.v.MapIndex(t.k) +} + +func (t mapTarget) set(v reflect.Value) { + t.v.SetMapIndex(t.k, v) +} + +func (t mapTarget) setString(v string) { + t.set(reflect.ValueOf(v)) +} + +func (t mapTarget) setBool(v bool) { + t.set(reflect.ValueOf(v)) +} + +func (t mapTarget) setInt64(v int64) { + t.set(reflect.ValueOf(v)) +} + +func (t mapTarget) setFloat64(v float64) { + t.set(reflect.ValueOf(v)) +} + +// makes sure that the value pointed at by t is indexable (Slice, Array), or +// dereferences to an indexable (Ptr, Interface). +func ensureValueIndexable(t target) error { + f := t.get() + + switch f.Type().Kind() { + case reflect.Slice: + if f.IsNil() { + t.set(reflect.MakeSlice(f.Type(), 0, 0)) + return nil + } + case reflect.Interface: + if f.IsNil() || f.Elem().Type() != sliceInterfaceType { + t.set(reflect.MakeSlice(sliceInterfaceType, 0, 0)) + return nil + } + case reflect.Ptr: + panic("pointer should have already been dereferenced") + case reflect.Array: + // arrays are always initialized. + default: + return fmt.Errorf("toml: cannot store array in a %s", f.Kind()) + } + + return nil +} + +var ( + sliceInterfaceType = reflect.TypeOf([]interface{}{}) + mapStringInterfaceType = reflect.TypeOf(map[string]interface{}{}) +) + +func ensureMapIfInterface(x target) { + v := x.get() + + if v.Kind() == reflect.Interface && v.IsNil() { + newElement := reflect.MakeMap(mapStringInterfaceType) + + x.set(newElement) + } +} + +func setString(t target, v string) error { + f := t.get() + + switch f.Kind() { + case reflect.String: + t.setString(v) + case reflect.Interface: + t.set(reflect.ValueOf(v)) + default: + return fmt.Errorf("toml: cannot assign string to a %s", f.Kind()) + } + + return nil +} + +func setBool(t target, v bool) error { + f := t.get() + + switch f.Kind() { + case reflect.Bool: + t.setBool(v) + case reflect.Interface: + t.set(reflect.ValueOf(v)) + default: + return fmt.Errorf("toml: cannot assign boolean to a %s", f.Kind()) + } + + return nil +} + +const ( + maxInt = int64(^uint(0) >> 1) + minInt = -maxInt - 1 +) + +//nolint:funlen,gocognit,cyclop +func setInt64(t target, v int64) error { + f := t.get() + + switch f.Kind() { + case reflect.Int64: + t.setInt64(v) + case reflect.Int32: + if v < math.MinInt32 || v > math.MaxInt32 { + return fmt.Errorf("toml: number %d does not fit in an int32", v) + } + + t.set(reflect.ValueOf(int32(v))) + return nil + case reflect.Int16: + if v < math.MinInt16 || v > math.MaxInt16 { + return fmt.Errorf("toml: number %d does not fit in an int16", v) + } + + t.set(reflect.ValueOf(int16(v))) + case reflect.Int8: + if v < math.MinInt8 || v > math.MaxInt8 { + return fmt.Errorf("toml: number %d does not fit in an int8", v) + } + + t.set(reflect.ValueOf(int8(v))) + case reflect.Int: + if v < minInt || v > maxInt { + return fmt.Errorf("toml: number %d does not fit in an int", v) + } + + t.set(reflect.ValueOf(int(v))) + case reflect.Uint64: + if v < 0 { + return fmt.Errorf("toml: negative number %d does not fit in an uint64", v) + } + + t.set(reflect.ValueOf(uint64(v))) + case reflect.Uint32: + if v < 0 || v > math.MaxUint32 { + return fmt.Errorf("toml: negative number %d does not fit in an uint32", v) + } + + t.set(reflect.ValueOf(uint32(v))) + case reflect.Uint16: + if v < 0 || v > math.MaxUint16 { + return fmt.Errorf("toml: negative number %d does not fit in an uint16", v) + } + + t.set(reflect.ValueOf(uint16(v))) + case reflect.Uint8: + if v < 0 || v > math.MaxUint8 { + return fmt.Errorf("toml: negative number %d does not fit in an uint8", v) + } + + t.set(reflect.ValueOf(uint8(v))) + case reflect.Uint: + if v < 0 { + return fmt.Errorf("toml: negative number %d does not fit in an uint", v) + } + + t.set(reflect.ValueOf(uint(v))) + case reflect.Interface: + t.set(reflect.ValueOf(v)) + default: + return fmt.Errorf("toml: integer cannot be assigned to %s", f.Kind()) + } + + return nil +} + +func setFloat64(t target, v float64) error { + f := t.get() + + switch f.Kind() { + case reflect.Float64: + t.setFloat64(v) + case reflect.Float32: + if v > math.MaxFloat32 { + return fmt.Errorf("toml: number %f does not fit in a float32", v) + } + + t.set(reflect.ValueOf(float32(v))) + case reflect.Interface: + t.set(reflect.ValueOf(v)) + default: + return fmt.Errorf("toml: float cannot be assigned to %s", f.Kind()) + } + + return nil +} + +// Returns the element at idx of the value pointed at by target, or an error if +// t does not point to an indexable. +// If the target points to an Array and idx is out of bounds, it returns +// (nil, nil) as this is not a fatal error (the unmarshaler will skip). +func elementAt(t target, idx int) target { + f := t.get() + + switch f.Kind() { + case reflect.Slice: + //nolint:godox + // TODO: use the idx function argument and avoid alloc if possible. + idx := f.Len() + + t.set(reflect.Append(f, reflect.New(f.Type().Elem()).Elem())) + + return valueTarget(t.get().Index(idx)) + case reflect.Array: + if idx >= f.Len() { + return nil + } + + return valueTarget(f.Index(idx)) + case reflect.Interface: + // This function is called after ensureValueIndexable, so it's + // guaranteed that f contains an initialized slice. + ifaceElem := f.Elem() + idx := ifaceElem.Len() + newElem := reflect.New(ifaceElem.Type().Elem()).Elem() + newSlice := reflect.Append(ifaceElem, newElem) + + t.set(newSlice) + + return valueTarget(t.get().Elem().Index(idx)) + default: + // Why ensureValueIndexable let it go through? + panic(fmt.Errorf("elementAt received unhandled value type: %s", f.Kind())) + } +} + +func (d *decoder) scopeTableTarget(shouldAppend bool, t target, name string) (target, bool, error) { + x := t.get() + + switch x.Kind() { + // Kinds that need to recurse + case reflect.Interface: + t := scopeInterface(shouldAppend, t) + return d.scopeTableTarget(shouldAppend, t, name) + case reflect.Ptr: + t := scopePtr(t) + return d.scopeTableTarget(shouldAppend, t, name) + case reflect.Slice: + t := scopeSlice(shouldAppend, t) + shouldAppend = false + return d.scopeTableTarget(shouldAppend, t, name) + case reflect.Array: + t, err := d.scopeArray(shouldAppend, t) + if err != nil { + return t, false, err + } + shouldAppend = false + + return d.scopeTableTarget(shouldAppend, t, name) + + // Terminal kinds + case reflect.Struct: + return scopeStruct(x, name) + case reflect.Map: + if x.IsNil() { + t.set(reflect.MakeMap(x.Type())) + x = t.get() + } + + return scopeMap(x, name) + default: + panic(fmt.Sprintf("can't scope on a %s", x.Kind())) + } +} + +func scopeInterface(shouldAppend bool, t target) target { + initInterface(shouldAppend, t) + return interfaceTarget{t} +} + +func scopePtr(t target) target { + initPtr(t) + return valueTarget(t.get().Elem()) +} + +func initPtr(t target) { + x := t.get() + if !x.IsNil() { + return + } + + t.set(reflect.New(x.Type().Elem())) +} + +// initInterface makes sure that the interface pointed at by the target is not +// nil. +// Returns the target to the initialized value of the target. +func initInterface(shouldAppend bool, t target) { + x := t.get() + + if x.Kind() != reflect.Interface { + panic("this should only be called on interfaces") + } + + if !x.IsNil() && (x.Elem().Type() == sliceInterfaceType || x.Elem().Type() == mapStringInterfaceType) { + return + } + + var newElement reflect.Value + if shouldAppend { + newElement = reflect.MakeSlice(sliceInterfaceType, 0, 0) + } else { + newElement = reflect.MakeMap(mapStringInterfaceType) + } + + t.set(newElement) +} + +func scopeSlice(shouldAppend bool, t target) target { + v := t.get() + + if shouldAppend { + newElem := reflect.New(v.Type().Elem()) + newSlice := reflect.Append(v, newElem.Elem()) + + t.set(newSlice) + + v = t.get() + } + + return valueTarget(v.Index(v.Len() - 1)) +} + +func (d *decoder) scopeArray(shouldAppend bool, t target) (target, error) { + v := t.get() + + idx := d.arrayIndex(shouldAppend, v) + + if idx >= v.Len() { + return nil, fmt.Errorf("toml: impossible to insert element beyond array's size: %d", v.Len()) + } + + return valueTarget(v.Index(idx)), nil +} + +func scopeMap(v reflect.Value, name string) (target, bool, error) { + k := reflect.ValueOf(name) + + keyType := v.Type().Key() + if !k.Type().AssignableTo(keyType) { + if !k.Type().ConvertibleTo(keyType) { + return nil, false, fmt.Errorf("toml: cannot convert map key of type %s to expected type %s", k.Type(), keyType) + } + + k = k.Convert(keyType) + } + + if !v.MapIndex(k).IsValid() { + newElem := reflect.New(v.Type().Elem()) + v.SetMapIndex(k, newElem.Elem()) + } + + return mapTarget{ + v: v, + k: k, + }, true, nil +} + +type fieldPathsMap = map[string][]int + +type fieldPathsCache struct { + m map[reflect.Type]fieldPathsMap + l sync.RWMutex +} + +func (c *fieldPathsCache) get(t reflect.Type) (fieldPathsMap, bool) { + c.l.RLock() + paths, ok := c.m[t] + c.l.RUnlock() + + return paths, ok +} + +func (c *fieldPathsCache) set(t reflect.Type, m fieldPathsMap) { + c.l.Lock() + c.m[t] = m + c.l.Unlock() +} + +var globalFieldPathsCache = fieldPathsCache{ + m: map[reflect.Type]fieldPathsMap{}, + l: sync.RWMutex{}, +} + +func scopeStruct(v reflect.Value, name string) (target, bool, error) { + //nolint:godox + // TODO: cache this, and reduce allocations + fieldPaths, ok := globalFieldPathsCache.get(v.Type()) + if !ok { + fieldPaths = map[string][]int{} + + path := make([]int, 0, 16) + + var walk func(reflect.Value) + walk = func(v reflect.Value) { + t := v.Type() + for i := 0; i < t.NumField(); i++ { + l := len(path) + path = append(path, i) + f := t.Field(i) + + if f.Anonymous { + walk(v.Field(i)) + } else if f.PkgPath == "" { + // only consider exported fields + fieldName, ok := f.Tag.Lookup("toml") + if !ok { + fieldName = f.Name + } + + pathCopy := make([]int, len(path)) + copy(pathCopy, path) + + fieldPaths[fieldName] = pathCopy + // extra copy for the case-insensitive match + fieldPaths[strings.ToLower(fieldName)] = pathCopy + } + path = path[:l] + } + } + + walk(v) + + globalFieldPathsCache.set(v.Type(), fieldPaths) + } + + path, ok := fieldPaths[name] + if !ok { + path, ok = fieldPaths[strings.ToLower(name)] + } + + if !ok { + return nil, false, nil + } + + return valueTarget(v.FieldByIndex(path)), true, nil +} diff --git a/vendor/github.com/pelletier/go-toml/v2/toml.abnf b/vendor/github.com/pelletier/go-toml/v2/toml.abnf new file mode 100644 index 0000000000..473f3749e8 --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/toml.abnf @@ -0,0 +1,243 @@ +;; This document describes TOML's syntax, using the ABNF format (defined in +;; RFC 5234 -- https://www.ietf.org/rfc/rfc5234.txt). +;; +;; All valid TOML documents will match this description, however certain +;; invalid documents would need to be rejected as per the semantics described +;; in the supporting text description. + +;; It is possible to try this grammar interactively, using instaparse. +;; http://instaparse.mojombo.com/ +;; +;; To do so, in the lower right, click on Options and change `:input-format` to +;; ':abnf'. Then paste this entire ABNF document into the grammar entry box +;; (above the options). Then you can type or paste a sample TOML document into +;; the beige box on the left. Tada! + +;; Overall Structure + +toml = expression *( newline expression ) + +expression = ws [ comment ] +expression =/ ws keyval ws [ comment ] +expression =/ ws table ws [ comment ] + +;; Whitespace + +ws = *wschar +wschar = %x20 ; Space +wschar =/ %x09 ; Horizontal tab + +;; Newline + +newline = %x0A ; LF +newline =/ %x0D.0A ; CRLF + +;; Comment + +comment-start-symbol = %x23 ; # +non-ascii = %x80-D7FF / %xE000-10FFFF +non-eol = %x09 / %x20-7F / non-ascii + +comment = comment-start-symbol *non-eol + +;; Key-Value pairs + +keyval = key keyval-sep val + +key = simple-key / dotted-key +simple-key = quoted-key / unquoted-key + +unquoted-key = 1*( ALPHA / DIGIT / %x2D / %x5F ) ; A-Z / a-z / 0-9 / - / _ +quoted-key = basic-string / literal-string +dotted-key = simple-key 1*( dot-sep simple-key ) + +dot-sep = ws %x2E ws ; . Period +keyval-sep = ws %x3D ws ; = + +val = string / boolean / array / inline-table / date-time / float / integer + +;; String + +string = ml-basic-string / basic-string / ml-literal-string / literal-string + +;; Basic String + +basic-string = quotation-mark *basic-char quotation-mark + +quotation-mark = %x22 ; " + +basic-char = basic-unescaped / escaped +basic-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii +escaped = escape escape-seq-char + +escape = %x5C ; \ +escape-seq-char = %x22 ; " quotation mark U+0022 +escape-seq-char =/ %x5C ; \ reverse solidus U+005C +escape-seq-char =/ %x62 ; b backspace U+0008 +escape-seq-char =/ %x66 ; f form feed U+000C +escape-seq-char =/ %x6E ; n line feed U+000A +escape-seq-char =/ %x72 ; r carriage return U+000D +escape-seq-char =/ %x74 ; t tab U+0009 +escape-seq-char =/ %x75 4HEXDIG ; uXXXX U+XXXX +escape-seq-char =/ %x55 8HEXDIG ; UXXXXXXXX U+XXXXXXXX + +;; Multiline Basic String + +ml-basic-string = ml-basic-string-delim [ newline ] ml-basic-body + ml-basic-string-delim +ml-basic-string-delim = 3quotation-mark +ml-basic-body = *mlb-content *( mlb-quotes 1*mlb-content ) [ mlb-quotes ] + +mlb-content = mlb-char / newline / mlb-escaped-nl +mlb-char = mlb-unescaped / escaped +mlb-quotes = 1*2quotation-mark +mlb-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii +mlb-escaped-nl = escape ws newline *( wschar / newline ) + +;; Literal String + +literal-string = apostrophe *literal-char apostrophe + +apostrophe = %x27 ; ' apostrophe + +literal-char = %x09 / %x20-26 / %x28-7E / non-ascii + +;; Multiline Literal String + +ml-literal-string = ml-literal-string-delim [ newline ] ml-literal-body + ml-literal-string-delim +ml-literal-string-delim = 3apostrophe +ml-literal-body = *mll-content *( mll-quotes 1*mll-content ) [ mll-quotes ] + +mll-content = mll-char / newline +mll-char = %x09 / %x20-26 / %x28-7E / non-ascii +mll-quotes = 1*2apostrophe + +;; Integer + +integer = dec-int / hex-int / oct-int / bin-int + +minus = %x2D ; - +plus = %x2B ; + +underscore = %x5F ; _ +digit1-9 = %x31-39 ; 1-9 +digit0-7 = %x30-37 ; 0-7 +digit0-1 = %x30-31 ; 0-1 + +hex-prefix = %x30.78 ; 0x +oct-prefix = %x30.6F ; 0o +bin-prefix = %x30.62 ; 0b + +dec-int = [ minus / plus ] unsigned-dec-int +unsigned-dec-int = DIGIT / digit1-9 1*( DIGIT / underscore DIGIT ) + +hex-int = hex-prefix HEXDIG *( HEXDIG / underscore HEXDIG ) +oct-int = oct-prefix digit0-7 *( digit0-7 / underscore digit0-7 ) +bin-int = bin-prefix digit0-1 *( digit0-1 / underscore digit0-1 ) + +;; Float + +float = float-int-part ( exp / frac [ exp ] ) +float =/ special-float + +float-int-part = dec-int +frac = decimal-point zero-prefixable-int +decimal-point = %x2E ; . +zero-prefixable-int = DIGIT *( DIGIT / underscore DIGIT ) + +exp = "e" float-exp-part +float-exp-part = [ minus / plus ] zero-prefixable-int + +special-float = [ minus / plus ] ( inf / nan ) +inf = %x69.6e.66 ; inf +nan = %x6e.61.6e ; nan + +;; Boolean + +boolean = true / false + +true = %x74.72.75.65 ; true +false = %x66.61.6C.73.65 ; false + +;; Date and Time (as defined in RFC 3339) + +date-time = offset-date-time / local-date-time / local-date / local-time + +date-fullyear = 4DIGIT +date-month = 2DIGIT ; 01-12 +date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on month/year +time-delim = "T" / %x20 ; T, t, or space +time-hour = 2DIGIT ; 00-23 +time-minute = 2DIGIT ; 00-59 +time-second = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second rules +time-secfrac = "." 1*DIGIT +time-numoffset = ( "+" / "-" ) time-hour ":" time-minute +time-offset = "Z" / time-numoffset + +partial-time = time-hour ":" time-minute ":" time-second [ time-secfrac ] +full-date = date-fullyear "-" date-month "-" date-mday +full-time = partial-time time-offset + +;; Offset Date-Time + +offset-date-time = full-date time-delim full-time + +;; Local Date-Time + +local-date-time = full-date time-delim partial-time + +;; Local Date + +local-date = full-date + +;; Local Time + +local-time = partial-time + +;; Array + +array = array-open [ array-values ] ws-comment-newline array-close + +array-open = %x5B ; [ +array-close = %x5D ; ] + +array-values = ws-comment-newline val ws-comment-newline array-sep array-values +array-values =/ ws-comment-newline val ws-comment-newline [ array-sep ] + +array-sep = %x2C ; , Comma + +ws-comment-newline = *( wschar / [ comment ] newline ) + +;; Table + +table = std-table / array-table + +;; Standard Table + +std-table = std-table-open key std-table-close + +std-table-open = %x5B ws ; [ Left square bracket +std-table-close = ws %x5D ; ] Right square bracket + +;; Inline Table + +inline-table = inline-table-open [ inline-table-keyvals ] inline-table-close + +inline-table-open = %x7B ws ; { +inline-table-close = ws %x7D ; } +inline-table-sep = ws %x2C ws ; , Comma + +inline-table-keyvals = keyval [ inline-table-sep inline-table-keyvals ] + +;; Array Table + +array-table = array-table-open key array-table-close + +array-table-open = %x5B.5B ws ; [[ Double left square bracket +array-table-close = ws %x5D.5D ; ]] Double right square bracket + +;; Built-in ABNF terms, reproduced here for clarity + +ALPHA = %x41-5A / %x61-7A ; A-Z / a-z +DIGIT = %x30-39 ; 0-9 +HEXDIG = DIGIT / "A" / "B" / "C" / "D" / "E" / "F" diff --git a/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go b/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go new file mode 100644 index 0000000000..6a156ae6a6 --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go @@ -0,0 +1,593 @@ +package toml + +import ( + "encoding" + "errors" + "fmt" + "io" + "io/ioutil" + "reflect" + "time" + + "github.com/pelletier/go-toml/v2/internal/ast" + "github.com/pelletier/go-toml/v2/internal/tracker" + "github.com/pelletier/go-toml/v2/internal/unsafe" +) + +// Unmarshal deserializes a TOML document into a Go value. +// +// It is a shortcut for Decoder.Decode() with the default options. +func Unmarshal(data []byte, v interface{}) error { + p := parser{} + p.Reset(data) + d := decoder{} + + return d.FromParser(&p, v) +} + +// Decoder reads and decode a TOML document from an input stream. +type Decoder struct { + // input + r io.Reader + + // global settings + strict bool +} + +// NewDecoder creates a new Decoder that will read from r. +func NewDecoder(r io.Reader) *Decoder { + return &Decoder{r: r} +} + +// SetStrict toggles decoding in stict mode. +// +// When the decoder is in strict mode, it will record fields from the document +// that could not be set on the target value. In that case, the decoder returns +// a StrictMissingError that can be used to retrieve the individual errors as +// well as generate a human readable description of the missing fields. +func (d *Decoder) SetStrict(strict bool) { + d.strict = strict +} + +// Decode the whole content of r into v. +// +// By default, values in the document that don't exist in the target Go value +// are ignored. See Decoder.SetStrict() to change this behavior. +// +// When a TOML local date, time, or date-time is decoded into a time.Time, its +// value is represented in time.Local timezone. Otherwise the approriate Local* +// structure is used. +// +// Empty tables decoded in an interface{} create an empty initialized +// map[string]interface{}. +// +// Types implementing the encoding.TextUnmarshaler interface are decoded from a +// TOML string. +// +// When decoding a number, go-toml will return an error if the number is out of +// bounds for the target type (which includes negative numbers when decoding +// into an unsigned int). +// +// Type mapping +// +// List of supported TOML types and their associated accepted Go types: +// +// String -> string +// Integer -> uint*, int*, depending on size +// Float -> float*, depending on size +// Boolean -> bool +// Offset Date-Time -> time.Time +// Local Date-time -> LocalDateTime, time.Time +// Local Date -> LocalDate, time.Time +// Local Time -> LocalTime, time.Time +// Array -> slice and array, depending on elements types +// Table -> map and struct +// Inline Table -> same as Table +// Array of Tables -> same as Array and Table +func (d *Decoder) Decode(v interface{}) error { + b, err := ioutil.ReadAll(d.r) + if err != nil { + return fmt.Errorf("toml: %w", err) + } + + p := parser{} + p.Reset(b) + dec := decoder{ + strict: strict{ + Enabled: d.strict, + }, + } + + return dec.FromParser(&p, v) +} + +type decoder struct { + // Tracks position in Go arrays. + arrayIndexes map[reflect.Value]int + + // Tracks keys that have been seen, with which type. + seen tracker.SeenTracker + + // Strict mode + strict strict +} + +func (d *decoder) arrayIndex(shouldAppend bool, v reflect.Value) int { + if d.arrayIndexes == nil { + d.arrayIndexes = make(map[reflect.Value]int, 1) + } + + idx, ok := d.arrayIndexes[v] + + if !ok { + d.arrayIndexes[v] = 0 + } else if shouldAppend { + idx++ + d.arrayIndexes[v] = idx + } + + return idx +} + +func (d *decoder) FromParser(p *parser, v interface{}) error { + err := d.fromParser(p, v) + if err == nil { + return d.strict.Error(p.data) + } + + var e *decodeError + if errors.As(err, &e) { + return wrapDecodeError(p.data, e) + } + + return err +} + +func keyLocation(node ast.Node) []byte { + k := node.Key() + + hasOne := k.Next() + if !hasOne { + panic("should not be called with empty key") + } + + start := k.Node().Data + end := k.Node().Data + + for k.Next() { + end = k.Node().Data + } + + return unsafe.BytesRange(start, end) +} + +//nolint:funlen,cyclop +func (d *decoder) fromParser(p *parser, v interface{}) error { + r := reflect.ValueOf(v) + if r.Kind() != reflect.Ptr { + return fmt.Errorf("toml: decoding can only be performed into a pointer, not %s", r.Kind()) + } + + if r.IsNil() { + return fmt.Errorf("toml: decoding pointer target cannot be nil") + } + + var ( + skipUntilTable bool + root target = valueTarget(r.Elem()) + ) + + current := root + + for p.NextExpression() { + node := p.Expression() + + if node.Kind == ast.KeyValue && skipUntilTable { + continue + } + + err := d.seen.CheckExpression(node) + if err != nil { + return err + } + + var found bool + + switch node.Kind { + case ast.KeyValue: + err = d.unmarshalKeyValue(current, node) + found = true + case ast.Table: + d.strict.EnterTable(node) + + current, found, err = d.scopeWithKey(root, node.Key()) + if err == nil && found { + // In case this table points to an interface, + // make sure it at least holds something that + // looks like a table. Otherwise the information + // of a table is lost, and marshal cannot do the + // round trip. + ensureMapIfInterface(current) + } + case ast.ArrayTable: + d.strict.EnterArrayTable(node) + current, found, err = d.scopeWithArrayTable(root, node.Key()) + default: + panic(fmt.Sprintf("this should not be a top level node type: %s", node.Kind)) + } + + if err != nil { + return err + } + + if !found { + skipUntilTable = true + + d.strict.MissingTable(node) + } + } + + return p.Error() +} + +// scopeWithKey performs target scoping when unmarshaling an ast.KeyValue node. +// +// The goal is to hop from target to target recursively using the names in key. +// Parts of the key should be used to resolve field names for structs, and as +// keys when targeting maps. +// +// When encountering slices, it should always use its last element, and error +// if the slice does not have any. +func (d *decoder) scopeWithKey(x target, key ast.Iterator) (target, bool, error) { + var ( + err error + found bool + ) + + for key.Next() { + n := key.Node() + + x, found, err = d.scopeTableTarget(false, x, string(n.Data)) + if err != nil || !found { + return nil, found, err + } + } + + return x, true, nil +} + +//nolint:cyclop +// scopeWithArrayTable performs target scoping when unmarshaling an +// ast.ArrayTable node. +// +// It is the same as scopeWithKey, but when scoping the last part of the key +// it creates a new element in the array instead of using the last one. +func (d *decoder) scopeWithArrayTable(x target, key ast.Iterator) (target, bool, error) { + var ( + err error + found bool + ) + + for key.Next() { + n := key.Node() + if !n.Next().Valid() { // want to stop at one before last + break + } + + x, found, err = d.scopeTableTarget(false, x, string(n.Data)) + if err != nil || !found { + return nil, found, err + } + } + + n := key.Node() + + x, found, err = d.scopeTableTarget(false, x, string(n.Data)) + if err != nil || !found { + return x, found, err + } + + v := x.get() + + if v.Kind() == reflect.Ptr { + x = scopePtr(x) + v = x.get() + } + + if v.Kind() == reflect.Interface { + x = scopeInterface(true, x) + v = x.get() + } + + switch v.Kind() { + case reflect.Slice: + x = scopeSlice(true, x) + case reflect.Array: + x, err = d.scopeArray(true, x) + default: + } + + return x, found, err +} + +func (d *decoder) unmarshalKeyValue(x target, node ast.Node) error { + assertNode(ast.KeyValue, node) + + d.strict.EnterKeyValue(node) + defer d.strict.ExitKeyValue(node) + + x, found, err := d.scopeWithKey(x, node.Key()) + if err != nil { + return err + } + + // A struct in the path was not found. Skip this value. + if !found { + d.strict.MissingField(node) + + return nil + } + + return d.unmarshalValue(x, node.Value()) +} + +var textUnmarshalerType = reflect.TypeOf(new(encoding.TextUnmarshaler)).Elem() + +func tryTextUnmarshaler(x target, node ast.Node) (bool, error) { + v := x.get() + + if v.Kind() != reflect.Struct { + return false, nil + } + + // Special case for time, because we allow to unmarshal to it from + // different kind of AST nodes. + if v.Type() == timeType { + return false, nil + } + + if v.Type().Implements(textUnmarshalerType) { + err := v.Interface().(encoding.TextUnmarshaler).UnmarshalText(node.Data) + if err != nil { + return false, newDecodeError(node.Data, "error calling UnmarshalText: %w", err) + } + + return true, nil + } + + if v.CanAddr() && v.Addr().Type().Implements(textUnmarshalerType) { + err := v.Addr().Interface().(encoding.TextUnmarshaler).UnmarshalText(node.Data) + if err != nil { + return false, newDecodeError(node.Data, "error calling UnmarshalText: %w", err) + } + + return true, nil + } + + return false, nil +} + +//nolint:cyclop +func (d *decoder) unmarshalValue(x target, node ast.Node) error { + v := x.get() + + if v.Kind() == reflect.Ptr { + if !v.Elem().IsValid() { + x.set(reflect.New(v.Type().Elem())) + v = x.get() + } + + return d.unmarshalValue(valueTarget(v.Elem()), node) + } + + ok, err := tryTextUnmarshaler(x, node) + if ok { + return err + } + + switch node.Kind { + case ast.String: + return unmarshalString(x, node) + case ast.Bool: + return unmarshalBool(x, node) + case ast.Integer: + return unmarshalInteger(x, node) + case ast.Float: + return unmarshalFloat(x, node) + case ast.Array: + return d.unmarshalArray(x, node) + case ast.InlineTable: + return d.unmarshalInlineTable(x, node) + case ast.LocalDateTime: + return unmarshalLocalDateTime(x, node) + case ast.DateTime: + return unmarshalDateTime(x, node) + case ast.LocalDate: + return unmarshalLocalDate(x, node) + default: + panic(fmt.Sprintf("unhandled node kind %s", node.Kind)) + } +} + +func unmarshalLocalDate(x target, node ast.Node) error { + assertNode(ast.LocalDate, node) + + v, err := parseLocalDate(node.Data) + if err != nil { + return err + } + + setDate(x, v) + + return nil +} + +func unmarshalLocalDateTime(x target, node ast.Node) error { + assertNode(ast.LocalDateTime, node) + + v, rest, err := parseLocalDateTime(node.Data) + if err != nil { + return err + } + + if len(rest) > 0 { + return newDecodeError(rest, "extra characters at the end of a local date time") + } + + setLocalDateTime(x, v) + + return nil +} + +func unmarshalDateTime(x target, node ast.Node) error { + assertNode(ast.DateTime, node) + + v, err := parseDateTime(node.Data) + if err != nil { + return err + } + + setDateTime(x, v) + + return nil +} + +func setLocalDateTime(x target, v LocalDateTime) { + if x.get().Type() == timeType { + cast := v.In(time.Local) + + setDateTime(x, cast) + return + } + + x.set(reflect.ValueOf(v)) +} + +func setDateTime(x target, v time.Time) { + x.set(reflect.ValueOf(v)) +} + +var timeType = reflect.TypeOf(time.Time{}) + +func setDate(x target, v LocalDate) { + if x.get().Type() == timeType { + cast := v.In(time.Local) + + setDateTime(x, cast) + return + } + + x.set(reflect.ValueOf(v)) +} + +func unmarshalString(x target, node ast.Node) error { + assertNode(ast.String, node) + + return setString(x, string(node.Data)) +} + +func unmarshalBool(x target, node ast.Node) error { + assertNode(ast.Bool, node) + v := node.Data[0] == 't' + + return setBool(x, v) +} + +func unmarshalInteger(x target, node ast.Node) error { + assertNode(ast.Integer, node) + + v, err := parseInteger(node.Data) + if err != nil { + return err + } + + return setInt64(x, v) +} + +func unmarshalFloat(x target, node ast.Node) error { + assertNode(ast.Float, node) + + v, err := parseFloat(node.Data) + if err != nil { + return err + } + + return setFloat64(x, v) +} + +func (d *decoder) unmarshalInlineTable(x target, node ast.Node) error { + assertNode(ast.InlineTable, node) + + ensureMapIfInterface(x) + + it := node.Children() + for it.Next() { + n := it.Node() + + err := d.unmarshalKeyValue(x, n) + if err != nil { + return err + } + } + + return nil +} + +func (d *decoder) unmarshalArray(x target, node ast.Node) error { + assertNode(ast.Array, node) + + err := ensureValueIndexable(x) + if err != nil { + return err + } + + // Special work around when unmarshaling into an array. + // If the array is not addressable, for example when stored as a value in a + // map, calling elementAt in the inner function would fail. + // Instead, we allocate a new array that will be filled then inserted into + // the container. + // This problem does not exist with slices because they are addressable. + // There may be a better way of doing this, but it is not obvious to me + // with the target system. + if x.get().Kind() == reflect.Array { + container := x + newArrayPtr := reflect.New(x.get().Type()) + x = valueTarget(newArrayPtr.Elem()) + defer func() { + container.set(newArrayPtr.Elem()) + }() + } + + return d.unmarshalArrayInner(x, node) +} + +func (d *decoder) unmarshalArrayInner(x target, node ast.Node) error { + idx := 0 + + it := node.Children() + for it.Next() { + n := it.Node() + + v := elementAt(x, idx) + + if v == nil { + // when we go out of bound for an array just stop processing it to + // mimic encoding/json + break + } + + err := d.unmarshalValue(v, n) + if err != nil { + return err + } + + idx++ + } + return nil +} + +func assertNode(expected ast.Kind, node ast.Node) { + if node.Kind != expected { + panic(fmt.Sprintf("expected node of kind %s, not %s", expected, node.Kind)) + } +} diff --git a/vendor/knative.dev/eventing/test/lib/creation.go b/vendor/knative.dev/eventing/test/lib/creation.go index 70edfd7b59..63b13d601e 100644 --- a/vendor/knative.dev/eventing/test/lib/creation.go +++ b/vendor/knative.dev/eventing/test/lib/creation.go @@ -26,6 +26,7 @@ import ( corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/api/errors" + apierrs "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/util/retry" @@ -771,11 +772,15 @@ func (c *Client) CreatePodOrFail(pod *corev1.Pod, options ...func(*corev1.Pod, * c.applyAdditionalEnv(&pod.Spec) - err := reconciler.RetryUpdateConflicts(func(attempts int) (err error) { + // the following retryable errors are expected when creating a blank Pod: + // - update conflicts + // - "No API token found for service account %q, + // retry after the token is automatically created and added to the service account" + err := reconciler.RetryErrors(func(attempts int) (err error) { c.T.Logf("Creating pod %+v", pod) _, e := c.Kube.CreatePod(context.Background(), pod) return e - }) + }, apierrs.IsConflict, apierrs.IsServerTimeout) if err != nil { c.T.Fatalf("Failed to create pod %q: %v", pod.Name, err) } diff --git a/vendor/knative.dev/eventing/test/lib/resources/eventing.go b/vendor/knative.dev/eventing/test/lib/resources/eventing.go index 167f0cace3..092c8439e9 100644 --- a/vendor/knative.dev/eventing/test/lib/resources/eventing.go +++ b/vendor/knative.dev/eventing/test/lib/resources/eventing.go @@ -379,43 +379,61 @@ func WithDependencyAnnotationTriggerV1Beta1(dependencyAnnotation string) Trigger // WithSubscriberServiceRefForTriggerV1Beta1 returns an option that adds a Subscriber Knative Service Ref for the given v1beta1 Trigger. func WithSubscriberServiceRefForTriggerV1Beta1(name string) TriggerOptionV1Beta1 { - return func(t *eventingv1beta1.Trigger) { - if name != "" { - t.Spec.Subscriber = duckv1.Destination{ - Ref: KnativeRefForService(name, t.Namespace), - } + return WithSubscriberDestinationV1Beta1(func(t *eventingv1beta1.Trigger) duckv1.Destination { + return duckv1.Destination{ + Ref: KnativeRefForService(name, t.Namespace), } - } + }) } // WithSubscriberServiceRefForTriggerV1 returns an option that adds a Subscriber Knative Service Ref for the given v1 Trigger. func WithSubscriberServiceRefForTriggerV1(name string) TriggerOptionV1 { - return func(t *eventingv1.Trigger) { - if name != "" { - t.Spec.Subscriber = duckv1.Destination{ - Ref: KnativeRefForService(name, t.Namespace), - } + return WithSubscriberDestinationV1(func(t *eventingv1.Trigger) duckv1.Destination { + return duckv1.Destination{ + Ref: KnativeRefForService(name, t.Namespace), } - } + }) } // WithSubscriberURIForTriggerV1Beta1 returns an option that adds a Subscriber URI for the given v1beta1 Trigger. func WithSubscriberURIForTriggerV1Beta1(uri string) TriggerOptionV1Beta1 { - apisURI, _ := apis.ParseURL(uri) - return func(t *eventingv1beta1.Trigger) { - t.Spec.Subscriber = duckv1.Destination{ + return WithSubscriberDestinationV1Beta1(func(t *eventingv1beta1.Trigger) duckv1.Destination { + apisURI, _ := apis.ParseURL(uri) + return duckv1.Destination{ URI: apisURI, } - } + }) } // WithSubscriberURIForTriggerV1 returns an option that adds a Subscriber URI for the given v1 Trigger. func WithSubscriberURIForTriggerV1(uri string) TriggerOptionV1 { - apisURI, _ := apis.ParseURL(uri) - return func(t *eventingv1.Trigger) { - t.Spec.Subscriber = duckv1.Destination{ + return WithSubscriberDestinationV1(func(t *eventingv1.Trigger) duckv1.Destination { + apisURI, _ := apis.ParseURL(uri) + return duckv1.Destination{ URI: apisURI, } + }) +} + +// WithSubscriberDestinationV1Beta1 returns an option that adds a Subscriber for given +// duckv1.Destination. +func WithSubscriberDestinationV1Beta1(destFactory func(t *eventingv1beta1.Trigger) duckv1.Destination) TriggerOptionV1Beta1 { + return func(t *eventingv1beta1.Trigger) { + dest := destFactory(t) + if dest.Ref != nil || dest.URI != nil { + t.Spec.Subscriber = dest + } + } +} + +// WithSubscriberDestinationV1 returns an option that adds a Subscriber for given +// duckv1.Destination. +func WithSubscriberDestinationV1(destFactory func(t *eventingv1.Trigger) duckv1.Destination) TriggerOptionV1 { + return func(t *eventingv1.Trigger) { + dest := destFactory(t) + if dest.Ref != nil || dest.URI != nil { + t.Spec.Subscriber = dest + } } } diff --git a/vendor/knative.dev/eventing/test/upgrade/prober/config.toml b/vendor/knative.dev/eventing/test/upgrade/prober/config.toml index 02adec7f34..20afa87d6f 100644 --- a/vendor/knative.dev/eventing/test/upgrade/prober/config.toml +++ b/vendor/knative.dev/eventing/test/upgrade/prober/config.toml @@ -1,6 +1,6 @@ -# logLevel = 5 # DEBUG(5) +# logLevel = 'DEBUG' [sender] -address = '{{- .BrokerURL -}}' +address = '{{- .Endpoint -}}' interval = {{ .Config.Interval.Nanoseconds }} [forwarder] -target = 'http://wathola-receiver.{{- .Config.Namespace -}}.svc.cluster.local' +target = 'http://wathola-receiver.{{- .Namespace -}}.svc.cluster.local' diff --git a/vendor/knative.dev/eventing/test/upgrade/prober/configuration.go b/vendor/knative.dev/eventing/test/upgrade/prober/configuration.go index 34197e983c..c33dda3c17 100644 --- a/vendor/knative.dev/eventing/test/upgrade/prober/configuration.go +++ b/vendor/knative.dev/eventing/test/upgrade/prober/configuration.go @@ -20,63 +20,77 @@ import ( "context" "fmt" "io/ioutil" + "os" "path" "runtime" + "strings" "text/template" "time" "github.com/kelseyhightower/envconfig" "github.com/wavesoftware/go-ensure" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - eventingv1beta1 "knative.dev/eventing/pkg/apis/eventing/v1beta1" - testlib "knative.dev/eventing/test/lib" - "knative.dev/eventing/test/lib/duck" "knative.dev/eventing/test/lib/resources" - "knative.dev/pkg/apis" + "knative.dev/eventing/test/upgrade/prober/sut" + duckv1 "knative.dev/pkg/apis/duck/v1" + pkgTest "knative.dev/pkg/test" + pkgupgrade "knative.dev/pkg/test/upgrade" ) const ( - defaultConfigName = "wathola-config" - defaultConfigHomedirPath = ".config/wathola" - defaultHomedir = "/home/nonroot" - defaultConfigFilename = "config.toml" - defaultWatholaEventsPrefix = "com.github.cardil.wathola" - defaultBrokerName = "default" - defaultHealthEndpoint = "/healthz" - defaultFinishedSleep = 5 * time.Second + defaultConfigName = "wathola-config" + defaultConfigHomedirPath = ".config/wathola" + defaultHomedir = "/home/nonroot" + defaultConfigFilename = "config.toml" + defaultHealthEndpoint = "/healthz" + defaultFinishedSleep = 5 * time.Second Silence DuplicateAction = "silence" Warn DuplicateAction = "warn" Error DuplicateAction = "error" + + prefix = "eventing_upgrade_tests" + + // TODO(ksuszyns): Remove this val in next release + // Deprecated: use 'eventing_upgrade_tests' prefix instead + deprecatedPrefix = "e2e_upgrade_tests" ) // DuplicateAction is the action to take in case of duplicated events type DuplicateAction string -var eventTypes = []string{"step", "finished"} - // Config represents a configuration for prober. type Config struct { Wathola - Namespace string Interval time.Duration FinishedSleep time.Duration Serving ServingConfig FailOnErrors bool OnDuplicate DuplicateAction - BrokerOpts []resources.BrokerV1Beta1Option + Ctx context.Context + // BrokerOpts holds opts for broker. + // TODO(ksuszyns): Remove this opt in next release + // Deprecated: use Wathola.SystemUnderTest instead. + BrokerOpts []resources.BrokerV1Beta1Option + // Namespace holds namespace in which test is about to be executed. + // TODO(ksuszyns): Remove this opt in next release + // Deprecated: namespace is about to be taken from testlib.Client created by + // NewRunner + Namespace string } // Wathola represents options related strictly to wathola testing tool. type Wathola struct { - ConfigMap - EventsTypePrefix string - HealthEndpoint string - BrokerName string + ConfigToml + ImageResolver + SystemUnderTest sut.SystemUnderTest + HealthEndpoint string } -// ConfigMap represents options of wathola config toml file. -type ConfigMap struct { +// ImageResolver will resolve the container image for given component. +type ImageResolver func(component string) string + +// ConfigToml represents options of wathola config toml file. +type ConfigToml struct { // ConfigTemplate is a template file that will be compiled to the configmap ConfigTemplate string ConfigMapName string @@ -90,122 +104,130 @@ type ServingConfig struct { ScaleToZero bool } +// NewConfigOrFail will create a prober.Config or fail trying. +func NewConfigOrFail(c pkgupgrade.Context) *Config { + errSink := func(err error) { + c.T.Fatal(err) + } + warnf := c.Log.Warnf + return newConfig(errSink, warnf) +} + // NewConfig creates a new configuration object with default values filled in. // Values can be influenced by kelseyhightower/envconfig with -// `e2e_upgrade_tests` prefix. -func NewConfig(namespace string) *Config { +// `eventing_upgrade_tests` prefix. +// TODO(ksuszyns): Remove this func in next release +// Deprecated: use NewContinualVerification or NewConfigOrFail +func NewConfig(namespace ...string) *Config { + errSink := func(err error) { + ensure.NoError(err) + } + warnf := func(template string, args ...interface{}) { + _, err := fmt.Fprintf(os.Stderr, template, args) + ensure.NoError(err) + } + config := newConfig(errSink, warnf) + if len(namespace) > 0 { + config.Namespace = strings.Join(namespace, ",") + } + return config +} + +func newConfig( + errSink func(err error), warnf func(template string, args ...interface{}), +) *Config { config := &Config{ - Namespace: "", Interval: Interval, FinishedSleep: defaultFinishedSleep, FailOnErrors: true, OnDuplicate: Warn, - BrokerOpts: make([]resources.BrokerV1Beta1Option, 0), + Ctx: context.Background(), Serving: ServingConfig{ Use: false, ScaleToZero: true, }, Wathola: Wathola{ - ConfigMap: ConfigMap{ + ImageResolver: pkgTest.ImagePath, + ConfigToml: ConfigToml{ ConfigTemplate: defaultConfigFilename, ConfigMapName: defaultConfigName, ConfigMountPoint: fmt.Sprintf("%s/%s", defaultHomedir, defaultConfigHomedirPath), ConfigFilename: defaultConfigFilename, }, - EventsTypePrefix: defaultWatholaEventsPrefix, - HealthEndpoint: defaultHealthEndpoint, - BrokerName: defaultBrokerName, + HealthEndpoint: defaultHealthEndpoint, + SystemUnderTest: sut.NewDefault(), }, } - // FIXME: remove while fixing https://github.com/knative/eventing/issues/2665 - config.FailOnErrors = false + if err := envconfig.Process(prefix, config); err != nil { + errSink(err) + } - err := envconfig.Process("e2e_upgrade_tests", config) - ensure.NoError(err) - config.Namespace = namespace + // TODO(ksuszyns): Remove this block in next release + for _, enventry := range os.Environ() { + if strings.HasPrefix(strings.ToLower(enventry), deprecatedPrefix) { + warnf( + "DEPRECATED: using deprecated '%s' prefix. Use '%s' instead.", + deprecatedPrefix, prefix) + if err := envconfig.Process(deprecatedPrefix, config); err != nil { + errSink(err) + } + break + } + } return config } func (p *prober) deployConfiguration() { - p.deployBroker() - p.deployConfigMap() - p.deployTriggers() -} - -func (p *prober) deployBroker() { - p.client.CreateBrokerV1Beta1OrFail(p.config.BrokerName, p.config.BrokerOpts...) -} - -func (p *prober) fetchBrokerURL() (*apis.URL, error) { - namespace := p.config.Namespace - p.log.Debugf("Fetching %s broker URL for ns %s", - p.config.BrokerName, namespace) - meta := resources.NewMetaResource( - p.config.BrokerName, p.config.Namespace, testlib.BrokerTypeMeta, - ) - err := duck.WaitForResourceReady(p.client.Dynamic, meta) - if err != nil { - return nil, err + sc := sut.Context{ + Ctx: p.config.Ctx, + Log: p.log, + Client: p.client, } - broker, err := p.client.Eventing.EventingV1beta1().Brokers(namespace).Get( - context.Background(), p.config.BrokerName, metav1.GetOptions{}, - ) - if err != nil { - return nil, err + ref := resources.KnativeRefForService(receiverName, p.client.Namespace) + if p.config.Serving.Use { + ref = resources.KnativeRefForKservice(forwarderName, p.client.Namespace) } - url := broker.Status.Address.URL - p.log.Debugf("%s broker URL for ns %s is %v", - p.config.BrokerName, namespace, url) - return url, nil + dest := duckv1.Destination{Ref: ref} + s := p.config.SystemUnderTest + endpoint := s.Deploy(sc, dest) + p.client.Cleanup(func() { + if tr, ok := s.(sut.HasTeardown); ok { + tr.Teardown(sc) + } + }) + p.deployConfigToml(endpoint) } -func (p *prober) deployConfigMap() { +func (p *prober) deployConfigToml(endpoint interface{}) { name := p.config.ConfigMapName - p.log.Infof("Deploying config map: \"%s/%s\"", p.config.Namespace, name) - brokerURL, err := p.fetchBrokerURL() - ensure.NoError(err) - configData := p.compileTemplate(p.config.ConfigTemplate, brokerURL) - p.client.CreateConfigMapOrFail(name, p.config.Namespace, map[string]string{ + p.log.Infof("Deploying config map: \"%s/%s\"", p.client.Namespace, name) + configData := p.compileTemplate(p.config.ConfigTemplate, endpoint) + p.client.CreateConfigMapOrFail(name, p.client.Namespace, map[string]string{ p.config.ConfigFilename: configData, }) } -func (p *prober) deployTriggers() { - for _, eventType := range eventTypes { - name := fmt.Sprintf("wathola-trigger-%v", eventType) - fullType := fmt.Sprintf("%v.%v", p.config.EventsTypePrefix, eventType) - subscriberOption := resources.WithSubscriberServiceRefForTriggerV1Beta1(receiverName) - if p.config.Serving.Use { - subscriberOption = resources.WithSubscriberKServiceRefForTrigger(forwarderName) - } - p.client.CreateTriggerOrFailV1Beta1(name, - resources.WithBrokerV1Beta1(p.config.BrokerName), - resources.WithAttributesTriggerFilterV1Beta1( - eventingv1beta1.TriggerAnyFilter, - fullType, - map[string]interface{}{}, - ), - subscriberOption, - ) - } -} - -func (p *prober) compileTemplate(templateName string, brokerURL fmt.Stringer) string { +func (p *prober) compileTemplate(templateName string, endpoint interface{}) string { _, filename, _, _ := runtime.Caller(0) templateFilepath := path.Join(path.Dir(filename), templateName) templateBytes, err := ioutil.ReadFile(templateFilepath) - ensure.NoError(err) + p.ensureNoError(err) tmpl, err := template.New(templateName).Parse(string(templateBytes)) - ensure.NoError(err) + p.ensureNoError(err) var buff bytes.Buffer data := struct { *Config + Namespace string + // Deprecated: use Endpoint BrokerURL string + Endpoint interface{} }{ p.config, - brokerURL.String(), + p.client.Namespace, + fmt.Sprintf("%v", endpoint), + endpoint, } - ensure.NoError(tmpl.Execute(&buff, data)) + p.ensureNoError(tmpl.Execute(&buff, data)) return buff.String() } diff --git a/vendor/knative.dev/eventing/test/upgrade/prober/continual.go b/vendor/knative.dev/eventing/test/upgrade/prober/continual.go new file mode 100644 index 0000000000..237fdb582e --- /dev/null +++ b/vendor/knative.dev/eventing/test/upgrade/prober/continual.go @@ -0,0 +1,59 @@ +/* +Copyright 2021 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package prober + +import ( + testlib "knative.dev/eventing/test/lib" + pkgupgrade "knative.dev/pkg/test/upgrade" +) + +// Configurator will customize default config. +type Configurator func(config *Config) error + +// ContinualVerificationOptions holds options for NewContinualVerification func. +type ContinualVerificationOptions struct { + Configurators []Configurator + ClientOptions []testlib.SetupClientOption +} + +// NewContinualVerification will create a new continual verification operation +// that will verify that SUT is propagating events well through the test +// process. It's a general pattern that can be used to validate upgrade or +// performance testing. +func NewContinualVerification( + name string, opts ContinualVerificationOptions, +) pkgupgrade.BackgroundOperation { + var config *Config + var runner Runner + setup := func(c pkgupgrade.Context) { + config = NewConfigOrFail(c) + if len(opts.Configurators) > 0 { + for _, fn := range opts.Configurators { + err := fn(config) + if err != nil { + c.T.Fatal(err) + } + } + } + runner = NewRunner(config, opts.ClientOptions...) + runner.Setup(c) + } + verify := func(c pkgupgrade.Context) { + runner.Verify(c) + } + return pkgupgrade.NewBackgroundVerification(name, setup, verify) +} diff --git a/vendor/knative.dev/eventing/test/upgrade/prober/errors.go b/vendor/knative.dev/eventing/test/upgrade/prober/errors.go new file mode 100644 index 0000000000..e70da17bd9 --- /dev/null +++ b/vendor/knative.dev/eventing/test/upgrade/prober/errors.go @@ -0,0 +1,23 @@ +/* +Copyright 2021 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package prober + +func (p *prober) ensureNoError(err error) { + if err != nil { + p.client.T.Fatal(err) + } +} diff --git a/vendor/knative.dev/eventing/test/upgrade/prober/forwarder.go b/vendor/knative.dev/eventing/test/upgrade/prober/forwarder.go index cfda56554b..14965fafb8 100644 --- a/vendor/knative.dev/eventing/test/upgrade/prober/forwarder.go +++ b/vendor/knative.dev/eventing/test/upgrade/prober/forwarder.go @@ -16,28 +16,26 @@ package prober import ( - "context" "fmt" - "github.com/wavesoftware/go-ensure" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" testlib "knative.dev/eventing/test/lib" "knative.dev/eventing/test/lib/duck" "knative.dev/eventing/test/lib/resources" - pkgTest "knative.dev/pkg/test" ) var ( forwarderName = "wathola-forwarder" ) -func (p *prober) deployForwarder(ctx context.Context) { +func (p *prober) deployForwarder() { p.log.Infof("Deploy forwarder knative service: %v", forwarderName) serving := p.client.Dynamic.Resource(resources.KServicesGVR).Namespace(p.client.Namespace) service := p.forwarderKService(forwarderName, p.client.Namespace) - _, err := serving.Create(context.Background(), service, metav1.CreateOptions{}) - ensure.NoError(err) + if _, err := serving.Create(p.config.Ctx, service, metav1.CreateOptions{}); err != nil { + p.client.T.Fatal(err) + } sc := p.servingClient() testlib.WaitFor(fmt.Sprintf("forwarder ksvc be ready: %v", forwarderName), func() error { @@ -46,7 +44,7 @@ func (p *prober) deployForwarder(ctx context.Context) { if p.config.Serving.ScaleToZero { testlib.WaitFor(fmt.Sprintf("forwarder scales to zero: %v", forwarderName), func() error { - return duck.WaitForKServiceScales(ctx, sc, forwarderName, p.client.Namespace, func(scale int) bool { + return duck.WaitForKServiceScales(p.config.Ctx, sc, forwarderName, p.client.Namespace, func(scale int) bool { return scale == 0 }) }) @@ -56,8 +54,8 @@ func (p *prober) deployForwarder(ctx context.Context) { func (p *prober) removeForwarder() { p.log.Infof("Remove forwarder knative service: %v", forwarderName) serving := p.client.Dynamic.Resource(resources.KServicesGVR).Namespace(p.client.Namespace) - err := serving.Delete(context.Background(), forwarderName, metav1.DeleteOptions{}) - ensure.NoError(err) + err := serving.Delete(p.config.Ctx, forwarderName, metav1.DeleteOptions{}) + p.ensureNoError(err) } func (p *prober) forwarderKService(name, namespace string) *unstructured.Unstructured { @@ -68,15 +66,15 @@ func (p *prober) forwarderKService(name, namespace string) *unstructured.Unstruc "name": name, "namespace": namespace, "labels": map[string]string{ - "serving.knative.dev/visibility": "cluster-local", + "networking.knative.dev/visibility": "cluster-local", }, }, "spec": map[string]interface{}{ "template": map[string]interface{}{ "spec": map[string]interface{}{ "containers": []map[string]interface{}{{ - "name": "forwarder", - "image": pkgTest.ImagePath(forwarderName), + "name": forwarderName, + "image": p.config.ImageResolver(forwarderName), "volumeMounts": []map[string]interface{}{{ "name": p.config.ConfigMapName, "mountPath": p.config.ConfigMountPoint, diff --git a/vendor/knative.dev/eventing/test/upgrade/prober/prober.go b/vendor/knative.dev/eventing/test/upgrade/prober/prober.go index a448c4ca27..201dc68622 100644 --- a/vendor/knative.dev/eventing/test/upgrade/prober/prober.go +++ b/vendor/knative.dev/eventing/test/upgrade/prober/prober.go @@ -17,52 +17,81 @@ package prober import ( "context" + "reflect" "testing" "time" - "github.com/wavesoftware/go-ensure" "go.uber.org/zap" testlib "knative.dev/eventing/test/lib" "knative.dev/eventing/test/lib/resources" + "knative.dev/eventing/test/upgrade/prober/sut" + pkgupgrade "knative.dev/pkg/test/upgrade" ) var ( - // FIXME: Interval is set to 200 msec, as lower values will result in errors: knative/eventing#2357 - // Interval = 10 * time.Millisecond - Interval = 200 * time.Millisecond + // Interval is used to send events in specific rate. + Interval = 10 * time.Millisecond ) -// Prober is the interface for a prober, which checks the result of the probes when stopped. +// Prober is the interface for a prober, which checks the result of the probes +// when stopped. +// TODO(ksuszyns): Remove this interface in next release +// Deprecated: use Runner instead, create it with NewRunner func. type Prober interface { // Verify will verify prober state after finished has been send - Verify(ctx context.Context) ([]error, int) + Verify() ([]error, int) // Finish send finished event - Finish(ctx context.Context) + Finish() // ReportErrors will reports found errors in proper way - ReportErrors(t *testing.T, errors []error) + ReportErrors(errors []error) +} + +// Runner will run continual verification with provided configuration. +type Runner interface { + // Setup will start a continual prober in background. + Setup(ctx pkgupgrade.Context) - // deploy a prober to a cluster - deploy(ctx context.Context) - // remove a prober from cluster - remove() + // Verify will verify that all sent events propagated at least once. + Verify(ctx pkgupgrade.Context) } -// RunEventProber starts a single Prober of the given domain. -func RunEventProber(ctx context.Context, log *zap.SugaredLogger, client *testlib.Client, config *Config) Prober { - pm := newProber(log, client, config) - pm.deploy(ctx) - return pm +// NewRunner will create a runner compatible with NewContinualVerification +// func. +func NewRunner(config *Config, options ...testlib.SetupClientOption) Runner { + return &probeRunner{ + prober: &prober{config: config}, + options: options, + } } -// AssertEventProber will send finish event and then verify if all events propagated well -func AssertEventProber(ctx context.Context, t *testing.T, prober Prober) { - prober.Finish(ctx) +type probeRunner struct { + *prober + options []testlib.SetupClientOption +} - waitAfterFinished(prober) +func (p *probeRunner) Setup(ctx pkgupgrade.Context) { + p.validate(ctx) + p.log = ctx.Log + p.client = testlib.Setup(ctx.T, false, p.options...) + p.deploy() +} - errors, events := prober.Verify(ctx) +func (p *probeRunner) Verify(ctx pkgupgrade.Context) { + if p.client == nil { + ctx.T.Fatal("prober isn't initiated (client is nil)") + return + } + // use T from new test + p.client.T = ctx.T + t := ctx.T + defer testlib.TearDown(p.client) + defer p.remove() + p.Finish() + waitAfterFinished(p.prober) + + errors, events := p.prober.Verify() if len(errors) == 0 { t.Logf("All %d events propagated well", events) } else { @@ -70,9 +99,62 @@ func AssertEventProber(ctx context.Context, t *testing.T, prober Prober) { "Listing them below.", events, len(errors)) } - prober.ReportErrors(t, errors) + p.ReportErrors(errors) +} + +func (p *probeRunner) validate(ctx pkgupgrade.Context) { + if p.config.Namespace != "" { + ctx.Log.Warnf( + "DEPRECATED: namespace set in Config: %s. Ignoring it.", + p.client.Namespace) + } + if len(p.config.BrokerOpts) > 0 { + ctx.Log.Warn( + "DEPRECATED: BrokerOpts set in Config. Use custom SystemUnderTest") + if reflect.ValueOf(p.config.Wathola.SystemUnderTest) == reflect.ValueOf(sut.NewDefault) { + bt := sut.NewDefault().(*sut.BrokerAndTriggers) + bt.Opts = append(bt.Opts, p.config.BrokerOpts...) + p.config.Wathola.SystemUnderTest = bt + } else { + ctx.T.Fatal("Can't use given BrokerOpts, as custom SUT is used as " + + "well. Drop using BrokerOpts in favor of custom SUT.") + } + } +} + +// RunEventProber starts a single Prober of the given domain. +// TODO(ksuszyns): Remove this func in next release +// Deprecated: use NewRunner func instead. +func RunEventProber(ctx context.Context, log *zap.SugaredLogger, client *testlib.Client, config *Config) Prober { + log.Warn("prober.RunEventProber is deprecated. Use NewRunner instead.") + config.Ctx = ctx + p := &prober{ + log: log, + client: client, + config: config, + } + p.deploy() + return p +} - prober.remove() +// AssertEventProber will send finish event and then verify if all events +// propagated well. +// TODO(ksuszyns): Remove this func in next release +// Deprecated: use NewRunner func instead. +func AssertEventProber(ctx context.Context, t *testing.T, probe Prober) { + t.Log("WARN: prober.AssertEventProber is deprecated. " + + "Use NewRunner instead.") + p := probe.(*prober) + p.client.T = t + p.config.Ctx = ctx + pr := &probeRunner{ + prober: p, + options: nil, + } + pr.Verify(pkgupgrade.Context{ + T: t, + Log: p.log, + }) } type prober struct { @@ -88,7 +170,8 @@ func (p *prober) servingClient() resources.ServingClient { } } -func (p *prober) ReportErrors(t *testing.T, errors []error) { +func (p *prober) ReportErrors(errors []error) { + t := p.client.T for _, err := range errors { if p.config.FailOnErrors { t.Error(err) @@ -104,17 +187,17 @@ func (p *prober) ReportErrors(t *testing.T, errors []error) { } } -func (p *prober) deploy(ctx context.Context) { +func (p *prober) deploy() { p.log.Infof("Using namespace for probe testing: %v", p.client.Namespace) p.deployConfiguration() - p.deployReceiver(ctx) + p.deployReceiver() if p.config.Serving.Use { - p.deployForwarder(ctx) + p.deployForwarder() } - p.client.WaitForAllTestResourcesReadyOrFail(ctx) + p.client.WaitForAllTestResourcesReadyOrFail(p.config.Ctx) - p.deploySender(ctx) - ensure.NoError(testlib.AwaitForAll(p.log)) + p.deploySender() + p.ensureNoError(testlib.AwaitForAll(p.log)) // allow sender to send at least some events, 2 sec wait time.Sleep(2 * time.Second) p.log.Infof("Prober is now sending events with interval of %v in "+ @@ -125,20 +208,10 @@ func (p *prober) remove() { if p.config.Serving.Use { p.removeForwarder() } - ensure.NoError(p.client.Tracker.Clean(true)) -} - -func newProber(log *zap.SugaredLogger, client *testlib.Client, config *Config) Prober { - return &prober{ - log: log, - client: client, - config: config, - } + p.ensureNoError(p.client.Tracker.Clean(true)) } -func waitAfterFinished(p Prober) { - s := p.(*prober) - cfg := s.config - s.log.Infof("Waiting %v after sender finished...", cfg.FinishedSleep) - time.Sleep(cfg.FinishedSleep) +func waitAfterFinished(p *prober) { + p.log.Infof("Waiting %v after sender finished...", p.config.FinishedSleep) + time.Sleep(p.config.FinishedSleep) } diff --git a/vendor/knative.dev/eventing/test/upgrade/prober/receiver.go b/vendor/knative.dev/eventing/test/upgrade/prober/receiver.go index 2b4f5883cf..d553798819 100644 --- a/vendor/knative.dev/eventing/test/upgrade/prober/receiver.go +++ b/vendor/knative.dev/eventing/test/upgrade/prober/receiver.go @@ -16,10 +16,8 @@ package prober import ( - "context" "fmt" - "github.com/wavesoftware/go-ensure" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -30,36 +28,32 @@ import ( ) var ( - receiverName = "wathola-receiver" - receiverNodePort int32 = -1 + receiverName = "wathola-receiver" ) -func (p *prober) deployReceiver(ctx context.Context) { - p.deployReceiverDeployment(ctx) - p.deployReceiverService(ctx) +func (p *prober) deployReceiver() { + p.deployReceiverDeployment() + p.deployReceiverService() } -func (p *prober) deployReceiverDeployment(ctx context.Context) { +func (p *prober) deployReceiverDeployment() { p.log.Info("Deploy of receiver deployment: ", receiverName) deployment := p.createReceiverDeployment() - _, err := p.client.Kube.AppsV1(). - Deployments(deployment.Namespace). - Create(ctx, deployment, metav1.CreateOptions{}) - ensure.NoError(err) + p.client.CreateDeploymentOrFail(deployment) testlib.WaitFor(fmt.Sprint("receiver deployment be ready: ", receiverName), func() error { return pkgTest.WaitForDeploymentScale( - ctx, p.client.Kube, receiverName, p.client.Namespace, 1, + p.config.Ctx, p.client.Kube, receiverName, p.client.Namespace, 1, ) }) } -func (p *prober) deployReceiverService(ctx context.Context) { +func (p *prober) deployReceiverService() { p.log.Infof("Deploy of receiver service: %v", receiverName) service := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: receiverName, - Namespace: p.config.Namespace, + Namespace: p.client.Namespace, }, Spec: corev1.ServiceSpec{ Ports: []corev1.ServicePort{ @@ -76,22 +70,10 @@ func (p *prober) deployReceiverService(ctx context.Context) { Selector: map[string]string{ "app": receiverName, }, - Type: corev1.ServiceTypeNodePort, + Type: corev1.ServiceTypeClusterIP, }, } - created, err := p.client.Kube.CoreV1().Services(p.config.Namespace). - Create(ctx, service, metav1.CreateOptions{}) - ensure.NoError(err) - for _, portSpec := range created.Spec.Ports { - if portSpec.Port == 80 { - receiverNodePort = portSpec.NodePort - } - } - if receiverNodePort == -1 { - panic(fmt.Errorf("couldn't find a node port for service: %v", receiverName)) - } else { - p.log.Debugf("Node port for service: %v is %v", receiverName, receiverNodePort) - } + p.client.CreateServiceOrFail(service) } func (p *prober) createReceiverDeployment() *appsv1.Deployment { @@ -99,7 +81,7 @@ func (p *prober) createReceiverDeployment() *appsv1.Deployment { return &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: receiverName, - Namespace: p.config.Namespace, + Namespace: p.client.Namespace, }, Spec: appsv1.DeploymentSpec{ Replicas: &replicas, @@ -127,7 +109,7 @@ func (p *prober) createReceiverDeployment() *appsv1.Deployment { }}, Containers: []corev1.Container{{ Name: "receiver", - Image: pkgTest.ImagePath(receiverName), + Image: p.config.ImageResolver(receiverName), VolumeMounts: []corev1.VolumeMount{{ Name: p.config.ConfigMapName, ReadOnly: true, diff --git a/vendor/knative.dev/eventing/test/upgrade/prober/sender.go b/vendor/knative.dev/eventing/test/upgrade/prober/sender.go index 0d5be70188..5f3bdd4ec9 100644 --- a/vendor/knative.dev/eventing/test/upgrade/prober/sender.go +++ b/vendor/knative.dev/eventing/test/upgrade/prober/sender.go @@ -16,10 +16,8 @@ package prober import ( - "context" "fmt" - "github.com/wavesoftware/go-ensure" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -29,13 +27,13 @@ import ( var senderName = "wathola-sender" -func (p *prober) deploySender(ctx context.Context) { +func (p *prober) deploySender() { p.log.Info("Deploy sender deployment: ", senderName) var replicas int32 = 1 deployment := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: senderName, - Namespace: p.config.Namespace, + Namespace: p.client.Namespace, }, Spec: appsv1.DeploymentSpec{ Replicas: &replicas, @@ -63,7 +61,7 @@ func (p *prober) deploySender(ctx context.Context) { }}, Containers: []corev1.Container{{ Name: "sender", - Image: pkgTest.ImagePath(senderName), + Image: p.config.ImageResolver(senderName), VolumeMounts: []corev1.VolumeMount{{ Name: p.config.ConfigMapName, ReadOnly: true, @@ -77,21 +75,21 @@ func (p *prober) deploySender(ctx context.Context) { _, err := p.client.Kube.AppsV1(). Deployments(p.client.Namespace). - Create(ctx, deployment, metav1.CreateOptions{}) - ensure.NoError(err) + Create(p.config.Ctx, deployment, metav1.CreateOptions{}) + p.ensureNoError(err) testlib.WaitFor(fmt.Sprint("sender deployment be ready: ", senderName), func() error { return pkgTest.WaitForDeploymentScale( - ctx, p.client.Kube, senderName, p.client.Namespace, int(replicas), + p.config.Ctx, p.client.Kube, senderName, p.client.Namespace, int(replicas), ) }) } -func (p *prober) removeSender(ctx context.Context) { +func (p *prober) removeSender() { p.log.Info("Remove of sender deployment: ", senderName) err := p.client.Kube.AppsV1(). Deployments(p.client.Namespace). - Delete(ctx, senderName, metav1.DeleteOptions{}) - ensure.NoError(err) + Delete(p.config.Ctx, senderName, metav1.DeleteOptions{}) + p.ensureNoError(err) } diff --git a/vendor/knative.dev/eventing/test/upgrade/prober/sut/broker.go b/vendor/knative.dev/eventing/test/upgrade/prober/sut/broker.go new file mode 100644 index 0000000000..488749e0ce --- /dev/null +++ b/vendor/knative.dev/eventing/test/upgrade/prober/sut/broker.go @@ -0,0 +1,130 @@ +/* +Copyright 2021 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package sut + +import ( + "fmt" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + eventingduckv1beta1 "knative.dev/eventing/pkg/apis/duck/v1beta1" + eventingv1beta1 "knative.dev/eventing/pkg/apis/eventing/v1beta1" + testlib "knative.dev/eventing/test/lib" + "knative.dev/eventing/test/lib/duck" + "knative.dev/eventing/test/lib/resources" + "knative.dev/pkg/apis" + duckv1 "knative.dev/pkg/apis/duck/v1" +) + +var ( + retryCount = int32(12) + backoffPolicy = eventingduckv1beta1.BackoffPolicyExponential + backoffDelay = "PT1S" +) + +// BrokerAndTriggers will deploy a default broker and 2 triggers to route two types of +// events to receiver. +type BrokerAndTriggers struct { + Broker + Triggers +} + +// Broker will hold settings for broker itself +type Broker struct { + Name string + Opts []resources.BrokerV1Beta1Option +} + +// Triggers will hold settings for triggers +type Triggers struct { + Types []string +} + +// NewBrokerAndTriggers will create default configuration for BrokerAndTriggers +// based SUT. +func NewBrokerAndTriggers() SystemUnderTest { + return &BrokerAndTriggers{ + Broker: Broker{ + Name: "sut", + Opts: []resources.BrokerV1Beta1Option{ + resources.WithDeliveryForBrokerV1Beta1( + &eventingduckv1beta1.DeliverySpec{ + Retry: &retryCount, + BackoffPolicy: &backoffPolicy, + BackoffDelay: &backoffDelay, + }), + }, + }, + Triggers: Triggers{ + Types: eventTypes, + }, + } +} + +func (b *BrokerAndTriggers) Deploy(ctx Context, dest duckv1.Destination) interface{} { + b.deployBroker(ctx) + url := b.fetchURL(ctx) + b.deployTriggers(ctx, dest) + return url +} + +func (b *BrokerAndTriggers) deployBroker(ctx Context) { + ctx.Client.CreateBrokerV1Beta1OrFail(b.Name, b.Broker.Opts...) +} + +func (b *BrokerAndTriggers) fetchURL(ctx Context) *apis.URL { + namespace := ctx.Client.Namespace + ctx.Log.Debugf("Fetching \"%s\" broker URL for ns %s", + b.Name, namespace) + meta := resources.NewMetaResource( + b.Name, namespace, testlib.BrokerTypeMeta, + ) + err := duck.WaitForResourceReady(ctx.Client.Dynamic, meta) + if err != nil { + ctx.T.Fatal(err) + } + broker, err := ctx.Client.Eventing.EventingV1beta1().Brokers(namespace).Get( + ctx.Ctx, b.Name, metav1.GetOptions{}, + ) + if err != nil { + ctx.T.Fatal(err) + } + url := broker.Status.Address.URL + ctx.Log.Debugf("\"%s\" broker URL for ns %s is %v", + b.Name, namespace, url) + return url +} + +func (b *BrokerAndTriggers) deployTriggers(ctx Context, dest duckv1.Destination) { + for _, eventType := range b.Triggers.Types { + name := fmt.Sprintf("%s-%s", b.Name, eventType) + subscriberOption := resources.WithSubscriberDestinationV1Beta1(func(t *eventingv1beta1.Trigger) duckv1.Destination { + return dest + }) + ctx.Log.Debugf("Creating trigger \"%s\" for type %s to route to %#v", + name, eventType, dest) + _ = ctx.Client.CreateTriggerOrFailV1Beta1( + name, + resources.WithBrokerV1Beta1(b.Name), + resources.WithAttributesTriggerFilterV1Beta1( + eventingv1beta1.TriggerAnyFilter, + eventType, + map[string]interface{}{}, + ), + subscriberOption, + ) + } +} diff --git a/vendor/knative.dev/eventing/test/upgrade/prober/sut/default.go b/vendor/knative.dev/eventing/test/upgrade/prober/sut/default.go new file mode 100644 index 0000000000..1f11edbdd2 --- /dev/null +++ b/vendor/knative.dev/eventing/test/upgrade/prober/sut/default.go @@ -0,0 +1,22 @@ +/* +Copyright 2021 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package sut + +// NewDefault will return a default SUT for this repo. +func NewDefault() SystemUnderTest { + return NewBrokerAndTriggers() +} diff --git a/vendor/knative.dev/eventing/test/upgrade/prober/sut/types.go b/vendor/knative.dev/eventing/test/upgrade/prober/sut/types.go new file mode 100644 index 0000000000..8f13410195 --- /dev/null +++ b/vendor/knative.dev/eventing/test/upgrade/prober/sut/types.go @@ -0,0 +1,54 @@ +/* +Copyright 2021 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package sut + +import ( + "context" + + "go.uber.org/zap" + testlib "knative.dev/eventing/test/lib" + watholaevent "knative.dev/eventing/test/upgrade/prober/wathola/event" + duckv1 "knative.dev/pkg/apis/duck/v1" +) + +var eventTypes = []string{ + watholaevent.Step{}.Type(), + watholaevent.Finished{}.Type(), +} + +// SystemUnderTest (SUT) represents a system that we'd like to test with +// continual prober. +type SystemUnderTest interface { + // Deploy is responsible for deploying SUT and returning a URL to feed + // events into. + Deploy(ctx Context, destination duckv1.Destination) interface{} +} + +// HasTeardown indicates that SystemUnderTest supports custom teardown that +// exceeds regular teardown via usage of testlib.Tracker. +type HasTeardown interface { + // Teardown will remove all deployed SUT resources. + Teardown(ctx Context) +} + +// Context represents a context of system under test that we'd +// like to deploy and teardown. +type Context struct { + Ctx context.Context + Log *zap.SugaredLogger + *testlib.Client +} diff --git a/vendor/knative.dev/eventing/test/upgrade/prober/verify.go b/vendor/knative.dev/eventing/test/upgrade/prober/verify.go index 66dbc0b653..22889a2bd6 100644 --- a/vendor/knative.dev/eventing/test/upgrade/prober/verify.go +++ b/vendor/knative.dev/eventing/test/upgrade/prober/verify.go @@ -18,11 +18,9 @@ package prober import ( "context" "encoding/json" - "errors" "fmt" "time" - "github.com/wavesoftware/go-ensure" "go.uber.org/zap" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" @@ -42,8 +40,8 @@ const ( ) // Verify will verify prober state after finished has been sent. -func (p *prober) Verify(ctx context.Context) ([]error, int) { - report := p.fetchReport(ctx) +func (p *prober) Verify() (eventErrs []error, eventsSent int) { + report := p.fetchReport() availRate := 0.0 if report.TotalRequests != 0 { availRate = float64(report.EventsSent*100) / float64(report.TotalRequests) @@ -53,35 +51,34 @@ func (p *prober) Verify(ctx context.Context) ([]error, int) { p.log.Infof("Availability: %.3f%%, Requests sent: %d.", availRate, report.TotalRequests) if report.State == "active" { - panic(errors.New("report fetched too early, receiver is in active state")) + p.client.T.Fatal("report fetched too early, receiver is in active state") } - errs := make([]error, 0) - for _, t := range report.Thrown.Missing { - errs = append(errs, errors.New(t)) + for _, e := range report.Thrown.Missing { + p.client.T.Error(e) } - for _, t := range report.Thrown.Unexpected { - errs = append(errs, errors.New(t)) + for _, e := range report.Thrown.Unexpected { + p.client.T.Error(e) } - for _, t := range report.Thrown.Unavailable { - errs = append(errs, errors.New(t)) + for _, e := range report.Thrown.Unavailable { + p.client.T.Error(e) } - for _, t := range report.Thrown.Duplicated { + for _, e := range report.Thrown.Duplicated { if p.config.OnDuplicate == Warn { - p.log.Warn("Duplicate events: ", t) + p.log.Warn("Duplicate events: ", e) } else if p.config.OnDuplicate == Error { - errs = append(errs, errors.New(t)) + p.client.T.Error(e) } } - return errs, report.EventsSent + return eventErrs, report.EventsSent } // Finish terminates sender which sends finished event. -func (p *prober) Finish(ctx context.Context) { - p.removeSender(ctx) +func (p *prober) Finish() { + p.removeSender() } -func (p *prober) fetchReport(ctx context.Context) *receiver.Report { - exec := p.fetchExecution(ctx) +func (p *prober) fetchReport() *receiver.Report { + exec := p.fetchExecution() replayLogs(p.log, exec) return exec.Report } @@ -101,14 +98,14 @@ func replayLogs(log *zap.SugaredLogger, exec *fetcher.Execution) { } } -func (p *prober) fetchExecution(ctx context.Context) *fetcher.Execution { - ns := p.config.Namespace - job := p.deployFetcher(ctx) - defer p.deleteFetcher(ctx) - pod, err := p.findSucceededPod(ctx, job) - ensure.NoError(err) - bytes, err := pkgTest.PodLogs(ctx, p.client.Kube, pod.Name, fetcherName, ns) - ensure.NoError(err) +func (p *prober) fetchExecution() *fetcher.Execution { + ns := p.client.Namespace + job := p.deployFetcher() + defer p.deleteFetcher() + pod, err := p.findSucceededPod(job) + p.ensureNoError(err) + bytes, err := pkgTest.PodLogs(p.config.Ctx, p.client.Kube, pod.Name, fetcherName, ns) + p.ensureNoError(err) ex := &fetcher.Execution{ Logs: []fetcher.LogEntry{}, Report: &receiver.Report{ @@ -124,13 +121,13 @@ func (p *prober) fetchExecution(ctx context.Context) *fetcher.Execution { }, } err = json.Unmarshal(bytes, ex) - ensure.NoError(err) + p.ensureNoError(err) return ex } -func (p *prober) deployFetcher(ctx context.Context) *batchv1.Job { +func (p *prober) deployFetcher() *batchv1.Job { p.log.Info("Deploying fetcher job: ", fetcherName) - jobs := p.client.Kube.BatchV1().Jobs(p.config.Namespace) + jobs := p.client.Kube.BatchV1().Jobs(p.client.Namespace) var replicas int32 = 1 fetcherJob := &batchv1.Job{ ObjectMeta: metav1.ObjectMeta{ @@ -160,7 +157,7 @@ func (p *prober) deployFetcher(ctx context.Context) *batchv1.Job { }}, Containers: []corev1.Container{{ Name: fetcherName, - Image: pkgTest.ImagePath(fetcherName), + Image: p.config.ImageResolver(fetcherName), VolumeMounts: []corev1.VolumeMount{{ Name: p.config.ConfigMapName, ReadOnly: true, @@ -171,28 +168,28 @@ func (p *prober) deployFetcher(ctx context.Context) *batchv1.Job { }, }, } - created, err := jobs.Create(ctx, fetcherJob, metav1.CreateOptions{}) - ensure.NoError(err) + created, err := jobs.Create(p.config.Ctx, fetcherJob, metav1.CreateOptions{}) + p.ensureNoError(err) p.log.Info("Waiting for fetcher job to succeed: ", fetcherName) - err = waitForJobToComplete(ctx, p.client.Kube, fetcherName, p.config.Namespace) - ensure.NoError(err) + err = waitForJobToComplete(p.config.Ctx, p.client.Kube, fetcherName, p.client.Namespace) + p.ensureNoError(err) return created } -func (p *prober) deleteFetcher(ctx context.Context) { - ns := p.config.Namespace +func (p *prober) deleteFetcher() { + ns := p.client.Namespace jobs := p.client.Kube.BatchV1().Jobs(ns) - err := jobs.Delete(ctx, fetcherName, metav1.DeleteOptions{}) - ensure.NoError(err) + err := jobs.Delete(p.config.Ctx, fetcherName, metav1.DeleteOptions{}) + p.ensureNoError(err) } -func (p *prober) findSucceededPod(ctx context.Context, job *batchv1.Job) (*corev1.Pod, error) { +func (p *prober) findSucceededPod(job *batchv1.Job) (*corev1.Pod, error) { pods := p.client.Kube.CoreV1().Pods(job.Namespace) - podList, err := pods.List(ctx, metav1.ListOptions{ + podList, err := pods.List(p.config.Ctx, metav1.ListOptions{ LabelSelector: fmt.Sprint("job-name=", job.Name), }) - ensure.NoError(err) + p.ensureNoError(err) for _, pod := range podList.Items { if pod.Status.Phase == corev1.PodSucceeded { return &pod, nil diff --git a/vendor/knative.dev/eventing/test/upgrade/prober/wathola/config/defaults.go b/vendor/knative.dev/eventing/test/upgrade/prober/wathola/config/defaults.go index a76a7c20c7..77f300a61e 100644 --- a/vendor/knative.dev/eventing/test/upgrade/prober/wathola/config/defaults.go +++ b/vendor/knative.dev/eventing/test/upgrade/prober/wathola/config/defaults.go @@ -62,7 +62,7 @@ func defaultValues() *Config { Duration: time.Second, }, Errors: ReceiverErrorConfig{ - UnavailablePeriodToReport: 5 * time.Second, + UnavailablePeriodToReport: time.Minute, }, }, Forwarder: ForwarderConfig{ @@ -79,6 +79,6 @@ func defaultValues() *Config { Message: "OK", Status: nethttp.StatusOK, }, - LogLevel: zap.InfoLevel, + LogLevel: zap.InfoLevel.String(), } } diff --git a/vendor/knative.dev/eventing/test/upgrade/prober/wathola/config/reader.go b/vendor/knative.dev/eventing/test/upgrade/prober/wathola/config/reader.go index efb7f563fd..f2c725ab5d 100644 --- a/vendor/knative.dev/eventing/test/upgrade/prober/wathola/config/reader.go +++ b/vendor/knative.dev/eventing/test/upgrade/prober/wathola/config/reader.go @@ -16,11 +16,10 @@ package config import ( - "github.com/mitchellh/go-homedir" - "github.com/pelletier/go-toml" - "github.com/wavesoftware/go-ensure" - "os" + + "github.com/mitchellh/go-homedir" + "github.com/pelletier/go-toml/v2" ) var location = "~/.config/wathola/config.toml" @@ -29,16 +28,21 @@ var logFatal = Log.Fatal // ReadIfPresent read a configuration file if it exists func ReadIfPresent() { configFile, err := homedir.Expand(location) - ensure.NoError(err) + if err != nil { + logFatal(err) + } if fileExists(configFile) { Log.Infof("Reading config file: %v", configFile) - err := Read(configFile) + err = Read(configFile) if err != nil { logFatal(err) } } else { Log.Infof("Define config file to be taken into account: %v", configFile) } + if err = setLogLevel(); err != nil { + logFatal(err) + } } // Read a config file and update configuration object @@ -48,9 +52,17 @@ func Read(configFile string) error { return err } d := toml.NewDecoder(r) + d.SetStrict(true) err = d.Decode(Instance) + return err +} + +func setLogLevel() error { + err := logConfig.Level.UnmarshalText([]byte(Instance.LogLevel)) if err == nil { - logConfig.Level.SetLevel(Instance.LogLevel) + Instance.LogLevel = logConfig.Level.String() + } else { + Instance.LogLevel = defaultValues().LogLevel } return err } diff --git a/vendor/knative.dev/eventing/test/upgrade/prober/wathola/config/structure.go b/vendor/knative.dev/eventing/test/upgrade/prober/wathola/config/structure.go index 03aa038bd7..dfbf73f64f 100644 --- a/vendor/knative.dev/eventing/test/upgrade/prober/wathola/config/structure.go +++ b/vendor/knative.dev/eventing/test/upgrade/prober/wathola/config/structure.go @@ -1,24 +1,23 @@ /* - * Copyright 2020 The Knative Authors - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +Copyright 2021 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package config import ( "time" - - "go.uber.org/zap/zapcore" ) // ReceiverTeardownConfig holds config receiver teardown @@ -46,7 +45,7 @@ type ReceiverConfig struct { // SenderConfig hold configuration for sender type SenderConfig struct { - Address string + Address interface{} Interval time.Duration } @@ -70,5 +69,5 @@ type Config struct { Forwarder ForwarderConfig Receiver ReceiverConfig Readiness ReadinessConfig - LogLevel zapcore.Level + LogLevel string } diff --git a/vendor/knative.dev/eventing/test/upgrade/prober/wathola/event/types.go b/vendor/knative.dev/eventing/test/upgrade/prober/wathola/event/types.go index f6559dab5e..2a7fe264bb 100644 --- a/vendor/knative.dev/eventing/test/upgrade/prober/wathola/event/types.go +++ b/vendor/knative.dev/eventing/test/upgrade/prober/wathola/event/types.go @@ -23,9 +23,9 @@ import ( const ( // StepType is a string type representation of step event - StepType = "com.github.cardil.wathola.step" + StepType = "dev.knative.eventing.wathola.step" // FinishedType os a string type representation of finished event - FinishedType = "com.github.cardil.wathola.finished" + FinishedType = "dev.knative.eventing.wathola.finished" ) // Step is a event call at each step of verification diff --git a/vendor/knative.dev/eventing/test/upgrade/prober/wathola/receiver/services.go b/vendor/knative.dev/eventing/test/upgrade/prober/wathola/receiver/services.go index 702d391638..f437d6e163 100644 --- a/vendor/knative.dev/eventing/test/upgrade/prober/wathola/receiver/services.go +++ b/vendor/knative.dev/eventing/test/upgrade/prober/wathola/receiver/services.go @@ -50,7 +50,7 @@ func (r receiver) Receive() { } func (r receiver) receiveEvent(e cloudevents.Event) { - // do something with event.Context and event.Data (via event.DataAs(foo) + log.Debug("Event received: ", e) t := e.Context.GetType() if t == event.StepType { step := &event.Step{} diff --git a/vendor/knative.dev/eventing/test/upgrade/prober/wathola/sender/services.go b/vendor/knative.dev/eventing/test/upgrade/prober/wathola/sender/services.go index 9ca70293cf..56134583b7 100644 --- a/vendor/knative.dev/eventing/test/upgrade/prober/wathola/sender/services.go +++ b/vendor/knative.dev/eventing/test/upgrade/prober/wathola/sender/services.go @@ -1,23 +1,26 @@ /* - * Copyright 2020 The Knative Authors - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +Copyright 2021 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package sender import ( "context" + "errors" "fmt" + "strings" cloudevents "github.com/cloudevents/sdk-go/v2" "github.com/wavesoftware/go-ensure" @@ -30,8 +33,15 @@ import ( "time" ) -var log = config.Log -var senderConfig = &config.Instance.Sender +var ( + // ErrEndpointTypeNotSupported is raised if configured endpoint isn't + // supported by any of the event senders that are registered. + ErrEndpointTypeNotSupported = errors.New("given endpoint isn't " + + "supported by any registered event sender") + log = config.Log + senderConfig = &config.Instance.Sender + eventSenders = make([]EventSender, 0, 1) +) type sender struct { // eventsSent is the number of events successfully sent @@ -71,8 +81,10 @@ func (s *sender) SendContinually() { if err != nil { if retry == 0 { start = time.Now() - log.Warnf("Could not send step event %v, retrying", s.eventsSent) } + log.Warnf("Could not send step event %v, retrying (%d)", + s.eventsSent, retry) + log.Debug("Error: ", err) retry++ } else { if retry != 0 { @@ -94,7 +106,7 @@ func NewCloudEvent(data interface{}, typ string) cloudevents.Event { ensure.NoError(err) e.SetSource(fmt.Sprintf("knative://%s/wathola/sender", host)) e.SetID(NewEventID()) - e.SetTime(time.Now()) + e.SetTime(time.Now().UTC()) err = e.SetData(cloudevents.ApplicationJSON, data) ensure.NoError(err) errs := e.Validate() @@ -104,15 +116,52 @@ func NewCloudEvent(data interface{}, typ string) cloudevents.Event { return e } +// ResetEventSenders will reset configured event senders to defaults. +func ResetEventSenders() { + eventSenders = make([]EventSender, 0, 1) +} + +// RegisterEventSender will register a EventSender to be used. +func RegisterEventSender(es EventSender) { + eventSenders = append(eventSenders, es) +} + // SendEvent will send cloud event to given url -func SendEvent(e cloudevents.Event, url string) error { +func SendEvent(ce cloudevents.Event, endpoint interface{}) error { + senders := make([]EventSender, 0, len(eventSenders)+1) + senders = append(senders, eventSenders...) + if len(senders) == 0 { + senders = append(senders, httpSender{}) + } + for _, eventSender := range senders { + if eventSender.Supports(endpoint) { + return eventSender.SendEvent(ce, endpoint) + } + } + return fmt.Errorf("%w: endpoint is %#v", ErrEndpointTypeNotSupported, endpoint) +} + +type httpSender struct{} + +func (h httpSender) Supports(endpoint interface{}) bool { + switch url := endpoint.(type) { + default: + return false + case string: + return strings.HasPrefix(url, "http://") || + strings.HasPrefix(url, "https://") + } +} + +func (h httpSender) SendEvent(ce cloudevents.Event, endpoint interface{}) error { + url := endpoint.(string) c, err := cloudevents.NewDefaultClient() if err != nil { return err } ctx := cloudevents.ContextWithTarget(context.Background(), url) - result := c.Send(ctx, e) + result := c.Send(ctx, ce) if cloudevents.IsACK(result) { return nil } @@ -122,9 +171,9 @@ func SendEvent(e cloudevents.Event, url string) error { func (s *sender) sendStep() error { step := event.Step{Number: s.eventsSent + 1} ce := NewCloudEvent(step, event.StepType) - url := senderConfig.Address - log.Infof("Sending step event #%v to %s", step.Number, url) - err := SendEvent(ce, url) + endpoint := senderConfig.Address + log.Infof("Sending step event #%v to %#v", step.Number, endpoint) + err := SendEvent(ce, endpoint) // Record every request regardless of the result s.totalRequests++ if err != nil { @@ -139,8 +188,8 @@ func (s *sender) sendFinished() { return } finished := event.Finished{EventsSent: s.eventsSent, TotalRequests: s.totalRequests, UnavailablePeriods: s.unavailablePeriods} - url := senderConfig.Address + endpoint := senderConfig.Address ce := NewCloudEvent(finished, event.FinishedType) - log.Infof("Sending finished event (count: %v) to %s", finished.EventsSent, url) - ensure.NoError(SendEvent(ce, url)) + log.Infof("Sending finished event (count: %v) to %#v", finished.EventsSent, endpoint) + ensure.NoError(SendEvent(ce, endpoint)) } diff --git a/vendor/knative.dev/eventing/test/upgrade/prober/wathola/sender/types.go b/vendor/knative.dev/eventing/test/upgrade/prober/wathola/sender/types.go index 3b78c46572..26bafd473c 100644 --- a/vendor/knative.dev/eventing/test/upgrade/prober/wathola/sender/types.go +++ b/vendor/knative.dev/eventing/test/upgrade/prober/wathola/sender/types.go @@ -1,21 +1,33 @@ /* - * Copyright 2020 The Knative Authors - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +Copyright 2021 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package sender +import cloudevents "github.com/cloudevents/sdk-go/v2" + // Sender will send messages continuously until process receives a SIGINT type Sender interface { SendContinually() } + +// EventSender will be used to send events to configured endpoint. +type EventSender interface { + // Supports will check given endpoint definition and decide if it's valid for + // this sender. + Supports(endpoint interface{}) bool + // SendEvent will send event to given endpoint. + SendEvent(ce cloudevents.Event, endpoint interface{}) error +} diff --git a/vendor/modules.txt b/vendor/modules.txt index bf932ddac9..d329c01325 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -347,8 +347,11 @@ github.com/openzipkin/zipkin-go/model github.com/openzipkin/zipkin-go/propagation github.com/openzipkin/zipkin-go/reporter github.com/openzipkin/zipkin-go/reporter/http -# github.com/pelletier/go-toml v1.8.0 -github.com/pelletier/go-toml +# github.com/pelletier/go-toml/v2 v2.0.0-beta.2 +github.com/pelletier/go-toml/v2 +github.com/pelletier/go-toml/v2/internal/ast +github.com/pelletier/go-toml/v2/internal/tracker +github.com/pelletier/go-toml/v2/internal/unsafe # github.com/pierrec/lz4 v2.6.0+incompatible github.com/pierrec/lz4 github.com/pierrec/lz4/internal/xxh32 @@ -1090,7 +1093,7 @@ k8s.io/utils/buffer k8s.io/utils/integer k8s.io/utils/pointer k8s.io/utils/trace -# knative.dev/eventing v0.22.0 +# knative.dev/eventing v0.22.0 => github.com/openshift/knative-eventing v0.99.1-0.20210610110214-7043d7e4f943 ## explicit knative.dev/eventing/pkg/adapter/v2 knative.dev/eventing/pkg/adapter/v2/test @@ -1223,6 +1226,7 @@ knative.dev/eventing/test/test_images/wathola-forwarder knative.dev/eventing/test/test_images/wathola-receiver knative.dev/eventing/test/test_images/wathola-sender knative.dev/eventing/test/upgrade/prober +knative.dev/eventing/test/upgrade/prober/sut knative.dev/eventing/test/upgrade/prober/wathola/client knative.dev/eventing/test/upgrade/prober/wathola/config knative.dev/eventing/test/upgrade/prober/wathola/event @@ -1346,3 +1350,4 @@ knative.dev/pkg/webhook/resourcesemantics/validation sigs.k8s.io/structured-merge-diff/v4/value # sigs.k8s.io/yaml v1.2.0 sigs.k8s.io/yaml +# knative.dev/eventing => github.com/openshift/knative-eventing v0.99.1-0.20210610110214-7043d7e4f943