mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	Move deployment e2e test for hash label adoption to integration
This commit is contained in:
		@@ -87,9 +87,6 @@ var _ = SIGDescribe("Deployment", func() {
 | 
				
			|||||||
	It("deployment should support rollback", func() {
 | 
						It("deployment should support rollback", func() {
 | 
				
			||||||
		testRollbackDeployment(f)
 | 
							testRollbackDeployment(f)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	It("deployment should label adopted RSs and pods", func() {
 | 
					 | 
				
			||||||
		testDeploymentLabelAdopted(f)
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
	It("scaled rollout deployment should not block on annotation check", func() {
 | 
						It("scaled rollout deployment should not block on annotation check", func() {
 | 
				
			||||||
		testScaledRolloutDeployment(f)
 | 
							testScaledRolloutDeployment(f)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
@@ -629,57 +626,6 @@ func testRollbackDeployment(f *framework.Framework) {
 | 
				
			|||||||
	Expect(err).NotTo(HaveOccurred())
 | 
						Expect(err).NotTo(HaveOccurred())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func testDeploymentLabelAdopted(f *framework.Framework) {
 | 
					 | 
				
			||||||
	ns := f.Namespace.Name
 | 
					 | 
				
			||||||
	c := f.ClientSet
 | 
					 | 
				
			||||||
	// Create nginx pods.
 | 
					 | 
				
			||||||
	podName := "nginx"
 | 
					 | 
				
			||||||
	podLabels := map[string]string{"name": podName}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rsName := "test-adopted-controller"
 | 
					 | 
				
			||||||
	replicas := int32(1)
 | 
					 | 
				
			||||||
	image := NginxImage
 | 
					 | 
				
			||||||
	_, err := c.Extensions().ReplicaSets(ns).Create(newRS(rsName, replicas, podLabels, podName, image))
 | 
					 | 
				
			||||||
	Expect(err).NotTo(HaveOccurred())
 | 
					 | 
				
			||||||
	// Verify that the required pods have come up.
 | 
					 | 
				
			||||||
	err = framework.VerifyPodsRunning(c, ns, podName, false, replicas)
 | 
					 | 
				
			||||||
	Expect(err).NotTo(HaveOccurred(), "error in waiting for pods to come up: %v", err)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Create a nginx deployment to adopt the old rs.
 | 
					 | 
				
			||||||
	deploymentName := "test-adopted-deployment"
 | 
					 | 
				
			||||||
	framework.Logf("Creating deployment %s", deploymentName)
 | 
					 | 
				
			||||||
	deploy, err := c.Extensions().Deployments(ns).Create(framework.NewDeployment(deploymentName, replicas, podLabels, podName, image, extensions.RollingUpdateDeploymentStrategyType))
 | 
					 | 
				
			||||||
	Expect(err).NotTo(HaveOccurred())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Wait for it to be updated to revision 1
 | 
					 | 
				
			||||||
	err = framework.WaitForDeploymentRevisionAndImage(c, ns, deploymentName, "1", image)
 | 
					 | 
				
			||||||
	Expect(err).NotTo(HaveOccurred())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// The RS and pods should be relabeled before the status is updated by syncRollingUpdateDeployment
 | 
					 | 
				
			||||||
	err = framework.WaitForDeploymentComplete(c, deploy)
 | 
					 | 
				
			||||||
	Expect(err).NotTo(HaveOccurred())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// There should be no old RSs (overlapping RS)
 | 
					 | 
				
			||||||
	deployment, err := c.Extensions().Deployments(ns).Get(deploymentName, metav1.GetOptions{})
 | 
					 | 
				
			||||||
	Expect(err).NotTo(HaveOccurred())
 | 
					 | 
				
			||||||
	oldRSs, allOldRSs, newRS, err := deploymentutil.GetAllReplicaSets(deployment, c.ExtensionsV1beta1())
 | 
					 | 
				
			||||||
	Expect(err).NotTo(HaveOccurred())
 | 
					 | 
				
			||||||
	Expect(len(oldRSs)).Should(Equal(0))
 | 
					 | 
				
			||||||
	Expect(len(allOldRSs)).Should(Equal(0))
 | 
					 | 
				
			||||||
	// New RS should contain pod-template-hash in its selector, label, and template label
 | 
					 | 
				
			||||||
	err = framework.CheckRSHashLabel(newRS)
 | 
					 | 
				
			||||||
	Expect(err).NotTo(HaveOccurred())
 | 
					 | 
				
			||||||
	// All pods targeted by the deployment should contain pod-template-hash in their labels, and there should be only 3 pods
 | 
					 | 
				
			||||||
	selector, err := metav1.LabelSelectorAsSelector(deployment.Spec.Selector)
 | 
					 | 
				
			||||||
	Expect(err).NotTo(HaveOccurred())
 | 
					 | 
				
			||||||
	options := metav1.ListOptions{LabelSelector: selector.String()}
 | 
					 | 
				
			||||||
	pods, err := c.Core().Pods(ns).List(options)
 | 
					 | 
				
			||||||
	Expect(err).NotTo(HaveOccurred())
 | 
					 | 
				
			||||||
	err = framework.CheckPodHashLabel(pods)
 | 
					 | 
				
			||||||
	Expect(err).NotTo(HaveOccurred())
 | 
					 | 
				
			||||||
	Expect(int32(len(pods.Items))).Should(Equal(replicas))
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func testScaledRolloutDeployment(f *framework.Framework) {
 | 
					func testScaledRolloutDeployment(f *framework.Framework) {
 | 
				
			||||||
	ns := f.Namespace.Name
 | 
						ns := f.Namespace.Name
 | 
				
			||||||
	c := f.ClientSet
 | 
						c := f.ClientSet
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4397,31 +4397,6 @@ func isElementOf(podUID types.UID, pods *v1.PodList) bool {
 | 
				
			|||||||
	return false
 | 
						return false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func CheckRSHashLabel(rs *extensions.ReplicaSet) error {
 | 
					 | 
				
			||||||
	if len(rs.Labels[extensions.DefaultDeploymentUniqueLabelKey]) == 0 ||
 | 
					 | 
				
			||||||
		len(rs.Spec.Selector.MatchLabels[extensions.DefaultDeploymentUniqueLabelKey]) == 0 ||
 | 
					 | 
				
			||||||
		len(rs.Spec.Template.Labels[extensions.DefaultDeploymentUniqueLabelKey]) == 0 {
 | 
					 | 
				
			||||||
		return fmt.Errorf("unexpected RS missing required pod-hash-template: %+v, selector = %+v, template = %+v", rs, rs.Spec.Selector, rs.Spec.Template)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func CheckPodHashLabel(pods *v1.PodList) error {
 | 
					 | 
				
			||||||
	invalidPod := ""
 | 
					 | 
				
			||||||
	for _, pod := range pods.Items {
 | 
					 | 
				
			||||||
		if len(pod.Labels[extensions.DefaultDeploymentUniqueLabelKey]) == 0 {
 | 
					 | 
				
			||||||
			if len(invalidPod) == 0 {
 | 
					 | 
				
			||||||
				invalidPod = "unexpected pods missing required pod-hash-template:"
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			invalidPod = fmt.Sprintf("%s %+v;", invalidPod, pod)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(invalidPod) > 0 {
 | 
					 | 
				
			||||||
		return fmt.Errorf("%s", invalidPod)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// timeout for proxy requests.
 | 
					// timeout for proxy requests.
 | 
				
			||||||
const proxyTimeout = 2 * time.Minute
 | 
					const proxyTimeout = 2 * time.Minute
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,6 +17,7 @@ limitations under the License.
 | 
				
			|||||||
package deployment
 | 
					package deployment
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
@@ -498,3 +499,137 @@ func TestRollbackDeploymentRSNoRevision(t *testing.T) {
 | 
				
			|||||||
		t.Fatal(err)
 | 
							t.Fatal(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func checkRSHashLabels(rs *v1beta1.ReplicaSet) (string, error) {
 | 
				
			||||||
 | 
						hash := rs.Labels[v1beta1.DefaultDeploymentUniqueLabelKey]
 | 
				
			||||||
 | 
						selectorHash := rs.Spec.Selector.MatchLabels[v1beta1.DefaultDeploymentUniqueLabelKey]
 | 
				
			||||||
 | 
						templateLabelHash := rs.Spec.Template.Labels[v1beta1.DefaultDeploymentUniqueLabelKey]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if hash != selectorHash || selectorHash != templateLabelHash {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("mismatching hash value found in replicaset %s: %#v", rs.Name, rs)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(hash) == 0 {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("unexpected replicaset %s missing required pod-template-hash labels", rs.Name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return hash, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func checkPodsHashLabel(pods *v1.PodList) (string, error) {
 | 
				
			||||||
 | 
						if len(pods.Items) == 0 {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("no pods given")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var hash string
 | 
				
			||||||
 | 
						for _, pod := range pods.Items {
 | 
				
			||||||
 | 
							podHash := pod.Labels[v1beta1.DefaultDeploymentUniqueLabelKey]
 | 
				
			||||||
 | 
							if len(podHash) == 0 {
 | 
				
			||||||
 | 
								return "", fmt.Errorf("found pod %s missing pod-template-hash label: %#v", pod.Name, pods)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// Save the first valid hash
 | 
				
			||||||
 | 
							if len(hash) == 0 {
 | 
				
			||||||
 | 
								hash = podHash
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if podHash != hash {
 | 
				
			||||||
 | 
								return "", fmt.Errorf("found pod %s with mismatching pod-template-hash value %s: %#v", pod.Name, podHash, pods)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return hash, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Deployment should label adopted ReplicaSets and Pods.
 | 
				
			||||||
 | 
					func TestDeploymentLabelAdopted(t *testing.T) {
 | 
				
			||||||
 | 
						s, closeFn, rm, dc, informers, c := dcSetup(t)
 | 
				
			||||||
 | 
						defer closeFn()
 | 
				
			||||||
 | 
						name := "test-adopted-deployment"
 | 
				
			||||||
 | 
						ns := framework.CreateTestingNamespace(name, s, t)
 | 
				
			||||||
 | 
						defer framework.DeleteTestingNamespace(ns, s, t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Start informer and controllers
 | 
				
			||||||
 | 
						stopCh := make(chan struct{})
 | 
				
			||||||
 | 
						defer close(stopCh)
 | 
				
			||||||
 | 
						informers.Start(stopCh)
 | 
				
			||||||
 | 
						go rm.Run(5, stopCh)
 | 
				
			||||||
 | 
						go dc.Run(5, stopCh)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Create a RS to be adopted by the deployment.
 | 
				
			||||||
 | 
						rsName := "test-adopted-controller"
 | 
				
			||||||
 | 
						replicas := int32(1)
 | 
				
			||||||
 | 
						rs := newReplicaSet(rsName, ns.Name, replicas)
 | 
				
			||||||
 | 
						_, err := c.ExtensionsV1beta1().ReplicaSets(ns.Name).Create(rs)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("failed to create replicaset %s: %v", rsName, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Mark RS pods as ready.
 | 
				
			||||||
 | 
						selector, err := metav1.LabelSelectorAsSelector(rs.Spec.Selector)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("failed to parse replicaset %s selector: %v", rsName, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err = wait.PollImmediate(pollInterval, pollTimeout, func() (bool, error) {
 | 
				
			||||||
 | 
							pods, err := c.CoreV1().Pods(ns.Name).List(metav1.ListOptions{LabelSelector: selector.String()})
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return false, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if len(pods.Items) != int(replicas) {
 | 
				
			||||||
 | 
								return false, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for _, pod := range pods.Items {
 | 
				
			||||||
 | 
								if err = markPodReady(c, ns.Name, &pod); err != nil {
 | 
				
			||||||
 | 
									return false, nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return true, nil
 | 
				
			||||||
 | 
						}); err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("failed to mark pods replicaset %s as ready: %v", rsName, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Create a Deployment to adopt the old rs.
 | 
				
			||||||
 | 
						tester := &deploymentTester{t: t, c: c, deployment: newDeployment(name, ns.Name, replicas)}
 | 
				
			||||||
 | 
						if tester.deployment, err = c.ExtensionsV1beta1().Deployments(ns.Name).Create(tester.deployment); err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("failed to create deployment %s: %v", tester.deployment.Name, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Wait for the Deployment to be updated to revision 1
 | 
				
			||||||
 | 
						if err = tester.waitForDeploymentRevisionAndImage("1", fakeImage); err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// The RS and pods should be relabeled after the Deployment finishes adopting it and completes.
 | 
				
			||||||
 | 
						if err := tester.waitForDeploymentComplete(); err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// There should be no old RSes (overlapping RS)
 | 
				
			||||||
 | 
						oldRSs, allOldRSs, newRS, err := deploymentutil.GetAllReplicaSets(tester.deployment, c.ExtensionsV1beta1())
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("failed to get all replicasets owned by deployment %s: %v", name, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(oldRSs) != 0 || len(allOldRSs) != 0 {
 | 
				
			||||||
 | 
							t.Errorf("expected deployment to have no old replicasets, got %d old replicasets", len(allOldRSs))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// New RS should be relabeled, i.e. contain pod-template-hash in its selector, label, and template label
 | 
				
			||||||
 | 
						rsHash, err := checkRSHashLabels(newRS)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Error(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// All pods targeted by the deployment should contain pod-template-hash in their labels, and there should be only 3 pods
 | 
				
			||||||
 | 
						selector, err = metav1.LabelSelectorAsSelector(tester.deployment.Spec.Selector)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("failed to parse deployment %s selector: %v", name, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						pods, err := c.CoreV1().Pods(ns.Name).List(metav1.ListOptions{LabelSelector: selector.String()})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("failed to list pods of deployment %s: %v", name, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(pods.Items) != int(replicas) {
 | 
				
			||||||
 | 
							t.Errorf("expected %d pods, got %d pods", replicas, len(pods.Items))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						podHash, err := checkPodsHashLabel(pods)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Error(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if rsHash != podHash {
 | 
				
			||||||
 | 
							t.Errorf("found mismatching pod-template-hash value: rs hash = %s whereas pod hash = %s", rsHash, podHash)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -200,6 +200,12 @@ func (d *deploymentTester) waitForDeploymentRevisionAndImage(revision, image str
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func markPodReady(c clientset.Interface, ns string, pod *v1.Pod) error {
 | 
				
			||||||
 | 
						addPodConditionReady(pod, metav1.Now())
 | 
				
			||||||
 | 
						_, err := c.Core().Pods(ns).UpdateStatus(pod)
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// markAllPodsReady manually updates all Deployment pods status to ready
 | 
					// markAllPodsReady manually updates all Deployment pods status to ready
 | 
				
			||||||
func (d *deploymentTester) markAllPodsReady() {
 | 
					func (d *deploymentTester) markAllPodsReady() {
 | 
				
			||||||
	ns := d.deployment.Namespace
 | 
						ns := d.deployment.Namespace
 | 
				
			||||||
@@ -215,14 +221,17 @@ func (d *deploymentTester) markAllPodsReady() {
 | 
				
			|||||||
			d.t.Logf("failed to list Deployment pods, will retry later: %v", err)
 | 
								d.t.Logf("failed to list Deployment pods, will retry later: %v", err)
 | 
				
			||||||
			return false, nil
 | 
								return false, nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							if len(pods.Items) != int(*d.deployment.Spec.Replicas) {
 | 
				
			||||||
 | 
								d.t.Logf("%d/%d of deployment pods are created", len(pods.Items), *d.deployment.Spec.Replicas)
 | 
				
			||||||
 | 
								return false, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		for i := range pods.Items {
 | 
							for i := range pods.Items {
 | 
				
			||||||
			pod := pods.Items[i]
 | 
								pod := pods.Items[i]
 | 
				
			||||||
			if podutil.IsPodReady(&pod) {
 | 
								if podutil.IsPodReady(&pod) {
 | 
				
			||||||
				readyPods++
 | 
									readyPods++
 | 
				
			||||||
				continue
 | 
									continue
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			addPodConditionReady(&pod, metav1.Now())
 | 
								if err = markPodReady(d.c, ns, &pod); err != nil {
 | 
				
			||||||
			if _, err = d.c.Core().Pods(ns).UpdateStatus(&pod); err != nil {
 | 
					 | 
				
			||||||
				d.t.Logf("failed to update Deployment pod %s, will retry later: %v", pod.Name, err)
 | 
									d.t.Logf("failed to update Deployment pod %s, will retry later: %v", pod.Name, err)
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				readyPods++
 | 
									readyPods++
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -172,7 +172,7 @@ func WaitForDeploymentRevisionAndImage(c clientset.Interface, ns, deploymentName
 | 
				
			|||||||
	var deployment *extensions.Deployment
 | 
						var deployment *extensions.Deployment
 | 
				
			||||||
	var newRS *extensions.ReplicaSet
 | 
						var newRS *extensions.ReplicaSet
 | 
				
			||||||
	var reason string
 | 
						var reason string
 | 
				
			||||||
	err := wait.Poll(pollInterval, pollTimeout, func() (bool, error) {
 | 
						err := wait.PollImmediate(pollInterval, pollTimeout, func() (bool, error) {
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		deployment, err = c.ExtensionsV1beta1().Deployments(ns).Get(deploymentName, metav1.GetOptions{})
 | 
							deployment, err = c.ExtensionsV1beta1().Deployments(ns).Get(deploymentName, metav1.GetOptions{})
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user